數字圖像處理第一次作業


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

累死了,樓主歇息去了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM