数字图像处理第一次作业

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

累死了,楼主歇息去了。

 

转载于:https://www.cnblogs.com/hqqxyy/archive/2013/03/12/2956476.html

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值