开发者

Display a image in a MFC/C++ application using OpenCV

开发者 https://www.devze.com 2023-03-13 05:08 出处:网络
I would like to display in a MFC application, frames that I capture from an avi file with OpenCV (cvCaptureFromAVI function).

I would like to display in a MFC application, frames that I capture from an avi file with OpenCV (cvCaptureFromAVI function).

I'm new to MFC but feel like I'm close to making it work. But instead of the frames being displayed in the picture box they are displayed in a new window.

cvGetWindowName returns always a null value.

There is my code:

CWnd* hPic = 0;
hPic = GetDlgItem(IDC_STATICPIC1);  
const char* szWindName = cvGe开发者_JS百科tWindowName(hPic->GetSafeHwnd());
cvShowImage(szWindName, frame_copy);


So I found something to make it work after long researches.

The solution is to create the window and then insert it inside the picture box. I'm not sure it's good practice but I haven't found anything better for now.

cvNamedWindow("IDC_STATIC_OUTPUT", 0); 
cvResizeWindow("IDC_STATIC_OUTPUT", 420, 240);

HWND hWnd = (HWND) cvGetWindowHandle("IDC_STATIC_OUTPUT"); 
HWND hParent = ::GetParent(hWnd); 
     ::SetParent(hWnd, GetDlgItem(IDC_PIC1)->m_hWnd); 
     ::ShowWindow(hParent, SW_HIDE); 

cvShowImage("IDC_STATIC_OUTPUT", frame_copy);

In this case the picture box is called IDC_PIC1 and frame_copy is a OpenCV IplImage.

Hope this helps somebody.


Using the following code you can convert Mat to CImage and then display CImage everywhere you want:

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


NOTE: If you use the StretchDIBits() method with the BITMAPINFO approach, you MUST be aware that StretchDIBits() expects the raw OpenCV Mat::data pointer to have row lengths in even multiples of 4 bytes! If not, you'll get freaky shearing when you try to copy the data to the DC via StretchDIBits() - where the image is not only sheered along an angle, but the colors are all be trashed as well.

Here is my completely working edition of the code, which also supports maintaining image aspect ratio in the target control's rectangle. It can probably be made a bit faster, but it works for now:

void AdjustAspectImageSize( const Size& imageSize,
                            const Size& destSize, 
                                  Size& newSize )
{
   double destAspectRatio   =  float( destSize.width  )  /  float( destSize.height  );
   double imageAspectRatio  =  float( imageSize.width )  /  float( imageSize.height );

   if ( imageAspectRatio > destAspectRatio )
   {
      // Margins on top/bottom
      newSize.width    =   destSize.width;
      newSize.height   =   int( imageSize.height  *  
                                    ( double( destSize.width )  /  double( imageSize.width ) ) );
   }
   else
   {
      // Margins on left/right
      newSize.height   =   destSize.height;
      newSize.width    =   int( imageSize.width  *
                                    ( double( destSize.height )  /  double( imageSize.height ) ) );
   }
}


void DrawPicToHDC( Mat  cvImg, 
                   UINT nDlgID, 
                   bool bMaintainAspectRatio /* =true*/  )
{
   // Get the HDC handle information from the ID passed
   CDC* pDC  =  GetDlgItem(nDlgID)->GetDC();
   HDC  hDC  =  pDC->GetSafeHdc();

   CRect rect;
   GetDlgItem(nDlgID)->GetClientRect(rect);

   Size winSize( rect.right, rect.bottom );

   // Calculate the size of the image that
   // will fit in the control rectangle.
   Size origImageSize( cvImg.cols, cvImg.rows );
   Size imageSize;
   int  offsetX;
   int  offsetY;

   if ( ! bMaintainAspectRatio )
   {
      // Image should be the same size as the control's rectangle
      imageSize = winSize;
   }
   else
   {
      Size newSize;

      _AdjustAspectImageSize( origImageSize,
                              winSize,
                              imageSize );
   }

   offsetX   =   ( winSize.width  - imageSize.width  )  /  2;
   offsetY   =   ( winSize.height - imageSize.height )  /  2;

   // Resize the source to the size of the destination image if necessary
   Mat cvImgTmp;

   resize( cvImg, 
           cvImgTmp, 
           imageSize,
           0,
           0,
           INTER_AREA );

   // To handle our Mat object of this width, the source rows must
   // be even multiples of a DWORD in length to be compatible with 
   // SetDIBits().  Calculate what the correct byte width of the 
   // row should be to be compatible with SetDIBits() below.
   int stride  =  ( ( ( ( imageSize.width * 24 )  +  31 )  &  ~31 )  >>  3 );

   // Allocate a buffer for our DIB bits
   uchar* pcDibBits  =  (uchar*) malloc( imageSize.height * stride );

   if ( pcDibBits != NULL )
   {
      // Copy the raw pixel data over to our dibBits buffer.
      // NOTE: Can setup cvImgTmp to add the padding to skip this.
      for ( int row = 0;  row < cvImgTmp.rows;  ++row )
      {
         // Get pointers to the beginning of the row on both buffers
         uchar* pcSrcPixel  =  cvImgTmp.ptr<uchar>(row);
         uchar* pcDstPixel  =  pcDibBits  +  ( row * stride );

         // We can just use memcpy
         memcpy( pcDstPixel,
                 pcSrcPixel,
                 stride );
      }

      // Initialize the BITMAPINFO structure
      BITMAPINFO bitInfo;

      bitInfo.bmiHeader.biBitCount       =  24;
      bitInfo.bmiHeader.biWidth          =   cvImgTmp.cols;
      bitInfo.bmiHeader.biHeight         =  -cvImgTmp.rows;
      bitInfo.bmiHeader.biPlanes         =  1;
      bitInfo.bmiHeader.biSize           =  sizeof(BITMAPINFOHEADER);
      bitInfo.bmiHeader.biCompression    =  BI_RGB;
      bitInfo.bmiHeader.biClrImportant   =  0;
      bitInfo.bmiHeader.biClrUsed        =  0;
      bitInfo.bmiHeader.biSizeImage      =  0;      //winSize.height * winSize.width * * 3;
      bitInfo.bmiHeader.biXPelsPerMeter  =  0;
      bitInfo.bmiHeader.biYPelsPerMeter  =  0;

      // Add header and OPENCV image's data to the HDC
      StretchDIBits( hDC, 
                     offsetX, 
                     offsetY,
                     cvImgTmp.cols,
                     cvImgTmp.rows,
                     0, 
                     0,
                     cvImgTmp.cols,
                     cvImgTmp.rows,
                     pcDibBits,
                     & bitInfo, 
                     DIB_RGB_COLORS, 
                     SRCCOPY );

      free(pcDibBits);
   }

   ReleaseDC(pDC);
}


int DrawImageToHDC(IplImage* img, HDC hdc, int xDest, int yDest, UINT iUsage, DWORD rop)

    char m_chBmpBuf[2048];
    BITMAPINFO *m_pBmpInfo = 0;
    m_pBmpInfo = (BITMAPINFO*)m_chBmpBuf;
    m_pBmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    m_pBmpInfo->bmiHeader.biWidth = img->width;
    m_pBmpInfo->bmiHeader.biHeight = -img->height;
    m_pBmpInfo->bmiHeader.biBitCount = 24;

    m_pBmpInfo->bmiHeader.biPlanes = 1;
    m_pBmpInfo->bmiHeader.biCompression = BI_RGB;
    m_pBmpInfo->bmiHeader.biSizeImage = 0;
    m_pBmpInfo->bmiHeader.biXPelsPerMeter = 0;
    m_pBmpInfo->bmiHeader.biYPelsPerMeter = 0;
    m_pBmpInfo->bmiHeader.biClrUsed = 0;
    m_pBmpInfo->bmiHeader.biClrImportant = 0;

    return StretchDIBits(hdc, xDest, yDest, img->width, img->height, 0, 0,
img->width, img->height, img->imageData, m_pBmpInfo, DIB_RGB_COLORS, SRCCOPY);

Usage: DrawImageToHDC(img, pDC->m_hDC, Area.left, Area.top, DIB_RGB_COLORS, SRCCOPY);

0

精彩评论

暂无评论...
验证码 换一张
取 消