您的位置:首页 > 运维架构

OpenCV学习笔记(8)VS2008 MFC下使用OpenCV2.0进行简单图像处理

2009-10-27 15:16 1201 查看
在OpenCV中文论坛看到有不少帖子讲到如何在MFC下应用OpenCV进行图像和视频处理的方法,受益颇丰,主要有下面这些帖子:

1、opencv 数据读写操作+图像噪声+ MFC下OpenCV源代码
2、MFC中快速应用OpenCV & 相应论坛帖子
3、MFC+openCV对话框中显示视频
4、如何把视频显示到MFC的picture控件上
5、一个人脸-人眼检测例程,大家可以参考参考
6、A step-by-step guide to the use of Microsoft Visual C++ and the Intel OpenCV library

另外还参考了于仕琪老师的《OpenCV教程-基础篇》,不过上面这些资料主要是 VC6 和 OpenCV1.0/1.1 的版本,而我使用的是 VS2008 和 OpenCV2.0,两者与之前版本相比都有很大的不同,按照论坛教程资料来做MFC下的OpenCV应用常会遇到各种各样的编程和调试问题。接下来的学习笔记将以上面的《A step-by-step guide …》为基础,总结一下VS2008 MFC下使用OpenCV2.0进行图像和视频处理的各种问题。

一、《OpenCV教程-基础篇》2.8节中的问题

1、P27初始化代码ROI值不为0的bug



IplImage* Temp = cvCreateImage(ImgSize, IPL_DEPTH_8U, 1); // 创建8U单通道图像来缓存RGB图的各通道子图;

      int h,w;
      float dx = (Temp->width / 256.0f);    // 将图像按宽度作256等分

      // 复制数据到IPL的蓝色通道
      for(w = 0; w < Temp->width; w++)
         for(h = 0;h < Temp->height; h++)
         {
            // 复制数据到IPL
            Temp->imageData[Temp->height * w + h] = (char)(w/dx); 
            // 单通道图像每一列像素的像素值等于(所在列/总列数)*256.0f
            // 其效果是使单通道图像显示为从上到下由黑到白的灰度渐变效果
         }
         /*
         函数 cvSetImageCOI 基于给定的值设置感兴趣的通道。
         值 0 意味着所有的通道都被选定, 1 意味着第一个通道被选定等等。
         如果 ROI 是 NULL 并且COI!= 0, ROI 被分配. 然而大多数的 OpenCV 函数不支持 COI, 
         对于这种状况当处理分离图像/矩阵通道时,可以拷贝(通过 cvCopy 或cvSplit) 通道来分离图像/矩阵,
         处理后如果需要可再拷贝(通过cvCopy 或 cvCvtPlaneToPix)回来.
         */
         cvSetImageCOI(TheImage,1);    // 选择TheImage的蓝色通道
         cvCopy(Temp,TheImage);        // 复制数据到TheImage的蓝色通道

      // 复制数据到IPL的绿色通道
      for(w = 0; w < Temp->width; w++)
         for(h = 0;h < Temp->height; h++)
         {
            // 复制数据到IPL
            Temp->imageData[Temp->height * w + h] = (char)(255-w/dx);
            // 用255去减w/dx,则三个通道子图合在一起会呈现出从上到下由绿变紫的彩色渐变效果
            // 如果也同红绿通道一样只用 w/dx,则最终是呈现由黑到白的灰度渐变
         }
         cvSetImageCOI(TheImage,2);    // 选择TheImage的绿色通道
         cvCopy(Temp,TheImage);        // 复制数据到TheImage的绿色通道

      // 复制数据到IPL的红色通道
      for(w = 0; w < Temp->width; w++)
         for(h = 0;h < Temp->height; h++)
         {
            // 复制数据到IPL
            Temp->imageData[Temp->height * w + h] = (char)(w/dx);//??
         }
         cvSetImageCOI(TheImage,3);    //选择TheImage的红色通道
         cvCopy(Temp,TheImage);        //复制数据到TheImage的红色通道

      cvReleaseImage(&Temp);        //释放Temp占用的内存空间




不过上述代码在运行之后,ROI的值不为0,这应该是一个bug,如何处理?

2、P34 cvCopy 使得运行出错的问题

P34执行Canny边缘检测的代码:

void MyIplClass::ProcessIpl(IplImage* ipl)
{
    IplImage *gray = 0, *edge = 0;
    gray = cvCreateImage( cvSize(IMAGE_WIDTH, IMAGE_HEIGHT), IPL_DEPTH_8U, 1 );
    edge = cvCreateImage( cvSize(IMAGE_WIDTH, IMAGE_HEIGHT), IPL_DEPTH_8U, 1 );
    cvCvtColor( ipl, gray, CV_BGR2GRAY );
    cvCanny( gray, edge, 30, 100, 3 );
    cvCvtColor( edge, ipl, CV_GRAY2BGR );
    // save result
    cvCopyImage( ipl, m_Ipl );

    cvReleaseImage( &gray );
    cvReleaseImage( &edge );
}


其中的 cvCopyImage( ipl, m_Ipl ) 是无法执行的,因为经过前3行的颜色转换处理后, ipl 和 m_ipl 的ROI的区域已经不一样。这需要重设 ipl 的ROI,即在 cvCopyImage 前加一行代码:

cvResetImageROI( ipl );
    cvCopyImage( ipl, m_Ipl );


3、P31增加的成员函数LoadBMP其输入参数类型应为 const char*

在书中,LoadBMP的输入参数类型是 CString,不过我在VS2008下编译时会报错,不能由 CString 类型转换到 const char* 类型。另外OpenCV函数 cvLoadImage 定义为:IplImage* cvLoadImage( const char* filename, int flags ) ,所以在 VS2008 下LoadBMP其输入参数类型应为 const char*。

void MyIplClass::GetIplData( IplImage* ipl )
{
    memcpy( ipl->imageData, m_Ipl->imageData, m_Ipl->imageSize );
}

void MyIplClass::LoadBMP( const char* FileName )
{
    m_Ipl = cvLoadImage( FileName, 1 );
}


上面的 GetIplData 其实是把 m_Ipl->imageData 的内容,复制到了 ipl->imageData里面,注意,这个 ipl 就是传入参数,也就是
TheImage, 因此,实际上的图片,被保存在了 TheImage 里面(注意,TheImage是一个在程序里面早就初始化好了的图片)。那么,在下面的程序中:

void Cmfc_imgDlg::OnBnClickedReadimg()
{
    // TODO: Add your control notification handler code here
    MyIplClass* img = new MyIplClass;    //实例化MyIplClass类
    const char* filename;
    filename = "C://OpenCV2.0//projects//mfc_img//lena.bmp";
    img->LoadBMP( filename );
    img->GetIplData( TheImage ); 
    delete img;
    RedrawWindow( NULL, NULL, RDW_INVALIDATE );
}


其中的 delete img 只是删除了实例 img,但读入的图像已经保存在 TheImage 中,不影响后续的处理,这样,我们就可以新建一个按钮来执行读入图像后的处理工作,例如:

void Cmfc_imgDlg::OnBnClickedEdgedetect()
{
    // TODO: Add your control notification handler code here
    MyIplClass* img = new MyIplClass;
    img->ProcessIpl( TheImage );
    delete img;
    RedrawWindow( NULL, NULL, RDW_INVALIDATE );
}


最终就得到一个简单的MFC程序如下:






内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐