opencv(四)-图像像素访问的七种方法

发布时间:2023-12-24 21:58:39

1. 图像在内存中的存储方式

BGR而不是RGB,内存足够大,可以以连续的方式存储行,所以这些行创建单个长行,所有内容都在一个接一个的位置。我们可以使用cv :: Mat :: isContinuous()函数判断。
在这里插入图片描述

2. 像素访问

像素访问速度的快慢,排序如下:
【1】使用LUT查找表访问图像像素,LUT方法通过映射关系表大大减少相关操作的时间复杂度,是最快的处理方式,常用于(替换、反转、赋值、阈值、二值化、灰度变换等)图像操作。
【2】使用isContinuous()访问图像像素,图像必须是连续的才能使用
【3】使用ptr<>(row)访问图像像素,速度最快
【4】使用data结合step访问图像像素
【5】使用ptr<>(row, col)访问图像像素
【6】使用迭代器iterator访问图像像素,相比用指针直接访问像素可能出现越界问题,迭代器绝对是非常安全
【7】使用at<>()访问图像像素,速度是最慢的,直观

2.1 使用下标M.at(i,j)

    int width = img.cols;
	int height = img.rows;
	int dims = img.channels();
	//循环遍历像素读写
	for(int row = 0; row < width;++row)
	{
		for(int col=0;col<height;++col )
		{
			if(dims==1)//黑白
			{
				img.at<uchar>(row, col) = saturate_cast<uchar>(img.at<uchar>(row, col) * 2);
			}
			if(dims==3)//彩图
			{
				img.at<Vec3b>(row, col)[0] = 255 - img.at<Vec3b>(row, col)[1];
				img.at<Vec3b>(row, col)[1] = 255 - img.at<Vec3b>(row, col)[2];
				img.at<Vec3b>(row, col)[2] = 255 - img.at<Vec3b>(row, col)[0];
			}
		}
	}

2.2 指针ptr<>(row)访问图像像素

for(int row=0;row<height;++row)
	{
		uchar*current_row = img.ptr<uchar>(row);
		for(int col=0;col<width;++col)
		{
			if (dims == 1)
			{
				*current_row++ = 255 - *current_row;//current_row[]数组的首地址,每行第一个像素
			}
			if (dims == 3)
			{
				*current_row++ = 255 - *current_row;
				*current_row++ = 255 - *current_row;
				*current_row++ = 255 - *current_row;
			}
		}
	}

参考博客:C/C++中 p++、++p、(*p)++、++*p的区别
或者

    Mat outputImg= srcImg.clone();
    int height = outputImg.rows;
    int weight= outputImg.cols;
    int colNum= outputImg.cols * outputImg.channels();//每一行元素个数
    for (int row = 0; row < height ; ++row)//行循环
    {
        uchar *srcData = srcImg.ptr<uchar>(row);
        uchar *outputData = outputImg.ptr<uchar>(row);
        for (int col = 0; col < colNum;j++)//列循环
        {
            outputData[col] = saturate_cast<uchar>(255 - srcData[col]);
        }
    }

2.3 使用迭代器iterator访问图像像素

// 设置参数
    dstImg = srcImg.clone(); // 设置目标图像与源图像一样
    // 获取迭代器
    Mat_<Vec3b>::iterator it = dstImg.begin<Vec3b>(); // 初始位置的迭代器
    Mat_<Vec3b>::iterator itEnd = dstImg.end<Vec3b>(); // 终止位置的迭代器

    // 存取彩色图像像素
    for (; it != itEnd; it++) // 行循环
    {
        // 开始处理每个像素,在OpenCV中,存储顺序是BGR
        (*it)[0] = (*it)[0] / div * div + div / 2;//blue通道
        (*it)[1] = (*it)[1] / div * div + div / 2;//绿色通道
        (*it)[2] = (*it)[2] / div * div + div / 2;//蓝色通道
    }

2.4 使用ptr<>(row, col)访问图像像素

        dstImg = srcImg.clone(); // 设置目标图像与源图像一样
		for (int row = 0; row < srcImg.rows; row++)
		{			
			for (int col = 0; col < srcImg.cols; col++)
			{
				Vec3b* pData = srcImg.ptr<Vec3b>(row, col);//彩图
				(*pData)[0] = 255 - (*pData)[0];
				(*pData)[1] = 255 - (*pData)[1];
				(*pData)[2] = 255 - (*pData)[2];
 
				//这种方式也可以
				//uchar* pData = m2.ptr<uchar>(row, col);
				//*(pData + 0) = 255 - *(pData + 0);
				//*(pData + 1) = 255 - *(pData + 1);
				//*(pData + 2) = 255 - *(pData + 2);
			}
		}

2.5 使用data结合step访问图像像素

成员函数step是返回该Mat对象一行所占的数据字节数。
成员函数data:uchar类型的指针,指向Mat数据矩阵的首地址
参考博客:https://blog.csdn.net/CxC2333/article/details/107735638

        dstImg = srcImg.clone(); // 设置目标图像与源图像一样
        uchar* pData = srcImg.data;
		MatStep mst = srcImg.step;
		for (int row = 0; row < srcImg.rows; row++)
		{			
			for (int col = 0; col < srcImg.cols; col++)
			{
				*(pData + row * mst + col * srcImg.channels() + 0) = 255 - *(pData + row * mst + col * srcImg.channels() + 0);
				*(pData + row * mst + col * srcImg.channels() + 1) = 255 - *(pData + row * mst + col * srcImg.channels() + 1);
				*(pData + row * mst + col * srcImg.channels() + 2) = 255 - *(pData + row * mst + col * srcImg.channels() + 2);
			}
		}
		

2.6 使用isContinouous()访问图像像素

图像行与行之间的存储可能是不连续的,进行像素值遍历,很大程度上造成数据指针移动的浪费。一般经过裁剪的Mat图像,都不再连续了,如cv::Mat crop_img = src(rect);crop_img 是不连续的Mat图像,如果想转为连续的,最简单的方法,就是将不连续的crop_img 重新clone()一份给新的Mat就是连续的了

    int row = src.rows;
    int col = src.cols;
    cv::Mat dstImg = src.clone();
    // 判断是否是连续图像,即是否有像素填充
    if (src.isContinuous() && dstImg.isContinuous())
    {
        row = 1;
        // 按照行展开
        col = col * src.rows * src.channels();
    }
    // 遍历图像的每个像素
    for (int i = 0; i < row; i++)
    {
        // 设定图像数据源指针及输出图像数据指针
        const uchar* pSrcData = src.ptr<uchar>(i);
        uchar* pResultData = temp.ptr<uchar>(i);
        for (int j = 0; j < col; j++)
        {
            *pResultData++ = 255 - *pSrcData++;
        }
    }

2.7 LTU查表法

    int row = src.rows;
    int col = src.cols;
    Mat temp = src.clone();
    // 建立LUT 反色table
    uchar LutTable[256 * 3];
    for (int i = 0; i < 256; ++i)
    {
        LutTable[i * 3] = 255 - i;
        LutTable[i * 3+1] = 255 - i;
        LutTable[i * 3+2] = 255 - i;
    }
    Mat lookUpTable(1, 256, CV_8UC3, LutTable);
    // 应用索引表进行查找
    LUT(src, lookUpTable, temp);

参考博客:【OpenCV】之LUT函数

3. 像素遍历

OpenCV3 之 遍历图像像素的14种方法

4.图像像素的算术操作

在原图上src1操作,如果src2和src1逻辑操作为1,src1像素值不变,否则src1像素值为0

    // 逻辑操作
    Mat dst1, dst2, dst3;
	bitwise_and(src1, src2, dst1);
	bitwise_xor(src1, src2, dst2);
	bitwise_or(src1, src2, dst3);
    // 取反操作
	Mat dst;
	bitwise_not(src, dst);
	
	Mat dst=~src;
文章来源:https://blog.csdn.net/cutemypig/article/details/107505567
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。