話說數字圖像作業可是真多啊.而網上找的版本大都是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號的報告可是我寫的啊,絕對是原創。
累死了,樓主歇息去了。