2011-12-14 16 views
5

Ho pensato che visualizzare OpenCV2 Mat su Vista MFC è semplice ma non lo è. This is only relevant material I found on google. Scusami per la mia ignoranza ma non riesco a trovare nessun altro materiale che mostri come usare SetDIBitsToDevice con i ritorni dei membri "dati" di un array di dimensioni. Più in particolare, ho bisogno di sapere come specificare BITMAPINFO per la funzione. Torno ad OpenCV in stile Old C per lavorare con MFC?Come visualizzare OpenCV Mat su MFC View

UPDATE:

ho trovato an example of SetDIBitsToDevice che è in realtà per i vecchi OpenCV in stile C. Ma è stato semplice convertirlo per OpenCV2. Ci sono cose che devo parlare per farlo funzionare:

  1. metodo

    Bpp non funziona così come la profondità di Mat restituisce 0. Ho appena cambiato in questo modo:

    static int Bpp(cv::Mat img) { return 8 * img.channels(); } 
    
  2. Mat non ha origine membro. Ma mettere semplicemente 0 va bene per l'argomento origine del metodo FillBitmapInfo.

Oltre a ciò, il codice seguente funziona alla grande. Spero che questo aiuti anche altri sviluppatori.

void COpenCVTestView::OnDraw(CDC* pDC) 
{ 
COpenCVTestDoc* pDoc = GetDocument(); 
ASSERT_VALID(pDoc); 
if (!pDoc) 
    return; 
if(pDoc->m_cvImage.empty()) return; 
// TODO: add draw code for native data here 
int height=pDoc->m_cvImage.rows; 
int width=pDoc->m_cvImage.cols; 
uchar buffer[sizeof(BITMAPINFOHEADER) + 1024]; 
BITMAPINFO* bmi = (BITMAPINFO*)buffer; 
FillBitmapInfo(bmi,width,height,Bpp(pDoc->m_cvImage),0); 
SetDIBitsToDevice(pDC->GetSafeHdc(), 0, 0, width, 
    height, 0, 0, 0, height, pDoc->m_cvImage.data, bmi, 
    DIB_RGB_COLORS); 
} 
void COpenCVTestView::FillBitmapInfo(BITMAPINFO* bmi, int width, int height, int bpp, int origin) 
{ 
assert(bmi && width >= 0 && height >= 0 && (bpp == 8 || bpp == 24 || bpp == 32)); 

BITMAPINFOHEADER* bmih = &(bmi->bmiHeader); 

memset(bmih, 0, sizeof(*bmih)); 
bmih->biSize = sizeof(BITMAPINFOHEADER); 
bmih->biWidth = width; 
bmih->biHeight = origin ? abs(height) : -abs(height); 
bmih->biPlanes = 1; 
bmih->biBitCount = (unsigned short)bpp; 
bmih->biCompression = BI_RGB; 

if (bpp == 8) 
{ 
    RGBQUAD* palette = bmi->bmiColors; 

      for (int i = 0; i < 256; i++) 
    { 
     palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i; 
     palette[i].rgbReserved = 0; 
    } 
} 
} 
+2

E cosa c'è di sbagliato con il link che hai fornito? – Sam

+0

@vasile Nonostante i documenti su SetDIBitsToDevice, non riesco a capire come utilizzare SetDIBitsToDevice con i ritorni dei membri "dati" di un array dimensionale. –

+0

@Paul Ho visto il tuo esempio come base per altre numerose domande/risposte. Tuttavia, mi chiedo, come evitare la perdita di memoria dall'uso di 'memset' nella funzione' FillBitmapInfo'? o in qualche modo viene automaticamente liberato? Puoi approfondire in proposito. – adn

risposta

1

Da MSDN:

lpvBits [a] un puntatore ai dati dei colori memorizzati come una matrice di byte. Per ulteriori informazioni, vedere la seguente sezione Note.

Questo è il puntatore che si deve iniziare con i dati restituiti da Mat :: data.

+0

qui è tutto ciò di cui hai bisogno. http://msdn.microsoft.com/en-us/library/dd162974(v=vs.85).aspx Basta leggerlo per te – Sam

+0

Il BITMAPINFO è una struttura con i nomi dei campi che dicono chiaramente cosa contengono: BITMAPINFOHEADER-> larghezza, altezza, ecc. Hai mai letto quella pagina? Si prega di leggere anche la documentazione Mat. – Sam

+0

Ok, l'ho usato qualche tempo fa, ma guardando la modifica sopra, non è così semplice come ricordato. Scusate. – Sam

2

Ecco un altro modo possibile di visualizzazione dei dati OpenCV in MFC che io uso e funziona alla grande:

IplImage* image// <-- this contains the image you want to display 
CvvImage tempdefault; 
RECT myrect; // <-- specifiy where on the screen you want it to be displayed 
myrect.top = 0; 
myrect.bottom = _pictureh; 
myrect.left = _picturex; 
myrect.right = _picturew+_picturex; 
tempdefault.Create(_pictureh,_picturew,32); 
tempdefault.CopyOf(image); 
tempdefault.DrawToHDC(pDC->GetSafeHdc(),&myrect); 
+1

non sono sicuro se sto usando la stessa versione di OpenCV, ma per convertire da un tappetino ad un IplImage, faccio semplicemente: 'CvMat mat = cvMat (altezza, larghezza, CV_8UC1, dati); IplImage * img = cvCreateImage (cvSize (altezza, larghezza), IPL_DEPTH_8U, 1); cvGetImage (& mat, img); ' – Smash

+0

no non so se è più veloce, è solo molto più facile;) – Smash

1

CvvImage non è disponibile nelle nuove versioni di OpenCV. Utilizzando il seguente codice è possibile convertire i Mat per CImage e quindi visualizzare CImage ovunque si desidera:

int Mat2CImage(Mat *mat, CImage &img){ 
    if(!mat || mat->empty()) 
    return -1; 
    int nBPP = mat->channels()*8; 
    img.Create(mat->cols, mat->rows, nBPP); 
    if(nBPP == 8) 
    { 
    static RGBQUAD pRGB[256]; 
    for (int i = 0; i < 256; i++) 
     pRGB[i].rgbBlue = pRGB[i].rgbGreen = pRGB[i].rgbRed = i; 
    img.SetColorTable(0, 256, pRGB); 
    } 
    uchar* psrc = mat->data; 
    uchar* pdst = (uchar*) img.GetBits(); 
    int imgPitch = img.GetPitch(); 
    for(int y = 0; y < mat->rows; y++) 
    { 
    memcpy(pdst, psrc, mat->cols*mat->channels());//mat->step is incorrect for those images created by roi (sub-images!) 
    psrc += mat->step; 
    pdst += imgPitch; 
    } 

    return 0; 
}