opencv中遍历每一个像素点进行处理

时间:2023-02-14 14:28:04

1.用动态地址操作像素:

	Mat srcImage(100, 100, CV_8UC3, Scalar(200,20,100));

imshow("显示图像", srcImage);


int rowNumber = srcImage.rows;
int colNumber = srcImage.cols;


for (int i = 0; i < rowNumber; i++)
{
for (int j = 0; j < colNumber; j++)
{

if (srcImage.at<Vec3b>(i, j)[0] > 180)
{
srcImage.at<Vec3b>(i, j)[0] = 0;
}

if (srcImage.at<Vec3b>(i, j)[1] < 50)
{
srcImage.at<Vec3b>(i, j)[1] = 255;
}

if (srcImage.at<Vec3b>(i, j)[2] < 120)
{
srcImage.at<Vec3b>(i, j)[2] = 0;
}

}
}


imshow("处理后的图像", srcImage);
cv::mat的成员函数: .at(int y, int x)可以用来存取图像中对应坐标为(x,y)的元素坐标。(Mat类中的cols和rows给出了图像的宽和高。而成员函数at(int x, int y)可以用来存取图像的元素。)由于at方法本身不会对任何数据类型进行转化,故一定要确保指定的数据类型和矩阵中的数据类型相符合。

假设提前已知一幅图像img的数据类型为 unsigned char型灰度图(单通道),对像素的赋值操作为image.at<uchar>(i,j) = value。而对于彩色图像,每个像素由三个部分构成:蓝色通道、绿色通道和红色通道(BGR),对于一个包含彩色图像的Mat,会返回一个由三个8位数组组成的量。OpenCV将此类型定义为Vec3b,即由三个unsigned char组成的向量。这也解释了为什么存取彩色图像像素的代码可以写成:image.at<Vec3b>(i,j)[channel] = value;


以下是统计canndy后的0像素点与255像素点之间的数量的比值:

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
Mat graySrc = imread("../../11.bmp", 0);

Mat canImage;
Canny(graySrc, canImage, 60, 120);

int PicZero = 0;
int PicFull = 0;

for (int i = 0; i < graySrc.rows; ++i)
{
for (int j = 0; j < graySrc.cols; ++j)
{
if (canImage.at<unsigned char>(i, j) == 0)
{
PicZero++;
}
else
{
PicFull++;
}
}

}

cout << "0像素点比255像素点的比值为" << (double)PicZero / PicFull << endl;

system("pause");
}





2.用指针的方法:

有时候我们需要遍历Mat中的每一个像素点,并且对像素点进行处理,这里以图像所有像素点都减去div(div属于int类型)

void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
// 参数准备
outputImage = inputImage.clone();

int rowNumber = outputImage.rows;
int colNumber = outputImage.cols*outputImage.channels();

for (int i = 0; i < rowNumber; i++)
{
// 获取第i行的首地址
uchar* data = outputImage.ptr<uchar>(i);

for (int j = 0; j < colNumber; j++) // 列循环
{
// 开始处理每一个像素值,每一个像素值都减去div
data[j] = data[j] - div;
}
}
}

也可以写成如下形式:

Mat inverseColor1(Mat srcImage) 
{
Mat tempImage = srcImage.clone();
int row = tempImage.rows;
int col = tempImage.cols * tempImage.channels();

for (int i = 0; i < row; ++i)
{

const unsigned char* sourcedata = srcImage.ptr(i);

unsigned char* data = tempImage.ptr(i);

for (int j = 0; j < col; j++)
{
data[j] = sourcedata[j] - div;
}
}
return tempImage;
}
此时是定义了两个指针类型: const unsigned char*和 unsigned char*,其中const unsigned char* 中的内容只能够被读取,不能被修改

特别需要注意的是:Mat中每一行元素的个数=列数*通道数


如需要打印M,

	Mat M(3, 2, CV_8UC3, Scalar(0, 0, 255));
cout << M << endl;
打印结果为:验证了每一行元素的个数为: 列数*通道数
opencv中遍历每一个像素点进行处理

另外需要注意的是:Mat 除了拥有成员变量cols,rows,成员函数channels()之外,还提供了ptr函数可以返回得到图像任意行的首地址。





3.用迭代器Matlterator_:

        Matlterator_是Mat数据操作的迭代器,:begin()表示指向Mat数据的起始迭代器,:end()表示指向Mat数据的终止迭代器。迭代器方法是一种更安全的用来遍历图像的方式,首先获取到数据图像的矩阵起始,再通过递增迭代实现移动数据指针。

Mat inverseColor4(Mat srcImage) 
{
Mat tempImage = srcImage.clone();

// 初始化原图像迭代器
MatConstIterator_<Vec3b> srcIterStart = srcImage.begin<Vec3b>();
MatConstIterator_<Vec3b> srcIterEnd = srcImage.end<Vec3b>();

// 初始化输出图像迭代器
MatIterator_<Vec3b> resIterStart = tempImage.begin<Vec3b>();
MatIterator_<Vec3b> resIterEnd = tempImage.end<Vec3b>();

while (srcIterStart != srcIterEnd)
{
(*resIterStart)[0] = 255 - (*srcIterStart)[0];
(*resIterStart)[1] = 255 - (*srcIterStart)[1];
(*resIterStart)[2] = 255 - (*srcIterStart)[2];

srcIterStart++;
resIterStart++;
}

return tempImage;

}