OpenCV开发笔记(三):OpenCV图像的概念和基本操作

本文是OpenCV开发笔记的第三篇,主要介绍了OpenCV中图像与矩阵的关系,重点讲解了cv::Mat类的定义、初始化方式和像素值的读写,包括at()函数、迭代器和数据指针的使用。此外,还讨论了图像局部操作,如选择单行/单列、指定区域以及取对角线元素。最后,提到了cv::Mat支持的矩阵运算操作。
摘要由CSDN通过智能技术生成

若该文为原创文章,未经允许不得转载

本文章博客地址:https://blog.csdn.net/qq21497936/article/details/100583521

各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究

红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中..

OpenCV开发专栏(点击传送门)

上一篇:OpenCV开发笔记(二):cvui交互界面

下一篇:OpenCV开发笔记(四):OpenCV图片和视频数据的读取与存储

前言

       OpenCV最主要的功能是用于操作图像,所以图像的概念贯穿整个OpenCV,与其相关的核心类就是cv::Mat。

图像与矩阵

 图像指数字图像。

        

       以上看到的是一张可视化的图片,但是在计算机中这副图像知识一系列亮度各异的店,该图片的像素为300 x 200像素的图,可以用一个300*200的矩阵来表示,举证元素的值表示这个位置上的像素的亮度,一般来说像素值越大表示该店越亮。

       放大“C”后如下图所示:

        

       每个点对应的亮度可以理解为rgb的值,无符号8位数3维,则一个像素点为3维数组,分别对应RGB的值,在OpenCV中数据类型为:CV_8U3C

       假设M x N,Iij表示第j行j列,对应上图就是M = 300, N = 200。

        

       注意:在Opencv中三维数组存储RGB值,存储颜色通道的顺序不是RGB,而是BGR,如下图:

       

OpenCV中的矩阵类cv::Mat

定义

       Mat贯穿了整个OpenCV。

       Mat类的关键定义如下:

class CV_EXPORTS Mat
{
    public:
    // 一系列函数
    ...
    /* flag 参数中包含许多关于矩阵的信息,如:
    -Mat 的标识
    -数据是否连续
    -深度
    -通道数目
    */
    int flags;
    // 矩阵的维数,取值应该大于或等于 2
    int dims;
    // 矩阵的行数和列数,如果矩阵超过 2 维,这两个变量的值都为-1
    int rows, cols;
    // 指向数据的指针
    uchar* data;
    // 指向引用计数的指针
    // 如果数据是由用户分配的,则为 NULL
    int* refcount;
    // 其他成员变量和成员函数
    ...
}

初始化方式

       Mat是一个非常优秀的图像类,它同时也是一个通用的矩阵类,可以用来创建和操作多维矩阵。有多种方法创建一个Mat对象。

       首先了解下数据的类型(举例:CV_8UC3):

       

初始化函数

// 默认形式
cv::Mat mat;
// 拷贝构造形式
cv::Mat mat(const cv::Mat& mat);
// 指定行列范围的拷贝构造
cv::Mat mat(const cv::Mat& mat, const cv::Range& rows, const cv::Range& cols);
// 指定ROI的拷贝构造
cv::Mat mat(const cv::Mat& mat, const cv::Rect& roi);
// 使用多维数组中指定范围内的数据的拷贝构造
cv::Mat mat(const cv::Mat& mat, const cv::Range* ranges);
// 指定类型和大小(行列)的二维数组(注意:是行在前,列在后)
cv::Mat mat(int rows, int cols, int type);
// 有初始化值的置顶类型和大小(行列)的二维数据
cv::Mat mat(int rows, int cols, int type, const Scalar& s);
// 使用预先存在数据定义的指定类型和大小(行列)的二维数组
cv::Mat mat(int rows, int cols, int type, void *data, size_t step = AUTO_STEP);
// 指定大小和类型的二维数组
cv::Mat mat(cv::Size sz, int type, const Scalar& s);
// 使用预先存在的数据定义的指定大小和类型的二维数组
cv::Mat mat(cv::Size sz, int type, void *data, size_t step = AUTO_STEP);
// 指定类型的多维数据
cv::Mat mat(int ndims, const int *sizes, int type);
// 使用预先存在的数据定义的指定类型的多维数组
cv::Mat mat(int ndims, const int* sizes, int type, void* data, size_t step = AUTO_STEP);
// 使用cv::Vec定义相同类型、大小为n的一维数组
cv::Mat mat(const cv::Vec<T, n>& vec, bool = copyData = true);
// 使用cv::Matx定义相同类型、大小为mxn的二维数组
cv::Mat mat(const cv::Matx<T, m, n>& vec, bool copyData = true);
// 使用STL vector定义相同类型的一维数组
cv::Mat mat(const std::vector<T>& vec, bool copyData = true);
// 使用zeros()函数定义指定大小和类型的cv::Mat(全为0)
cv::Mat mat = cv::Mat::zeros(int rows, int cols, int type);
// 使用ones()函数定义指定大小和类型的cv::Mat(全为0)
cv::Mat ma = cv::Mat::ones(int rows, int cols, int type);
// 使用eye()函数定义指定大小和类型的cv::Mat(恒等矩阵)
cv::Mat mat = cv::Mat::eye(int rows, int cols, int type);

像素值的读写

       很多时候,我们需要读取某个像素值,或者设置某个像素值;在更多的时候,
我们需要对整个图像里的所有像素进行遍历。OpenCV提供了多种方法来实现图像的遍历。

方法一:at()函数

cv::Mat grayim(600, 800, CV_8UC1);
cv::Mat colorim(600, 800, CV_8UC3);
// 遍历所有像素,并设置像素值
for( int i = 0; i < grayim.rows; ++i)
{
    for( int j = 0; j < grayim.cols; ++j )
}
    grayim.at<uchar>(i,j) = (i+j)%255;
// 遍历所有像素,并设置像素值
for( int i = 0; i < colorim.rows; ++i)
{
    for( int j = 0; j < colorim.cols; ++j )
    {
        cv::Vec3b pixel;
        // 注意:opencv通道顺序,BGR,非RGB
        pixel[0] = i%255;  // Blue
        pixel[1] = j%255;  // Green
        pixel[2] = 0;      // Red
        colorim.at<Vec3b>(i,j) = pixel;
    }
}

方法二:使用迭代器

// 遍历所有像素,并设置像素值

// 遍历所有像素,并设置像素值
cv::MatIterator_<uchar> grayit, grayend;
for(grayit = grayim.begin<uchar>(), grayend = grayim.end<uchar>();
    grayit != grayend; ++grayit)
{
    *grayit = rand()%255;
}
// 遍历所有像素,并设置像素值
cv::MatIterator_<cv::Vec3b> colorit, colorend;
for(colorit = colorim.begin<cv::Vec3b>(), colorend = colorim.end<cv::Vec3b>();
    colorit != colorend; ++colorit)
{
    (*colorit)[0] = rand()%255; // Blue
    (*colorit)[1] = rand()%255; // Green
    (*colorit)[2] = rand()%255; // Red
}

方法三:通过数据指针

cv::Mat grayim(600, 800, CV_8UC1);
cv::Mat colorim(600, 800, CV_8UC3);
//遍历所有像素,并设置像素值
for( int i = 0; i < grayim.rows; ++i)
{
    //获取第 i 行首像素指针
    uchar * p = grayim.ptr<uchar>(i);
    //对第 i 行的每个像素(byte)操作
    for( int j = 0; j < grayim.cols; ++j )
    p[j] = (i+j)%255;
}
//遍历所有像素,并设置像素值
for( int i = 0; i < colorim.rows; ++i)
{
    //获取第 i 行首像素指针
    cv::Vec3b * p = colorim.ptr<cv::Vec3b>(i);
    for( int j = 0; j < colorim.cols; ++j )
    {
        p[j][0] = i%255;    //Blue
        p[j][1] = j%255;    //Green
        p[j][2] = 0;        //Red
    }
}

图像局部操作

选择单行/单列

Mat Mat::row(int i) const
Mat Mat::col(int j) const

示例: A 矩阵的第i行,将这一行的所有元素都乘以2,然后赋值给第j行

A.row(j) = A.row(i)*2;

选择多行/多列

       Range是OpenCV 中新增的类,该类有两个关键变量star和end。Range 对
象可以用来表示矩阵的多个连续的行或者多个连续的列。其表示的范围为从 start到end,包含start。

示例

// 创建一个单位阵
Mat A = Mat::eye(10, 10, CV_32S);
// 提取第 1 到 3 列(不包括 3)
Mat B = A(Range::all(), Range(1, 3));
// 提取B的第 5 至 9 行(不包括 9)
// 其实等价于C = A(Range(5, 9), Range(1, 3))
Mat C = B(Range(5, 9), Range::all());

选择指定区域

    图像中提取感兴趣区域(Region of interest)有两种方法:

  • 方法一:使用构造函数
//创建宽度为 320,高度为 240 的 3 通道图像
Mat img(Size(320, 240), CV_8UC3);
//roi 是表示 img 中 Rect(10, 10, 100, 100)区域的对象
Mat roi(img, Rect(10, 10, 100, 100));
  • 方法二:使用括号运算符
Mat roi2 = img(Rect(10, 10, 100, 100));
当然也可以使用Range对象来定义感兴趣区域,如下:
// 用括号运算符
Mat roi3 = img(Range(10, 100), Range(10, 100)); 
// 用构造函数
Mat roi4(img, Range(10, 100), Range(10, 100));

取对角线元素

       矩阵的对角线元素可以使用cv::Mat类的diag()函数获取:

Mat Mat::diag(int d) const
  • 参数d=0时,表示取主对角线;
  • 当参数d>0是,表示取主对角线下方的次对线,如当d=1 时,表示取主对角线下方,且紧贴主多角线的元素;
  • 当参数d<0时,示取主对角线上方的次对角线。如同 row()和 col()函数, diag()函数也不进行内存复制操作,其复杂度也是O(1)。

cv::Mat矩阵支持的运算操作

       cv::Mat表达式所支持的运算。

       下面的列表中使用 A 和 B 表示 Mat 类型的对象,使用s表示Scalar对象,alpha 表示double值。

  • 加法,减法,取负:A+B, A-B, A+s, A-s, s+A, s-A, -A
  • 缩放取值范围:A * alpha
  • 矩阵对应元素的乘法和除法:A.mul(B),A/B,alpha/A
  • 矩阵乘法:A*B(注意此处是矩阵乘法,而不是矩阵对应元素相乘)
  • 矩阵转置:A.t()
  • 矩阵求逆和求伪逆:A.inv()
  • 矩阵比较运算:A cmpop B,A cmpop alpha,alpha cmpop A。此处cmpop
  • 可以是>,>=,==,!=,<=,<。如果条件成立,则结果矩阵(8U类型矩阵)的对应元素被置为255;否则置0
  • 矩阵位逻辑运算: A logicop B, A logicop s, s logicop A, ~A,此处 logicop
  • 可以是&,|和^
  • 矩阵对应元素的最大值和最小值:min(A, B),min(A, alpha),max(A, B),max(A, alpha)
  • 矩阵中元素的绝对值:abs(A)
  • 叉积和点积:A.cross(B),A.dot(B)

对矩阵不理解的可以阅读《线性代数矩阵以及Eigen库的介绍、编译和使用》中的矩阵操作解析。

上一篇:OpenCV开发笔记(二):cvui交互界面

下一篇:OpenCV开发笔记(四):OpenCV图片和视频数据的读取与存储

原博主博客地址:长沙红胖子Qt C++ Linux Arm_长沙红胖子_CSDN博客-Qt开发,图形图像处理,OpenCV图像处理领域博主
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:OpenCV开发笔记(三):OpenCV图像的概念和基本操作_长沙红胖子Qt C++ Linux Arm-CSDN博客

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙红胖子Qt(长沙创微智科)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值