第三章 学习OpenCV——初探OpenCV

第三章 学习OpenCV——初探OpenCV

目录

  • 第三章 学习OpenCV初探OpenCV
    • 目录
      • 例3-1 使用cvSetImageROI来增加某范围的像素
      • 例3-2 利用widthStep方法把interest_img的所有像素增加1
      • 例3-3 AlphaRGBA中的透明度通道融合
      • 例3-4 往磁盘上写一个配置文件 cfgxml并将该配置文件读入
      • 例3-5 使用OpenCV库函数实现数据类型操作
      • 例3-6 使用OpenCV库函数实现矩阵操作
      • 例3-7 使用OpenCV库函数实现图像操作
      • 例3-8 创建一个结构实现磁盘存储读入

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

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

#include 
#include 
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;
}

运行结果如下图:
第三章 学习OpenCV——初探OpenCV_第1张图片

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

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

#include 
#include 
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;
}

运行结果如下图:
第三章 学习OpenCV——初探OpenCV_第2张图片
widthStep操作可以保持一幅图像的多个子区域处于活动状态,而不必像cvSetImageROI()函数一样反复的设置、重置区域。

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

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

#include 
#include 
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;
}

运行结果如下图:
第三章 学习OpenCV——初探OpenCV_第3张图片

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

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

#include 
#include 
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");                                                    //暂停
}

运行结果如下图:
第三章 学习OpenCV——初探OpenCV_第4张图片
第三章 学习OpenCV——初探OpenCV_第5张图片

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

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

本例完成的工作如下:

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

具体代码如下:

#include 
#include   
#include   
#include  

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");
}

运行结果如下图:
第三章 学习OpenCV——初探OpenCV_第8张图片
如果使用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 
#include   
#include   
#include  

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; icols; i++)        //矩阵指针行寻址
    //{
    //  for (int j = 0; jrows; 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");
}

运行结果如下图:
第三章 学习OpenCV——初探OpenCV_第9张图片
第三章 学习OpenCV——初探OpenCV_第10张图片
第三章 学习OpenCV——初探OpenCV_第11张图片
第三章 学习OpenCV——初探OpenCV_第12张图片

例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 
#include   
#include   
#include  

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");
}

运行结果如下图:
第三章 学习OpenCV——初探OpenCV_第13张图片

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

本例完成的工作如下:

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

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

#include 
#include   
#include   
#include  

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);
}

运行结果如下图:
第三章 学习OpenCV——初探OpenCV_第14张图片

你可能感兴趣的:(OpenCV)