2011-12-14 10 views
5

Pensé que mostrar OpenCV2 Mat en MFC View es simple pero no lo es. This is only relevant material I found on google. Perdóneme por mi ignorancia, pero no puedo encontrar ningún otro material que demuestre cómo usar SetDIBitsToDevice con los retornos de un miembro "de datos" de una matriz dimensional. Más específicamente, necesito saber cómo especificar BITMAPINFO para la función. ¿Vuelvo a Old C-style OpenCV para trabajar con MFC?Cómo mostrar OpenCV Mat en MFC View

ACTUALIZACIÓN:

He encontrado an example of SetDIBitsToDevice que en realidad es para el viejo estilo de OpenCV-C. Pero fue sencillo convertirlo para OpenCV2. Hay cosas que tengo que mencionar a hacer que funcione:

  1. método

    Bpp no ​​funciona así como la profundidad de Mat devuelve 0. Me acaba de cambiar de esta manera:

    static int Bpp(cv::Mat img) { return 8 * img.channels(); } 
    
  2. Mat no tiene origen miembro. Pero simplemente poner 0 está bien para el argumento de origen del método FillBitmapInfo.

Aparte de eso, siguiente código funciona muy bien. Espero que esto ayude a otros desarrolladores también.

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

Y lo que es malo en el enlace que ya ha proporcionado? – Sam

+0

@vasile A pesar de los documentos en SetDIBitsToDevice, no puedo encontrar la forma de usar SetDIBitsToDevice con una matriz dimensional que devuelve un miembro de "datos". –

+0

@Paul He visto su ejemplo como base para otras varias preguntas/respuestas. Sin embargo, me pregunto, ¿cómo se puede evitar la pérdida de memoria por el uso del 'memset' en la función' FillBitmapInfo'? o de alguna manera se libera automáticamente? ¿Puedes elaborar en este sentido? – adn

Respuesta

1

De MSDN:

lpvBits [en] un puntero a los datos de color almacenados como una matriz de bytes. Para obtener más información, consulte la siguiente sección de Comentarios.

Este es el puntero que debe iniciar con los datos devueltos desde Mat :: data.

+0

aquí es todo lo que necesita. http://msdn.microsoft.com/en-us/library/dd162974(v=vs.85).aspx Solo léalo usted mismo – Sam

+0

El BITMAPINFO es una estructura con los nombres de campo que dicen claramente lo que contienen: BITMAPINFOHEADER-> ancho, alto, etc. ¿Alguna vez leíste esa página? Por favor, lea también la documentación Mat. – Sam

+0

Ok, lo he usado hace un tiempo, pero mirando la edición anterior, no es tan simple como se recuerda. Lo siento. – Sam

2

Aquí hay otra manera posible de mostrar los datos de OpenCV en MFC que utilizo y funciona muy bien:

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

no estoy seguro si estoy usando la misma versión de OpenCV, pero para convertir de una estera a una IplImage, simplemente lo hago: 'CvMat mat = cvMat (alto, ancho, CV_8UC1, datos); IplImage * img = cvCreateImage (cvSize (alto, ancho), IPL_DEPTH_8U, 1); cvGetImage (& mat, img); ' – Smash

+0

no, no sé si es más rápido, es mucho más fácil;) – Smash

1

CvvImage no está disponible en las nuevas versiones de OpenCV. Usando el siguiente código se puede convertir Mat a CImage y luego mostrar CImage todo el mundo quiere:

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; 
} 
Cuestiones relacionadas