话说数字图像作业可是真多啊.而网上找的版本大都是Matlab做的,为了使学弟学妹们的可用的版本多一点,想着数字图像处理怎么可以没有OpenCV这个大杀器,尽管用OpenCV确实是比Matlab要麻烦很多,但是为了提高自己的编程能力, 本着我不入地狱谁入地狱的原则, 也是为了给我可爱的学弟学妹们留下一点财富吧.(你的老师要是也布置这样的作业, 那毫无疑问, 咱们是一个大学的, 甚至我们的老师都是同一个人, 当你们默默的复制粘贴时, 可千万不要忘了你们的学长啊).
进入正题,本次主要解决五个问题
1、Bmp图像格式简介;
2、把lena 512*512图像灰度级逐级递减8-1显示;
3、计算lena图像的均值方差;
4、把lena图像用近邻、双线性和双三次插值法zoom到2048*2048;
5、把lena和elain图像分别进行水平shear(参数可设置为1.5,或者自行选择)和旋转30度,并采用用近邻、双线性和双三次插值法zoom到2048*2048;
一、 BMP图像格式简介
BMP( Bitmap-File )图形文件是Windows采用的图形文件格式,在Windows环境下运行的所有图像处理软件都支持BMP图像文件格式。 Windows系统内部各图像绘制操作都是以BMP为基础的。Windows 3.0以前的BMP图像文件格式与显示设备有关,因此把这种BMP图像文件格式称为设备相关位图DDB( device-dependent bitmap )文件格式。Windows 3.0以后的BMP图像文件与显示设备无关,因此把这种BMP图像文件格式称为设备无关位图DIB( device-independent bitmap )格式。
Windows 3.0以后,在系统中仍然存在DDB位图。像 BitBlt 这种函数就是基于DDB位图的,只不过如果你想将图像以BMP格式保存到磁盘文件中时,微软极力推荐你以DIB格式保存,目的是为了让Windows能够在任何类型的显示设备上显示所存储的图像。BMP位图文件默认的文件扩展名是BMP或者bmp,有时它也会以.DIB或.RLE作扩展名。
二、 把lena 512*512图像灰度级逐级递减8-1显示
原图像
Lena elain
本次应当对单个像素点一一处理,故使用了cvGet2D来取每一个像素点的值,通过整形和浮点型的运算来分级,然后通过cvSet2D函数设置每个像素点的值。具体程序请向后翻,其实博主事先是想用指针对数组操作来处理的,但是总是报内存错误,不清楚是哪儿的原因,好像是OpenCV内部函数的缘故,当做加减法时,会出现条纹,当做乘除法时,直接报错,调试时发现单个像素点可以达到1.6*e10的值,远大于256,不明所以。还请高手明示。这是用指针时的代码
//对行 进行遍历 for(y=0;y<mat_3Chanel.rows;y++) { //将指针偏移到第y行 的起始处 float* p_float = (float*)(mat_3Chanel.data.ptr + y*mat_3Chanel.step) ; //对第y行的 元素进行遍历 for(x=0;x<mat_3Chanel.cols;x++) { *(p_float+x*nChannels) = int(*(p_float+x*nChannels)/2.0)*2; //指针偏移到第y行的第x个元素的起始处 } }
正确代码请往后翻啊,先贴个例子,这是将Lena化为两个灰度级的核心函数
cv::Scalar pixel1; //int nChannels = 1; for(y=0;y<lena->height;y++) { for(x=0;x<lena->width;x++) { //得到一个像素的值 pixel1 = cvGet2D( lena, y, x); //转换 pixel1.val[0] = int(pixel1.val[0]/128.0)*128; //设置格式 cvSet2D( lena1, y, x, pixel1 ); } }
最后结果
总结一下哈:
当遇见暂时不能解决的问题时,要想想还有没有其他的办法,其他原来看不好的办法是不是在本题中可以很好使用,不要耗在一点上不动,白浪费时间。
三、 计算lena图像的均值方差;
分析
计算均值方差考虑到函数cvAvgSdv, 由函数直接得出答案.
直接上代码
CvScalar Avg; CvScalar std_dev; cvAvgSdv( lena, &Avg, &std_dev); cout<<"averege is "<<Avg.val[0]<<endl; cout<<"std_dev is "<<std_dev.val[0]<<endl;
结果
均值为99.0512 标准差为52.8775
四、 把lena图像用近邻、双线性和双三次插值法zoom到2048*2048;
调用函数cvResize, 其核心代码是
cvResize(lena, lena_NN, CV_INTER_NN);
cvResize(lena, lena_LINEAR, CV_INTER_LINEAR);
cvResize(lena, lena_CUBIC, CV_INTER_CUBIC);
其中CV_INTER_NN代表的是近邻插值法, CV_INTER_LINEAR代表的是双线性插值法, CV_INTER_CUBIC代表的是双三次插值法
又由于2048*2048的图像过大,不易于显示,故采用CV_WINDOW_NORMAL形式显示.
对了这个程序还有一个小bug,我给lena_NN创建窗口时,用了CV_WINDOW_NORMAL,但是显示时还是很大,不知道为什么
结果
总结一下
三者仅凭肉眼是很难看出差别的,与原图像相比也很难找出不同之处,表示这三者在对图像的显示方面都是很好的。
五、 把lena和elain图像分别进行水平shear(参数可设置为1.5,或者自行选择)和旋转30度,并采用用近邻、双线性和双三次插值法zoom到2048*2048;
注:下面大段引用小魏的博客,其实我觉的她讲的比我好多了,建议你们想搞明白的直接去翻她的博客,网址:http://blog.csdn.net/xiaowei_cqu/article/details/7616044
好,我们继续
分析
几何变换可以看成图像中物体(或像素)空间位置改变,或者说是像素的移动。
几何运算需要空间变换和灰度级差值两个步骤的算法,像素通过变换映射到新的坐标位置,新的位置可能是在几个像素之间,即不一定为整数坐标。这时就需要灰度级差值将映射的新坐标匹配到输出像素之间。最简单的插值方法是最近邻插值,就是令输出像素的灰度值等于映射最近的位置像素,该方法可能会产生锯齿。插值算法感觉只要了解就可以了,图像处理中比较需要理解的还是空间变换。
空间变换对应矩阵的仿射变换。一个坐标通过函数变换的新的坐标位置:
所以在程序中我们可以使用一个2*3的数组结构来存储变换矩阵:
以最简单的平移变换为例,平移(b1,b2)坐标可以表示为:
因此,平移变换的变换矩阵及逆矩阵记为:
缩放变换:将图像横坐标放大(或缩小)sx倍,纵坐标放大(或缩小)sy倍,变换矩阵及逆矩阵为:
选择变换:图像绕原点逆时针旋转a角,其变换矩阵及逆矩阵(顺时针选择)为:
OpenCV中的图像变换函数
基本的放射变换函数:
void cvWarpAffine( const CvArr* src,//输入图像 CvArr* dst, //输出图像 const CvMat* map_matrix, //2*3的变换矩阵 int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS, //插值方法的组合 CvScalar fillval=cvScalarAll(0) //用来填充边界外的值 );
另外一个比较类似的函数是cvGetQuadrangleSubPix:
void cvGetQuadrangleSubPix( const CvArr* src, //输入图像 CvArr* dst, // 提取的四边形 const CvMat* map_matrix //2*3的变换矩阵 );
这个函数用以提取输入图像中的四边形,并通过map_matrix变换存储到dst中,与WarpAffine变换意义相同,
即对应每个点的变换:
WarpAffine与 GetQuadrangleSubPix 不同的在于cvWarpAffine 要求输入和输出图像具有同样的数据类型,有更大的资源开销(因此对小图像不太合适)而且输出图像的部分可以保留不变。而 cvGetQuadrangleSubPix 可以精确地从8位图像中提取四边形到浮点数缓存区中,具有比较小的系统开销,而且总是全部改变输出图像的内容。
首先对shear. 在OpenCV 2.3的参考手册中《opencv_tutorials》介绍了一种确定变换矩阵的方法,通过三个点变换的几何关系映射实现变换。
shear核心代码
srcTri[0] = Point2f( 0,0 ); srcTri[1] = Point2f( src.cols - 1, 0 ); srcTri[2] = Point2f( 0, src.rows - 1 ); dstTri[0] = Point2f( src.cols*0.0, src.rows*0.0); dstTri[1] = Point2f( src.cols * 0.5, src.rows*0.0 ); dstTri[2] = Point2f( src.cols*0.5, src.rows - 1 ); warp_mat = getAffineTransform( srcTri, dstTri ); warpAffine( src, warp_dst, warp_mat, warp_dst.size() );
然后就是扩展至2048*2048 , 其核心代码为
cv::resize( warp_dst , lena_shear_NN,cv::Size(2048,2048),CV_INTER_NN); cv::resize( warp_dst , lena_shear_LINEAR,cv::Size(2048,2048),CV_INTER_LINEAR); cv::resize( warp_dst , lena_shear_CUBIC,cv::Size(2048,2048),CV_INTER_CUBIC);
然后对旋转30度的
构造旋转矩阵
CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2); cv2DRotationMatrix(center, 30, 1.0, &map_matrix); map[2] += (width_rotate - width) / 2; map[5] += (height_rotate - height) / 2;
然后就是旋转运算和放大(690是博主试了好多次才试出来的,大小刚好撑得下512旋转30度)
cv::warpAffine( elain ,elain_rotate_NN, map_matrix1,cv::Size(690,690),CV_INTER_NN); cv::warpAffine( elain ,elain_rotate_LINEAR, map_matrix1,cv::Size(690,690),CV_INTER_LINEAR); cv::warpAffine( elain ,elain_rotate_CUBIC, map_matrix1,cv::Size(690,690),CV_INTER_CUBIC); cv::resize( elain_rotate_NN, elain_rotate_NN, cv::Size(2048,2048),CV_INTER_NN); cv::resize( elain_rotate_LINEAR, elain_rotate_LINEAR, cv::Size(2048,2048),CV_INTER_LINEAR); cv::resize( elain_rotate_CUBIC, elain_rotate_CUBIC, cv::Size(2048,2048),CV_INTER_CUBIC);
结果
六、 总结
OpenCV确实是比Matlab要麻烦很多,但是可以加强我们的动手能力, 同时加强我们对图像本质的认识,不过,我认为从中学到的东西最重要的是面对困难时的心态和方法, 以及当面对位置问题时分析问题,解决问题的能力和信心吧,毕竟问题是永远也解决不完的,但是我们可以改变的是面对困难时的态度提升的是我们的能力, 还有一点是小魏说的,我觉的挺好的,就是别人总结出来的东西能帮助我们在一开始迅速入门,但要学深,学精,终归还是要自己去努力挖的。
七、 源代码
// Homework No1.cpp : Defines the entry point for the console application. // houqiqi #include "stdafx.h" #include <iostream> #include "highgui.h" #include "cv.h" #include "cxcore.h" #include "math.h" using namespace std; using namespace cv; int _tmain(int argc, _TCHAR* argv[]) { //load the image lena IplImage* lena = cvLoadImage ( "C:\\Users\\qiqi\\Desktop\\第一次作业[1]\\lena.bmp",0); IplImage* lena1 = cvCreateImage( cvGetSize( lena ), lena->depth, lena->nChannels); IplImage* lena2 = cvCreateImage( cvGetSize( lena ), lena->depth, lena->nChannels); IplImage* lena3 = cvCreateImage( cvGetSize( lena ), lena->depth, lena->nChannels); IplImage* lena4 = cvCreateImage( cvGetSize( lena ), lena->depth, lena->nChannels); IplImage* lena5 = cvCreateImage( cvGetSize( lena ), lena->depth, lena->nChannels); IplImage* lena6 = cvCreateImage( cvGetSize( lena ), lena->depth, lena->nChannels); IplImage* lena7 = cvCreateImage( cvGetSize( lena ), lena->depth, lena->nChannels); IplImage* lena8 = cvCreateImage( cvGetSize( lena ), lena->depth, lena->nChannels); //create windows cv::namedWindow( "lena8", CV_WINDOW_AUTOSIZE ); cv::namedWindow( "lena7", CV_WINDOW_AUTOSIZE ); cv::namedWindow( "lena6", CV_WINDOW_AUTOSIZE ); cv::namedWindow( "lena5", CV_WINDOW_AUTOSIZE ); cv::namedWindow( "lena4", CV_WINDOW_AUTOSIZE ); cv::namedWindow( "lena3", CV_WINDOW_AUTOSIZE ); cv::namedWindow( "lena2", CV_WINDOW_AUTOSIZE ); cv::namedWindow( "lena1", CV_WINDOW_AUTOSIZE ); int y, x; float temp; cv::Scalar pixel1; cv::Scalar pixel2; cv::Scalar pixel3; cv::Scalar pixel4; cv::Scalar pixel5; cv::Scalar pixel6; cv::Scalar pixel7; cv::Scalar pixel8; //int nChannels = 1; for(y=0;y<lena->height;y++) { for(x=0;x<lena->width;x++) { // pixel1 = cvGet2D( lena, y, x); pixel2 = cvGet2D( lena, y, x); pixel3 = cvGet2D( lena, y, x); pixel4 = cvGet2D( lena, y, x); pixel5 = cvGet2D( lena, y, x); pixel6 = cvGet2D( lena, y, x); pixel7 = cvGet2D( lena, y, x); pixel8 = cvGet2D( lena, y, x); // pixel1.val[0] = int(pixel1.val[0]/128.0)*128; pixel2.val[0] = int(pixel2.val[0]/64.0)*64; pixel3.val[0] = int(pixel3.val[0]/32.0)*32; pixel4.val[0] = int(pixel4.val[0]/16.0)*16; pixel5.val[0] = int(pixel5.val[0]/8.0)*8; pixel6.val[0] = int(pixel6.val[0]/4.0)*4; pixel7.val[0] = int(pixel7.val[0]/2.0)*2; pixel8.val[0] = int(pixel8.val[0]/1.0)*1; // cvSet2D( lena1, y, x, pixel1 ); cvSet2D( lena2, y, x, pixel2 ); cvSet2D( lena3, y, x, pixel3 ); cvSet2D( lena4, y, x, pixel4 ); cvSet2D( lena5, y, x, pixel5 ); cvSet2D( lena6, y, x, pixel6 ); cvSet2D( lena7, y, x, pixel7 ); cvSet2D( lena8, y, x, pixel8 ); } } //show the image cvShowImage( "lena1", lena1); cvShowImage( "lena2", lena2); cvShowImage( "lena3", lena3); cvShowImage( "lena4", lena4); cvShowImage( "lena5", lena5); cvShowImage( "lena6", lena6); cvShowImage( "lena7", lena7); cvShowImage( "lena8", lena8); /*********************************************************************************** we calculate the averery and std_dev ***********************************************************************************/ CvScalar Avg; CvScalar std_dev; cvAvgSdv( lena, &Avg, &std_dev); cout<<"averege is "<<Avg.val[0]<<endl; cout<<"std_dev is "<<std_dev.val[0]<<endl; /************************************************************************************ we convert the lena 512*512 to 2048*2048 by NN, LINEAR and CUBIC ************************************************************************************/ IplImage* lena_NN = cvCreateImage( cvSize(2048,2048), lena->depth, lena->nChannels); IplImage* lena_LINEAR = cvCreateImage( cvSize(2048,2048), lena->depth, lena->nChannels); IplImage* lena_CUBIC = cvCreateImage( cvSize(2048,2048), lena->depth, lena->nChannels); cvNamedWindow("lena_NN",CV_WINDOW_NORMAL); cvNamedWindow("lena_LINEAR",CV_WINDOW_NORMAL); cvNamedWindow("lena_CUBIC",CV_WINDOW_NORMAL); cvResize(lena, lena_NN, CV_INTER_NN); cvResize(lena, lena_LINEAR, CV_INTER_LINEAR); cvResize(lena, lena_CUBIC, CV_INTER_CUBIC); cvShowImage("lena_NN",lena_NN); cvShowImage("lena_LINEAR",lena_LINEAR); cvShowImage("lena_CUBIC",lena_CUBIC); /********************************************************************************************* we shear lena whose parameter is 2 *********************************************************************************************/ Point2f srcTri[3]; Point2f dstTri[3]; Mat rot_mat( 2, 3, CV_32FC1 ); Mat warp_mat( 2, 3, CV_32FC1 ); Mat src, warp_dst, warp_rotate_dst; //读入图像 src = imread( "C:\\Users\\qiqi\\Desktop\\第一次作业[1]\\lena.bmp", 1 ); warp_dst = Mat::zeros( src.rows, src.cols, src.type() ); srcTri[0] = Point2f( 0,0 ); srcTri[1] = Point2f( src.cols - 1, 0 ); srcTri[2] = Point2f( 0, src.rows - 1 ); dstTri[0] = Point2f( src.cols*0.0, src.rows*0.0); dstTri[1] = Point2f( src.cols * 0.5, src.rows*0.0 ); dstTri[2] = Point2f( src.cols*0.5, src.rows - 1 ); warp_mat = getAffineTransform( srcTri, dstTri ); warpAffine( src, warp_dst, warp_mat, warp_dst.size() ); Mat lena_shear_NN; Mat lena_shear_LINEAR; Mat lena_shear_CUBIC; cv::resize( warp_dst , lena_shear_NN,cv::Size(2048,2048),CV_INTER_NN); cv::resize( warp_dst , lena_shear_LINEAR,cv::Size(2048,2048),CV_INTER_LINEAR); cv::resize( warp_dst , lena_shear_CUBIC,cv::Size(2048,2048),CV_INTER_CUBIC); cv::namedWindow("lena_shear_NN",CV_WINDOW_NORMAL); cv::namedWindow("lena_shear_LINEAR",CV_WINDOW_NORMAL); cv::namedWindow("lena_shear_CUBIC",CV_WINDOW_NORMAL); cv::imshow("lena_shear_NN",lena_shear_NN); cv::imshow("lena_shear_LINEAR",lena_shear_LINEAR); cv::imshow("lena_shear_CUBIC",lena_shear_CUBIC); /********************************************************************************************* we rotated the elain Image 30 degrees and convert it to 2048*2048 by NN, LINEAR and CUBIC *********************************************************************************************/ Mat elain = imread("C:\\Users\\qiqi\\Desktop\\第一次作业[1]\\elain1.bmp",1); double angle = 30 * CV_PI / 180.; double a = sin(angle), b = cos(angle); int width = elain.cols; int height =elain.rows; int width_rotate= int(height * fabs(a) + width * fabs(b)); int height_rotate=int(width * fabs(a) + height * fabs(b)); float map[6]; CvMat map_matrix = cv::Mat(2, 3, CV_32F, map); CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2); cv2DRotationMatrix(center, 30, 1.0, &map_matrix); map[2] += (width_rotate - width) / 2; map[5] += (height_rotate - height) / 2; Mat elain_rotate_NN; Mat elain_rotate_LINEAR; Mat elain_rotate_CUBIC; Mat map_matrix1 = Mat::Mat( &map_matrix ); cv::warpAffine( elain ,elain_rotate_NN, map_matrix1,cv::Size(690,690),CV_INTER_NN); cv::warpAffine( elain ,elain_rotate_LINEAR, map_matrix1,cv::Size(690,690),CV_INTER_LINEAR); cv::warpAffine( elain ,elain_rotate_CUBIC, map_matrix1,cv::Size(690,690),CV_INTER_CUBIC); cv::resize( elain_rotate_NN, elain_rotate_NN, cv::Size(2048,2048),CV_INTER_NN); cv::resize( elain_rotate_LINEAR, elain_rotate_LINEAR, cv::Size(2048,2048),CV_INTER_LINEAR); cv::resize( elain_rotate_CUBIC, elain_rotate_CUBIC, cv::Size(2048,2048),CV_INTER_CUBIC); cv::namedWindow("elain_rotate_NN",CV_WINDOW_NORMAL); cv::namedWindow("elain_rotate_LINEAR",CV_WINDOW_NORMAL); cv::namedWindow("elain_rotate_CUBIC",CV_WINDOW_NORMAL); cv::imshow ("elain_rotate_NN",elain_rotate_NN); cv::imshow ("elain_rotate_LINEAR",elain_rotate_LINEAR); cv::imshow ("elain_rotate_CUBIC",elain_rotate_CUBIC); cv::waitKey (0); return 0; }
八、 参考文献
http://blog.csdn.net/fengbingchun/article/details/6408293
http://blog.sina.com.cn/s/blog_753dfc490100vc6l.html
http://blog.sina.com.cn/s/blog_72e198a10100sbrh.html
http://www.opencv.org.cn/forum/viewtopic.php?t=7613
http://blog.csdn.net/xiaowei_cqu/article/details/7616044
这些都对我的帮助挺大的。感谢大家支持。
PS:如果是老师搜的话,3月13号的报告可是我写的啊,绝对是原创。
累死了,楼主歇息去了。