OpenCV笔记(2)图像基本操作

一副尺寸为 M × N 的图像可以用一个 的图像可以用一个 M × N 的矩 阵来表示,的矩 阵来表示,阵元素的值表示这个位置上像亮度,一般来说越大该点亮。
一般来说,灰度图用 2维矩阵表示,彩色(多通道)图像用 3维矩阵( M × N × 3)表示。对于图像显来说,目前大部分设备都是用无符号 )表示。对于图像显来说,目前大部分设备都是用无符号 8 位整 数(类型为 CV_8U )
图像数据在计算机内存中的储顺序为以最左上点(也可能是下点)开始

I00I01I0(N-1)
I10I11I1(N-1)
I(M-1)0I(M-1)1I(M-1)(N-1)

Iij 表示第 i行 j列的像素值。如果是多通道图,比RGB图像,则每个图像,则每个像素用三个字节表示。在OpenCV中,RGB图像的通道顺序为BGR。

B00G00R00B01G01R01
B10G10R10B11G11R11

1.OpenCV命名空间
OpenCV中的C++类和函数都是定义在命名空间cv之内的,有两种方法可以访问。(1)在代码开头的适当位置,加上usingnamespace cv;这句。
(2)在使用OpenCV类和函数时,都加入cv::命名空间。

2.Mat类
cv::Mat类是用于保存图像以及其他矩阵数据的数据结构,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不同的维数)的指针。矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。

Mat基本操作——矩阵

    //二维三通道矩阵建立  (定义尺寸、指定存储元素的数据类型以及每个矩阵点的通道数)
    Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255)); //使用构造函数创建矩阵  
     /*
     CV_8UC3 表示使用8位的 unsigned char 型,每个像素由三个元素组成三通道,初始化为(0,0,255)
    */
    cout << "M = " << endl << " " << M << endl << endl; //格式化输出  

Mat重新定义了“<<”这个操作符,可以用来方便地输出所有像素值,而不需要使用for循环逐个像素输出。
这里写图片描述

    //三维  
    int sz[3] = { 3,3,3 };
    Mat L(3, sz, CV_8UC(1), Scalar::all(0));
    /*
    超过两维的矩阵:指定维数,然后传递一个指向一个数组的指针,这个数组包含每个维度的尺寸;其余的相同
    */

对于单通道图像,元素类型通常是8U(即8 unsigned char),也有可能是16S(16 short)或者32F(32 float)等等,但是基本都是C中的基本数据类型。
对于多通道图像,如RGB彩色图像,就需要用三个通道来表示。如果仍将图像视为二维矩阵,那么矩阵元素就不再是基本数据类型了,OpenCV中用Vec类预定义了一些向量,可以用来表示矩阵元素,如

 typedef Vec<uchar, 2> Vec2b; 
 typedef Vec<uchar, 3> Vec3b; 
 typedef Vec<uchar, 4> Vec4b; 

例如8U类型的RGB彩色图像可以用Vec3b,对Vec对象读取的时候也可以像数组一样用[]

Vec3b color;  // 用 color变量描述一种RGB 颜色
color[0]=255; //B 分量
color[1]= 0; //G分量
color[2]=  0; //R分量  

Mat基本操作——图像
当在程序中传递图像并创建拷贝时,思路是让每个 Mat 对象有自己的信息头,但共享同一个矩阵。这通过让矩阵指针指向同一地址而实现。而拷贝构造函数则只拷贝信息头和矩阵指针,而不拷贝矩阵。

    //拷贝构造函数和赋值函数 只拷贝信息头和矩阵指针
    Mat A, C;      // 创建信息头  
    A=imread("test.jpg", CV_LOAD_IMAGE_COLOR); // 读入图像  
    Mat B(A);                // 使用拷贝构造函数  
    C = A;                  // 赋值       

    //创建一个感兴趣区域(ROI),只需要创建包含边界信息的信息头
    Mat D(A, Rect(10, 10, 100, 100)); // 选取A中一个矩形区域,即只访问其矩形区域的信息头,只是创建信息头
    Mat E = A( Range::all(), Range(1, 3)); // 创建访问边界的信息头

    //拷贝矩阵本身(不只是信息头和矩阵指针)
    Mat F = A.clone();//复制图像,包括数据
    Mat G;
    A.copyTo(G);

    //测试  
    namedWindow( "a", CV_WINDOW_AUTOSIZE );  
    namedWindow( "c", CV_WINDOW_AUTOSIZE );  

    imshow( "a", D);  
    imshow( "c", E ); 

使用Mat要注意:
1.OpenCV函数中输出图像的内存分配是自动完成的(如果不特别指定的话)
2.使用OpenCV的C++接口时不需要考虑内存释放问题
3.赋值运算符和拷贝构造函数只拷贝信息头,仍然共用同一个矩阵
4.如果要复制矩阵数据,可以使用clone和copyTo函数

3.选取局部区域
1.单行或者单列选择

Mat line = A.row(i);//取出A矩阵的第i行

2.用Range选择多行或者多列

Mat B = A(Range::all(),Range(1,3));//提取1-3列
Mat C = B(Range(5,9),Range::all());//提取5-9行
//等价于C = A(Range(5,9),Range(1,3))

3.感兴趣区域(Region of Interest)
可使用构造函数或者括号运算符
对于ROI区域的定义也可以使用矩形对象或者Range对象

Mat ROI(img,Rect(10,10,100,100));
//Mat ROI = img(Rect(10,10,100,100));
Mat ROI(img,Range(10,100),Range(10,100);
//Mat ROI = img(Range(10,100),Range(10,100));

4.读写像素值
1.at()函数
读取矩阵中的某个像素或对某个像素赋值

uchar value = img.at<uchar>(i,j);//读出第i行第j列像素值
img.at<uchar>(i,j)=128;//将第i行第j列像素值设置为128

遍历图像

        Mat greyim(600, 800, CV_8UC1);
        Mat colorim(600, 800, CV_8UC3);
        // 遍历所有像素,并设置值
        for (int i = 0; i < greyim.rows; ++i)
            for (int j = 0; j < greyim.cols; ++j)
                greyim.at<uchar>(i, j) = (i + j) % 255;

        // 遍历所有像素,并设置值置值 
        for (int i = 0; i < colorim.rows; ++i)
            for (int j = 0; j < colorim.cols; ++j)
            {
                Vec3b pixel;
                pixel[0] = i % 255; //Blue pixel[0] = i%255; 
                pixel[1] = j % 255; //Green pixel[1] = j%255;
                pixel[2] = 0; //Re dpixel[2] = 0; 
                colorim.at<Vec3b>(i, j) = pixel; 
        }
        // 显示结果 
        imshow("greyim", greyim);
        imshow("colorim", colorim);

这里写图片描述

2.通过指针访问像素

for( int i = 0; i< greyim.rows; ++i)
{
// 获取第i行首像素指针
    uchar * p = greyim.ptr<uchar>(i);
// 对第i行的每个像素 (byte) 操作
    for( int j = 0; j< greyim.cols; ++)
        p[j] = (i+j)%255;
}

3.把W·H的一幅图像看成是一个1·(W·H)的一个一维数组,利用isContinuous()这个函数判断图像内的像素是否填充满

    int nc;  
    if(greyim.isContinuous())//判断是否被所有的像素填满  
    {  
        nc = greyim.rows*greyim.cols*greyim.channels();     
    }    
    uchar* data_2 = greyim.ptr<uchar>(0);//提取第一个像素点指针  
    for(int i=0;i<nc;i++)//遍历所有的元素  
    {  
        data_2[i] = 255;  
    }  

4.使用迭代器(iterator)

MatIterator_<Vec3b> it = img.begin<Vec3b>();  
MatIterator_<Vec3b> itend = img.end<Vec3b>();  
for (; it!=itend; it++)  
{  
         (*it)[0] = 255;  
} 

Tips:
1.使用迭代器也会是速度变慢,但迭代器的使用可以减少程序错误的发生几率
2.at()操作要比指针的操作慢很多,有大量数据时,用指针
3.扫描连续图像的做法用看作数组的那个方法也可以提高速度

参考Mat - The Basic Image Container

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值