OpenCV学习笔记(4)_Mat的简单理解及像素获取

OpenCV学习笔记(4)_Mat的简单理解

文章目录

0. 引言

​ OpenCV作为一个开源的图像处理库,它的任务主要是对图像进行操作. 那么对于我们学习者来说,首先要弄明白的是OpenCV的基础数据结构.

​ 稍微有点图像处理基础的同学都知道,图像的基础结构就是像素. 那么对于OpenCV2.1以上的版本来说,不需要再使用IplImage这个数据结构来作为图像的基础数据结构了,现在我们只需要了解Mat.

​ 今天的例程是一个对图像进行反色处理的样例.

OpenCV学习笔记(4)_Mat的简单理解及像素获取

OpenCV学习笔记(4)_Mat的简单理解及像素获取
OpenCV学习笔记(4)_Mat的简单理解及像素获取

​ 老规矩,先上代码,再进行分析.

​ 代码1.数组方法访问像素.

int test1()
{
    std::string filename = "../images/lena.jpg";
    cv::Mat srcImg = cv::imread(filename, CV_8UC2);
    cv::Mat dstImg = cv::Mat::zeros(srcImg.size(), srcImg.type());

    for (int i = 0; i < srcImg.rows; ++i)
    {
        uchar* curr_row = srcImg.ptr<uchar>(i);
        uchar* dst_row = dstImg.ptr<uchar>(i);
        for (int j = 0; j < srcImg.cols; ++j)
        {
            if (3 == srcImg.channels())
            {

                dstImg.at<cv::Vec3b>(i, j)[0] = 255 - srcImg.at<cv::Vec3b>(i, j)[0];
                dstImg.at<cv::Vec3b>(i, j)[1] = 255 - srcImg.at<cv::Vec3b>(i, j)[1];
                dstImg.at<cv::Vec3b>(i, j)[2] = 255 - srcImg.at<cv::Vec3b>(i, j)[2];
            }
            else if (1 == srcImg.channels())
            {

                dstImg.at<uchar>(i, j) = 255 - srcImg.at<uchar>(i, j);
            }

        }
    }

    cv::namedWindow("result", CV_WINDOW_AUTOSIZE);
    cv::imshow("result", dstImg);
    cv::waitKey();
    cv::destroyAllWindows();
    return 0;
}

​ 代码2.利用指针访问像素.

int test2()
{
    std::string filename = "../images/lena.jpg";
    cv::Mat srcImg = cv::imread(filename, CV_8UC2);
    cv::Mat dstImg = cv::Mat::zeros(srcImg.size(), srcImg.type());

    for (int i = 0; i < srcImg.rows; ++i)
    {
        uchar* curr_row = srcImg.ptr<uchar>(i);
        uchar* dst_row = dstImg.ptr<uchar>(i);
        for (int j = 0; j < srcImg.cols; ++j)
        {
            if (3 == srcImg.channels())
            {

                *dst_row++ = 255 - *curr_row++;
                *dst_row++ = 255 - *curr_row++;
                *dst_row++ = 255 - *curr_row++;
            }
            else if (1 == srcImg.channels())
            {

                *dst_row++ = 255 - *curr_row++;
            }

        }
    }

    cv::namedWindow("result", CV_WINDOW_AUTOSIZE);
    cv::imshow("result", dstImg);
    cv::waitKey();
    cv::destroyAllWindows();
    return 0;
}

注:当以灰度图像读取时,则imread参数2选择CV_8UC1,若以RGB三通道图像读取时,则imread参数2选择CV_8UC2(注意此处并不是CV_8UC3,CV_8UC2表示RGB3通道图像,CV_8UC3表示的是RGB+透明色即4通道图像)

1. Mat的简单理解

​ Mat的数据结构可以理解为Head部分+Data部分.

OpenCV学习笔记(4)_Mat的简单理解及像素获取

​ 这里的Head部分用来存储Mat的属性参数,例如维度(dim)、尺寸大小(size)等.

​ Data部分用来存储Mat的图像矩阵,实际上这里只是保存了图像矩阵像素数据的指针.

​ 对于图像像素矩阵来说, rows表示行数(图像的高度),cols表示列数(图像的宽度),如同代码中的 srcImg.rowssrcImg.cols.

OpenCV学习笔记(4)_Mat的简单理解及像素获取

​ 以类型为CV_8UC1为例,像素中存储的是uchar格式的数据,范围为0~255.

​ 以类型为CV_8UC2为例 ,像素中存储的是Vec3b格式的数据,Vec3b由三个uchar数据组成,依次存储B、G、R三个通道的像素值.

OpenCV学习笔记(4)_Mat的简单理解及像素获取

​ 若以内存的角度来看Mat的像素矩阵存储,对于CV_8UC2来说,则如上图所示,存储的顺序为

pix[0,0].B, pix[0,0].G, pix[0,0].R, pix[0,1].B, pix[0,1].G, pix[0,1].R ... pix[0, cols-1].B, pix[0, cols-1].G, pix[0, cols-1].R
pix[1,0].B, pix[1,0].G, pix[1,0].R, pix[1,1].B, pix[1,1].G, pix[1,1].R ... pix[1, cols-1].B, pix[1, cols-1].G, pix[1, cols-1].R
...

pix[rows-1,0].B, pix[rows-1,0].G, pix[rows-1,0].R, pix[rows-1,1].B, pix[rows-1,1].G, pix[rows-1,1].R ... pix[rows-1, cols-1].B, pix[rows-1, cols-1].G, pix[rows-1, cols-1].R

​ 这个顺序非常重要,在后面如何访问像素时,我们将会再次提到它.

​ 来看一下读取到的lena图像在Mat中的存储状态:

OpenCV学习笔记(4)_Mat的简单理解及像素获取

2. 用Mat来存放图像数据

std::string filename = "../images/lena.jpg";
    cv::Mat srcImg = cv::imread(filename, CV_8UC2);
    cv::Mat dstImg = cv::Mat::zeros(srcImg.size(), srcImg.type());

cv::imread此函数通过带路径的图像文件名,和图像的存储类型,来读取图像.

如前所述,若类型选为CV_8UC1,则读取的是灰度图像;

若类型选为CV_8UC2,则读取的是三通道图像(注意,源图像是RGB或者是BGR没有关系,读取进来以后是以BGR通道形式.

3. 像素的访问和操作

3.1 以数组方式访问

​ 访问Mat中的像素,有两种方式,第一种方式是以数组的方式访问.

for (int i = 0; i < srcImg.rows; ++i)
    {
        uchar* curr_row = srcImg.ptr<uchar>(i);
        uchar* dst_row = dstImg.ptr<uchar>(i);
        for (int j = 0; j < srcImg.cols; ++j)
        {
            if (3 == srcImg.channels())
            {

                dstImg.at<cv::Vec3b>(i, j)[0] = 255 - srcImg.at<cv::Vec3b>(i, j)[0];
                dstImg.at<cv::Vec3b>(i, j)[1] = 255 - srcImg.at<cv::Vec3b>(i, j)[1];
                dstImg.at<cv::Vec3b>(i, j)[2] = 255 - srcImg.at<cv::Vec3b>(i, j)[2];
            }
            else if (1 == srcImg.channels())
            {

                dstImg.at<uchar>(i, j) = 255 - srcImg.at<uchar>(i, j);
            }

        }
    }

​ 若是灰度图像(1==srcImg.channels()),则像素为uchar类型,则 srcImg.at<uchar>(i,j)</uchar>读取了第i行(i从0到rows-1),第j列(j从0到cols-1)的像素.

​ 若是彩色图像(3==srcImg.channels()),则像素为Vec3b类型,则 srcImg.at<vec3b>(i,j)</vec3b>读取了第i行(i从0到rows-1),第j列(j从0到cols-1)的像素. 在这个像素里, srcImg.at<vec3b>(i,j)[0]</vec3b>为Blue通道的灰度值, srcImg.at<vec3b>(i,j)[1]</vec3b>为Green通道的灰度值, srcImg.at<vec3b>(i,j)[2]</vec3b>为Red通道的灰度值.

​ 反色的处理即用255减去每个通道的灰度值,将结果对应赋值给dstImg的对应像素.

​ 看一下程序运行过程中的dstImg像素值变化情况:

OpenCV学习笔记(4)_Mat的简单理解及像素获取

OpenCV学习笔记(4)_Mat的简单理解及像素获取

OpenCV学习笔记(4)_Mat的简单理解及像素获取

OpenCV学习笔记(4)_Mat的简单理解及像素获取

​ 对第一行操作完成后,才开始操作第二行,依此类推:

OpenCV学习笔记(4)_Mat的简单理解及像素获取

3.2 以指针方式访问

​ 第二种方式是以指针方式访问:

for (int i = 0; i < srcImg.rows; ++i)
    {
        uchar* curr_row = srcImg.ptr<uchar>(i);
        uchar* dst_row = dstImg.ptr<uchar>(i);
        for (int j = 0; j < srcImg.cols; ++j)
        {
            if (3 == srcImg.channels())
            {

                *dst_row++ = 255 - *curr_row++;
                *dst_row++ = 255 - *curr_row++;
                *dst_row++ = 255 - *curr_row++;
            }
            else if (1 == srcImg.channels())
            {

                *dst_row++ = 255 - *curr_row++;
            }

        }
    }

srcImg.ptr提供了访问像素中数据的指针方法,回忆起像素的存储顺序,则可知:

​ 若为灰色图像,则 srcImg.ptr<uchar>(i)</uchar>指向每一行的像素首地址,向下移动,直至移动到第j列,即到行末.

​ 若为彩色图像,则 srcImg.ptr<uchar>(i)</uchar>也同样指向每一行的像素首地址,顺序为BGRBGRBGR…,向下移动3个位置则到下一个像素,直至移动到第j列,即到行末.

OpenCV学习笔记(4)_Mat的简单理解及像素获取

OpenCV学习笔记(4)_Mat的简单理解及像素获取

OpenCV学习笔记(4)_Mat的简单理解及像素获取
OpenCV学习笔记(4)_Mat的简单理解及像素获取

Original: https://blog.csdn.net/horsee/article/details/123011178
Author: CarnivoreRabbit
Title: OpenCV学习笔记(4)_Mat的简单理解及像素获取

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/702480/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球