有过之前的那次碰壁之后,不敢在费时间编译OpenCV了,不过还是一直关注这个问题,openhw论坛中也有人遇到这个问题了,有的人解决了,不过我的情况还是比他的复杂。
这里我把他的贴出来,算是一个方法把。不过我还是继续往下走了,因为摄像头已经打开了。他的博客:http://www.openhw.org/wicoboy/blog/13-04/293302_71692.html
我今天的主题时opencv处理图像,以及QT界面设计。
根据之前做过的例子,主要在paintEvent中进行图像的显示。现在我的思路时将QImage转为OpenCV能够处理的Mat/IplImage/Array的形式,利用OpenCV库进行图像的处理。
void Widget::paintEvent(QPaintEvent *) { rs = vd->get_frame((void **)&p,&len); //IplImage* img = NULL; //IplImage* cannyImg = NULL; //CvArr* Cvframe; convert_yuv_to_rgb_buffer(p,pp,640,480); frame->loadFromData((uchar *)pp,/*len*/640 * 480 * 3 * sizeof(char)); ui->label->setPixmap(QPixmap::fromImage(*frame,Qt::AutoColor)); //Mat cvframe=qimage2mat(*frame); //cannyImg = cvCreateImage( cvGetSize( cvframe ), IPL_DEPTH_8U, 1 ); //cvConvertImage(cvframe,cannyImg,0); //Mat mtx(cvframe); //Canny(cvframe,cvframe, 50, 150, 3); //IplImage* cvframe=mtx; //*frame=mat2qimage(cvframe); ui->label_2->setPixmap(QPixmap::fromImage(*frame,Qt::AutoColor)); ui->label_3->setPixmap(QPixmap::fromImage(*frame,Qt::AutoColor)); ui->label_4->setPixmap(QPixmap::fromImage(*frame,Qt::AutoColor)); rs = vd->unget_frame(); }
要进行图像转换,必须要了解QImage,IplImage,Mat,Arrary的图像结构和要素吧。
Qt提供了4个图像处理的类:QImage,QPixmap,QBitmap和QPicture;QImage优化了I/O操作,可直接存取操纵像素,QImage支持单色,8位,32位和Alpha混合格式。
使用QImage构造函数,load函数,loadFromData几种方法。loadFromData((uchar *)pp,/*len*/640 * 480 * 3 * sizeof(char));
关于Mat数据格式我看了这篇博客,写的到位:http://blog.csdn.net/yang_xian521/article/details/7107786
Mat的矩阵结构其实在书里面也写的很清楚了,
<span style="font-size: medium;">typedef struct CvMat { int type; int step; /* for internal use only */ int* refcount; int hdr_refcount; union { uchar* ptr; short* s; int* i; float* fl; double* db; } data; #ifdef __cplusplus union { int rows; int height; }; union { int cols; int width; }; #else int rows; int cols; #endif } CvMat;</span>
主要分为两部分了,一个矩阵头,一个数据data;操作方法在上面的博客中讲的非常好。
关于IplImage:
在类型关系上,我们可以说IplImage类型继承自CvMat类型,当然还包括其他的变量将之解析成图像数据。
<span style="font-size: medium;">typedef struct _IplImage { int nSize; /* sizeof(IplImage) */ int ID; /* version (=0)*/ int nChannels; /* Most of OpenCV functions support 1,2,3 or 4 channels */ int alphaChannel; /* Ignored by OpenCV */ int depth; /* Pixel depth in bits: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F are supported. */ char colorModel[4]; /* Ignored by OpenCV */ char channelSeq[4]; /* ditto */ int dataOrder; /* 0 - interleaved color channels, 1 - separate color channels. cvCreateImage can only create interleaved images */ int origin; /* 0 - top-left origin, 1 - bottom-left origin (Windows bitmaps style). */ int align; /* Alignment of image rows (4 or 8). OpenCV ignores it and uses widthStep instead. */ int width; /* Image width in pixels. */ int height; /* Image height in pixels. */ struct _IplROI *roi; /* Image ROI. If NULL, the whole image is selected. */ struct _IplImage *maskROI; /* Must be NULL. */ void *imageId; /* " " */ struct _IplTileInfo *tileInfo; /* " " */ int imageSize; /* Image data size in bytes (==image->height*image->widthStep in case of interleaved data)*/ char *imageData; /* Pointer to aligned image data. */ int widthStep; /* Size of aligned image row in bytes. */ int BorderMode[4]; /* Ignored by OpenCV. */ int BorderConst[4]; /* Ditto. */ char *imageDataOrigin; /* Pointer to very origin of image data (not necessarily aligned) - needed for correct deallocation */ } IplImage;</span>
下面就是要进行QImage和OpenCV图像格式的转换,以及OpenCV和QImage图像格式的转换,图像格式清楚了,转换也就方便了。刚开始我用了下面两个函数:
/*QImage *Widget::IplImageToQImage(const IplImage *img)//change IplImage to QImage { QImage *image; // cvCvtColor(img,img,CV_BGR2RGB); //Mat mtx(img); //cv::cvtColor(mtx,mtx,CV_BGR2RGB); uchar *imgData=(uchar *)img->imageData; image=new QImage(imgData,img->width,img->height,QImage::Format_RGB888); return image; }*/ /*IplImage *Widget::QImageToIplImage(const QImage * qImage)//change QImage to IplImage { int width = qImage->width(); int height = qImage->height(); CvSize Size; Size.height = height; Size.width = width; IplImage *IplImageBuffer = cvCreateImage(Size, IPL_DEPTH_8U, 3); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { QRgb rgb = qImage->pixel(x, y); cvSet2D(IplImageBuffer, y, x, CV_RGB(qRed(rgb), qGreen(rgb), qBlue(rgb))); } } return IplImageBuffer; }*/
但是,IplImage的图像格式好多函数用不了,比如我用了cvtColor,Canny,发现一直是这样的错误:
: 错误:invalid initialization of reference of type 'cv::InputArray {aka const cv::_InputArray&}' from expression of type 'const IplImage* {aka const _IplImage*}'
一顿搜,发现这个错误是类型问题,才知道跟OpenCV版本有关系啊,
旧版本的OpenCV中的C结构体有 CvMat 和 CvMatND,目前我用的是 2.3.1版,里面的文档指出 CvMat 和 CvMatND 弃用了,在C++封装中用 Mat 代替,另外旧版还有一个 IplImage,同样用 Mat 代替;
然后发现我用的两个函数原型时这样的:
//! applies Canny edge detector and produces the edge map. CV_EXPORTS_W void Canny( InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false );
//! converts image from one color space to another CV_EXPORTS_W void cvtColor( InputArray src, OutputArray dst, int code, int dstCn=0 );
InputArray和IplImage显然不是同一种数据图像格式,而且强制转换在程序执行时出现了:segmentation fault;
所以我采用QImage和Mat相互转换来进行图像处理;
QImage Widget::mat2qimage(const Mat& mat) { Mat rgb; cvtColor(mat, rgb, CV_BGR2RGB); return QImage((const unsigned char*)(rgb.data), rgb.cols, rgb.rows, QImage::Format_RGB888); } Mat Widget::qimage2mat(const QImage& qimage) { cv::Mat mat = cv::Mat(qimage.height(), qimage.width(), CV_8UC4, (uchar*)qimage.bits(), qimage.bytesPerLine()); cv::Mat mat2 = cv::Mat(mat.rows, mat.cols, CV_8UC3 ); int from_to[] = { 0,0, 1,1, 2,2 }; cv::mixChannels( &mat, 1, &mat2, 1, from_to, 3 ); return mat2; }
编译很顺利通过了,但是在Zed板子上执行出错了:
OpenCV Error: Assertion failed (scn == 3 || scn == 4) in cvtColor, file /home/j4
terminate called after throwing an instance of 'cv::Exception'
what(): /home/jiong/opencv-2.4.5/modules/imgproc/src/color.cpp:3414: error: r
Aborted
看到错误实在cvtColor这里出现的,那就又有可能时cvtColor的参数不对了,回去再看这两个图像的格式。
其实,格式转换无非就是找到格式间参数的对应关系:
QImage Mat
数据指针 uchar* bits() uchar* data
宽度 int width() int cols
高度 int height() int rows
步长 int bytesPerLine() cols * elemeSize()
格式 Format_Indexed8 8UC1, GRAY,灰度图
Format_RGB888 8UC3, BGR, 需要通过mixChannels进行顺序调换
Format_ARGB32 8UC4, BGRA,需要通过mixChannels进行顺序调换
以此类推,只要保证通道数以及排列顺序一致即可。
发现Canny(cvframe,cvframe, 50, 150, 3);这一句输入输出都是单通道的,所以对于输出的图像进行修改cv::cvtColor(cvframe, cvframe, CV_GRAY2RGB);
果然错误消失了,出现图像了,可惜边缘检测后出现了4个重叠的图像,很是郁闷!
初步设计如下界面:
今天先到这里了,又要去看论文了!