话说数字图像作业可是真多啊.而网上找的版本大都是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 );
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);
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号的报告可是我写的啊,绝对是原创。
累死了,楼主歇息去了。