第三章 学习OpenCV——初探OpenCV

来源:互联网 发布:北京邮电大学软件学院 编辑:程序博客网 时间:2024/05/16 05:27

第三章 学习OpenCV——初探OpenCV

例3-1 使用cvSetImageROI来增加某范围的像素

调用cvSetImageROI()函数来构造RIO,实现对图像指定范围的蓝色通道增加150级操作。笔者将其写成了通过cmd调用和直接调用的两个版本,具体代码如下:

#include <cv.h>#include <highgui.h>using namespace std;int main(int argc, char* argv[]){    IplImage* src;    /*******************************cmd*********************************/    //if (argc == 7 && ((src = cvLoadImage(argv[1], 1)) != 0))   //判断传递给主函数的参数个数,加载图像至内存    //{    //  int x = atoi(argv[2]);            //字符串类型转化为整型 char2int    //  int y = atoi(argv[3]);    //  int width = atoi(argv[4]);    //  int height = atoi(argv[5]);    //  int add = atoi(argv[6]);    /*******************************Address*********************************/    if (src = cvLoadImage("D:\\Template\\OpenCV\\Template12_SetROI\\Debug\\3.jpg"))       //加载图像至内存,返回指向的指针    {        int x = 100;        int y = 100;        int width = 100;        int height = 100;        int add = 150;        cvSetImageROI(src, cvRect(x, y, width, height));    //设置ROI        cvAddS(src, cvScalar(add), src);                    //数组与标量的元素级相加        cvResetImageROI(src);                               //释放ROI        cvNamedWindow("Roi_Add", CV_WINDOW_AUTOSIZE);       //创建窗口        cvShowImage("Roi_Add", src);                        //显示图片        cvWaitKey(0);        cvReleaseImage(&src);                               //释放图像内存        cvDestroyWindow("Roi_Add");                         //释放窗口    }    return 0;}

运行结果如下图:
设置RIO操作指定范围

例3-2 利用widthStep方法把interest_img的所有像素增加1

利用widthStep方法RIO所有像素值增加1,实现cvSetImageROI()函数相同的功能,代码如下:

#include <cv.h>#include <highgui.h>using namespace std;int main(int argc, char* argv[]){    IplImage* interest_img = cvLoadImage("D:\\Template\\OpenCV\\Template12_SetROI\\Debug\\3.jpg");    CvRect interest_rect = cvRect(100,100,100,100);    IplImage* sub_img = cvCreateImageHeader(                    //创建一个图像头        cvSize(interest_rect.width, interest_rect.height),      //设置图像头的宽度、高度(与ROI相同)        interest_img->depth,                                    //设置图像头的深度(与ROI相同)        interest_img->nChannels);                               //设置图像头的通道数(与ROI相同)    sub_img->origin = interest_img->origin;             //设置图像头原点(与ROI相同)    sub_img->widthStep = interest_img->widthStep;       //设置行数据长度(可逐行步进到下一行开头)    sub_img->imageData = interest_img->imageData+       //指向第一行数据的指针+    interest_rect.y*interest_img->widthStep +           //行数*行数据长度+    interest_rect.x*interest_img->nChannels;            //列数*通道数    cvAddS(sub_img, cvScalar(150), sub_img);            //数组与标量的元素级相加    cvNamedWindow("Rio_Add", CV_WINDOW_AUTOSIZE);       //创建窗口    cvShowImage("Rio_Add", interest_img);               //显示图片    cvWaitKey(0);    cvReleaseImage(&interest_img);                      //释放图像内存    cvReleaseImage(&sub_img);                           //释放图像内存    cvDestroyWindow("Rio_Add");                         //释放窗口    return 0;}

运行结果如下图:
运用widthStep实现相同功能
widthStep操作可以保持一幅图像的多个子区域处于活动状态,而不必像cvSetImageROI()函数一样反复的设置、重置区域。

例3-3 Alpha(RGBA中的透明度通道)融合

调用cvAddWeighted()函数来实现Alpha融合,实现对两张图像的融合。笔者将其写成了通过cmd调用和直接调用的两个版本,具体代码如下:

#include <cv.h>#include <highgui.h>using namespace std;int main(int argc, char* argv[]){    IplImage* src1,*src2;    /*******************************cmd*********************************/    //if (argc == 9 && ((src1 = cvLoadImage(argv[1], 1)) != 0) && ((src2 = cvLoadImage(argv[2], 1)) != 0))   //判断传递给主函数的参数个数,加载图像至内存    //{    //  int x = atoi(argv[3]);            //字符串类型转化为整型 char2int    //  int y = atoi(argv[4]);    //  int width = atoi(argv[5]);    //  int height = atoi(argv[6]);    //  double alpha = (double)atof(argv[7]);    //  double beta = (double)atof(argv[8]);    /*******************************Address*********************************/    src1 = cvLoadImage("D:\\Template\\OpenCV\\Template13_AlphaBlend\\Debug\\2.jpg");    src2 = cvLoadImage("D:\\Template\\OpenCV\\Template13_AlphaBlend\\Debug\\3.jpg");       //加载图像至内存,返回指向的指针    int x = 50;    int y = 0;    int width = 320;    int height = 320;    double alpha = 0.8;    double beta = 0.2;    cvSetImageROI(src1, cvRect(x, y, width, height));       //设置ROI    cvSetImageROI(src2, cvRect(150, 70, width, height));    //设置ROI    cvAddWeighted(src1, alpha, src2, beta,0.0,src1);        //AlphaBlend    cvResetImageROI(src1);    cvResetImageROI(src2);                                  //释放ROI    cvNamedWindow("Roi_Add", CV_WINDOW_AUTOSIZE);           //创建窗口    cvShowImage("Roi_Add", src1);                           //显示图片    cvWaitKey(0);    cvReleaseImage(&src1);                                  //释放图像内存    cvReleaseImage(&src2);                                  //释放图像内存    cvDestroyWindow("Roi_Add");                             //释放窗口    //}    return 0;}

运行结果如下图:
Alpha融合

例3-4 往磁盘上写一个配置文件 cfg.xml,并将该配置文件读入

笔者未构造相应的色彩转换矩阵,因此省略了编写色彩变换矩阵那个环节,为求方便读入文件时仅打印了部分参数,程序代码如下:

#include <cv.h>#include <highgui.h>using namespace std;int main(){    CvFileStorage* fs = cvOpenFileStorage("cfg.xml",0,CV_STORAGE_WRITE);   //创建并打开CvFileStorage写数据                                                                            //0:临时内存区域创建    cvWriteInt(fs, "frame_count", 10);                       //写整型数据    cvStartWriteStruct(fs,"frame_size",CV_NODE_SEQ);         //创建结构    cvWriteInt(fs, 0, 320);                                  //编写结构                 cvWriteInt(fs, 0, 200);    cvEndWriteStruct(fs);                                    //结束结构编写//  cvWrite(fs, "color_cvt_matrix", cmatrix);                //编写色彩变换矩阵    cvReleaseFileStorage(&fs);                               //释放CvFileStorage句柄    CvFileStorage* fs1 = cvOpenFileStorage("cfg.xml", 0, CV_STORAGE_READ);  //创建并打开CvFileStorage写数据                                                                         //0:临时内存区域创建    int frame_cout = cvReadIntByName(fs1, 0, "frame_count", 5);         //读一个有名称整数 0:文件节点    CvSeq* s = cvGetFileNodeByName(fs1, 0, "frame_size")->data.seq;     //获取文件节点    int frame_width = cvReadInt((CvFileNode*)cvGetSeqElem(s,0));        //读一个无名称整数。    int frame_height = cvReadInt((CvFileNode*)cvGetSeqElem(s,1));       //cvGetSeqElem:指向指定序列元素指针    CvMat * color_cvt_matrix = (CvMat*)cvReadByName(fs1, 0, "color_cvt_matrix");    //找到对象并解码    cvReleaseFileStorage(&fs1);                                         //释放CvFileStorage句柄    cout << "frame_width = " << frame_width << endl << endl;            //输出cfg.xml中存储的宽度    system("pause");                                                    //暂停}

运行结果如下图:
往磁盘上写配置文件
读入的内容

此处应当注意,生成的cfg.xml文件的位置,与程序运行的根目录有关。
当在VS环境下使用本地Windows调试器调试运行时,生成的cfg.xml文件位置与项目文件的根目录在一起,如下图:
本地Windows调试器
文件位置
而直接运行已经编译生成的.exe文件进行写入,此时生成的cfg.xml文件位置,则是.exe文件存放的根目录,如下图:
文件位置

例3-5 使用OpenCV库函数实现数据类型操作

本例完成的工作如下:

 1. 选取一个负的浮点数,取绝对值,四舍五入后取极值; 2. 以系统时间作为种子产生一些随机数; 3. 创建CvPoint2D32f和CvPoint,并进行互相转换;

具体代码如下:

#include <cv.h>#include <highgui.h>  #include <stdlib.h>  #include <stdio.h> using namespace std;int main(){    float in, absolute;    int extremum;    in = -1.55;    absolute = abs(in);                     //取绝对值    extremum = cvRound(absolute);           //四舍五入取整    cout << "输入= " << in << endl;    cout << "绝对值= " << absolute << endl;    cout << "四舍五入极值= " << extremum << endl;    system("pause");    CvRNG rng;    rng = cvRNG(cvGetTickCount());              //64位长整数的时间数据作为种子    for (int i = 0; i<5; i++)    {        printf("%d\n", cvRandInt(&rng) % 6);    //返回均匀分布32位的随机数,%6将会是0~255的正整数        printf("%.2f\n", cvRandReal(&rng));     //返回均匀分布,0~1之间的随机小数    }    printf("Tick Frequency= %f\n", cvGetTickFrequency());   //系统时钟频率    system("pause");    CvPoint2D32f pointFloat;    CvPoint pointInt;    pointFloat = cvPoint2D32f(3.33, 2.22);    pointInt = cvPointFrom32f(pointFloat);      //CvPoint2D32f-->CvPoint    pointFloat = cvPointTo32f(pointInt);        //CvPoint2D32f<--CvPoint    cout << "整型点= " << pointInt.x << pointInt.y << endl;    cout << "浮点型= " << pointFloat.x << pointFloat.y << endl;    printf("浮点型= %f %f\n", pointFloat.x, pointFloat.y);    system("pause");}

运行结果如下图:
数据类型操作
如果使用cout输出发现数值为整数,千万不要认为CvPointf到CvPoint2D32f的转换失败了,而是因为ANSI C++里一个浮点型若是小数部分为0,直接输出必然是不带小数点,结果正如上图。

例3-6 使用OpenCV库函数实现矩阵操作

本例完成的工作如下:

 1. 创建一个三通道二维矩阵,字节类型,大小500*500,赋值为0,使用CVCircle()函数画一个圆,并显示,结果如图 1; 2. 通过CVPtrD()函数将指针指向中间的通道并设置为“绿色”,以(20,5)和(40,20)为顶点画一个绿色的长方形,结果如图1; 3. 创建一个100*100的RGB图像,赋值为0,以(20,5)和(40,20)为顶点画一个绿色的平面,结果如图2; 4. 创建一个210*210的单通道图像,赋值为0,使用RIO和cvSet()建立一个增长如金字塔状的数组,最外层为0,第二层为20,第三层40,以此类推,每层为10像素宽度,结果如图3; 5. 读取一个图像,创建两个原点位置设置、深度、通道、行长度都与读取图像相同的图像头。在新的图像头中设置宽度为20,高度为30,将imageData指针指向像素(5,10)和(50,60)像素位置,传递这两个新的图像头给cvNot(),显示读取的图像,大图像中有两个矩形,矩形内的值是原始值的反值,结果如图4。

具体代码如下(代码中附有打印矩阵函数,方便调试时查看矩阵的值):

#include <cv.h>#include <highgui.h>  #include <stdlib.h>  #include <stdio.h> using namespace std;int main(){    CvMat* firstMat = cvCreateMat(500,500, CV_8UC3);    CvPoint center = cvPoint(250, 250);    CvPoint rec1 = cvPoint(20, 5);    CvPoint rec2 = cvPoint(40, 20);    int radius = 100;    CvScalar color = CV_RGB(55, 55, 255);    CvSize image1size = cvSize(100, 100);    IplImage* img1 = cvCreateImage(image1size, IPL_DEPTH_8U, 3);    CvSize image2size = cvSize(210, 210);    int x = 0;    int y = 0;    int width = 210;    int height = 210;    int add = 20;    IplImage* img2 = cvCreateImage(image2size, IPL_DEPTH_8U, 1);    IplImage* img3 = cvLoadImage("D:\\Template\\OpenCV\\Template16_Create_Mat_Image_Imageheader\\Debug\\3.jpg");    IplImage* img3header1 = cvCreateImageHeader(cvSize(20,30), img3->depth, img3->nChannels);       //创建两个新的图像头,通道、深度与原图相同    IplImage* img3header2 = cvCreateImageHeader(cvSize(20, 30), img3->depth, img3->nChannels);      //大小20*30    img3header1->widthStep = img3->widthStep;       //设置widthStep与原图像相同    img3header2->widthStep = img3->widthStep;           img3header1->origin = img3->origin;             //设置原点与原图像相同    img3header2->origin = img3->origin;    img3header1->imageData = img3->imageData + 5 * img3->widthStep + 10 * img3->nChannels;      //指向(5,10)像素位置    img3header2->imageData = img3->imageData + 50 * img3->widthStep + 60 * img3->nChannels;     //指向(50,60)像素位置    cvZero(firstMat);           //矩阵元素清0    cvZero(img1);               //图像元素设置为0    cvZero(img2);               //图像元素设置为0    cvNot(img3header1, img3header1);    cvNot(img3header2, img3header2);    /******************打印矩阵********************/    //for (int i = 0; i<firstMat->cols; i++)        //矩阵指针行寻址    //{    //  for (int j = 0; j<firstMat->rows; j++)  //矩阵指针列寻址    //  {    //      int text = CV_MAT_ELEM(*firstMat,char, i, j);   //获取i行j列元素值    //      cout << text << " ";                            //空格    //  }    //  cout << endl;           //换行    //}    cvCircle(firstMat, center, radius, color);      //画一个圆    /**************矩阵上画一个绿色矩形,使用cvPtr2D算法***************/    for (int i = 20; i<40; i++)     //矩阵指针行寻址    {        for (int j = 5; j<20; j++)  //矩阵指针列寻址        {            uchar *ptr = cvPtr2D(firstMat, i, j);   //index1 行 index2 列              ptr[1] = 255;     //*****ptr[0]=255为蓝色 ptr[1]=255为绿色  ptr[2]=255为红色***/        }    }    /***********图像上画一个绿色矩形,指针算法**************/    for (int i = 20; i<40; i++)     //矩阵指针行寻址    {        uchar *ptr = (uchar*)img1->imageData + i*img1->widthStep;   //index1 行 index2 列        for (int j = 5; j<20; j++)      //矩阵指针列寻址        {            ptr[3*j+1] = 255;           //*****ptr[0]=255为蓝色 ptr[1]=255为绿色  ptr[2]=255为红色***/        }    }    /******************图像上做一个金字塔状数组,用RIO和cvSet()建立********************/    for (int i = 0; i < 10; i++)    {        cvSetImageROI(img2, cvRect(x, y, width, height));       //设置ROI//      cvAddS(img2, cvScalar(add), img2);                      //数组与标量的元素级相加        cvSet(img2, cvScalar(20*i));                            //设置所选通道所有值        cvResetImageROI(img2);                                  //释放ROI        x += 10;        y += 10;        width -= 20;        height -= 20;    }    cvNamedWindow("Mat", CV_WINDOW_AUTOSIZE);       //在窗口中显示    cvShowImage("Mat", firstMat);    cvNamedWindow("Image1", CV_WINDOW_AUTOSIZE);    //在窗口中显示    cvShowImage("Image1", img1);    cvNamedWindow("Image2", CV_WINDOW_AUTOSIZE);    //在窗口中显示    cvShowImage("Image2", img2);    cvNamedWindow("Image3", CV_WINDOW_AUTOSIZE);    //在窗口中显示    cvShowImage("Image3", img3);    cvWaitKey(0);    cvReleaseMat(&firstMat);    cvReleaseImage(&img1);    cvReleaseImage(&img2);    cvReleaseImage(&img3);    cvDestroyWindow("Mat");    cvDestroyWindow("Image1");    cvDestroyWindow("Image2");    cvDestroyWindow("Image3");}

运行结果如下图:
图1
图2
图3
图4

例3-7 使用OpenCV库函数实现图像操作

本例完成的工作如下:

 1. 加载一幅真实的图像,使用cvSplit()将图像分割为红、绿、蓝三个通道的图像,找到并显示绿图; 2. 克隆绿图两次,clone1和clone2; 3. 找出绿色平面的最大、最小值; 4. 将clone1的元素赋值为thresh=(max-min)/2.0; 5. 将clone2的元素赋值为0,调用cvCmp()函数,将clone2穿建伟一个标识绿图中值超过thresh的掩码图像; 6. 使用cvSubS()函数进行处理,并显示结果。

具体代码如下(为了显示对比效果明显最后的cvSubS()叠加的值设置为100):

#include <cv.h>#include <highgui.h>  #include <stdlib.h>  #include <stdio.h> using namespace std;int main(){    IplImage* img = cvLoadImage("D:\\Template\\OpenCV\\Template17_Image_Split_Clone_CmpMask\\Debug\\3.jpg");    IplImage* imgR = cvCreateImage(cvGetSize(img), img->depth, 1);    IplImage* imgG = cvCreateImage(cvGetSize(img), img->depth, 1);    IplImage* imgB = cvCreateImage(cvGetSize(img), img->depth, 1);    IplImage* clone1 = cvCreateImage(cvGetSize(img), img->depth, 1);    IplImage* clone2 = cvCreateImage(cvGetSize(img), img->depth, 1);    double* minG = NULL;    double* maxG = NULL;    uchar thresh = (uchar)((maxG - minG) / 2.0);    cvSplit(img, imgR, imgG, imgB, NULL);    cvNamedWindow("Green", CV_WINDOW_AUTOSIZE);     //在窗口中显示绿图    cvShowImage("Green", imgG);    cvCopy(imgG, clone1);       //克隆绿图    cvCopy(imgG, clone2);    cvMinMaxLoc(imgG, minG, maxG);  //找出绿图平面中最大值最小值    cvSet(clone1, cvScalar(thresh));    //clone1所有元素赋值为thresh    cvZero(clone2);                     //clone2赋值为0    cvCmp(imgG, clone1, clone2, CV_CMP_GE);     //设置clone2为标识绿图中超过thresh的掩码//  cvSubS(imgG, cvScalar(thresh / 2), imgG, clone2);    cvSubS(imgG, cvScalar(100), imgG, clone2);    cvNamedWindow("Change", CV_WINDOW_AUTOSIZE);        //在窗口中显示绿图    cvShowImage("Change", imgG);    cvWaitKey(0);    cvReleaseImage(&img);    cvReleaseImage(&imgR);    cvReleaseImage(&imgG);    cvReleaseImage(&imgB);    cvReleaseImage(&clone1);    cvReleaseImage(&clone1);    cvDestroyWindow("Green");    cvDestroyWindow("Change");}

运行结果如下图:
库函数进行图像处理

例3-8 创建一个结构,实现磁盘存储、读入

本例完成的工作如下:

 1. 创建一个结构体包含int、CvPoint和CvRect; 2. 构造该结构体的读写函数; 3. 创建一个长度为10的该结构体数组,写入磁盘、读入内存。

具体代码如下(本例中文件名处更改了路径,因此创建的文件出现在D盘):

#include <cv.h>#include <highgui.h>  #include <stdlib.h>  #include <stdio.h> using namespace std;typedef struct mystruct{    int m_x;    CvPoint m_point;    CvRect m_rect;}mystruct;void write_mystruct(CvFileStorage *fs, char *name, mystruct *ms){    fs = cvOpenFileStorage(name, 0, CV_STORAGE_WRITE);      //创建并打开CvFileStorage写数据                                                             //0:临时内存区域创建    cvWriteInt(fs, "my_int", ms->m_x);                      //写整型数据    cvStartWriteStruct(fs, "my_point", CV_NODE_SEQ);        //创建结构    cvWriteInt(fs, 0, ms->m_point.x);                       //编写结构      cvWriteInt(fs, 0, ms->m_point.y);    cvEndWriteStruct(fs);                                   //结束结构编写    cvStartWriteStruct(fs, "my_rect", CV_NODE_SEQ);    cvWriteInt(fs, 0, ms->m_rect.height);    cvWriteInt(fs, 0, ms->m_rect.width);    cvWriteInt(fs, 0, ms->m_rect.x);    cvWriteInt(fs, 0, ms->m_rect.y);    cvEndWriteStruct(fs);    cvReleaseFileStorage(&fs);                              //释放CvFileStorage句柄}void read_mystruct(CvFileStorage *fs, CvFileNode* ms_node, mystruct *ms){    fs = cvOpenFileStorage("D:\\mystruct.xml", 0, CV_STORAGE_READ);         //创建并打开CvFileStorage读数据     int frame_cout = cvReadIntByName(fs, 0, "my_int", 5);               //读一个有名称整数 0:文件节点    CvSeq* s = cvGetFileNodeByName(fs, 0, "my_point")->data.seq;        //获取文件节点    int point_x = cvReadInt((CvFileNode*)cvGetSeqElem(s, 0));           //读一个无名称整数    int point_y = cvReadInt((CvFileNode*)cvGetSeqElem(s, 1));           //cvGetSeqElem:指向指定序列元素指针    CvSeq* s1 = cvGetFileNodeByName(fs, 0, "my_rect")->data.seq;            int rect_height = cvReadInt((CvFileNode*)cvGetSeqElem(s1, 0));          int rect_width = cvReadInt((CvFileNode*)cvGetSeqElem(s1, 1));           int rect_x = cvReadInt((CvFileNode*)cvGetSeqElem(s1, 2));               int rect_y = cvReadInt((CvFileNode*)cvGetSeqElem(s1, 3));               cvReleaseFileStorage(&fs);                                          //释放CvFileStorage句柄    cout << "rect_height = " << rect_height << endl;                //输出cfg.xml中存储的宽度    system("pause");                                                //暂停}int main(){    mystruct m_array[10];    CvFileStorage *m_fs = NULL;    CvFileNode* m_node = NULL;    m_array[0] = { 1, cvPoint(1, 1), cvRect(1, 1, 1, 1) };    m_array[1] = { 2, cvPoint(2, 2), cvRect(2, 2, 2, 2) };    write_mystruct(m_fs, "D:\\mystruct.xml", m_array);    read_mystruct(m_fs, m_node, m_array);}

运行结果如下图:
创建一个结构体实现读写

1 0
原创粉丝点击