1.用动态地址操作像素:
Mat srcImage(100, 100, CV_8UC3, Scalar(200,20,100));cv::mat的成员函数: .at(int y, int x)可以用来存取图像中对应坐标为(x,y)的元素坐标。(Mat类中的cols和rows给出了图像的宽和高。而成员函数at(int x, int y)可以用来存取图像的元素。)由于at方法本身不会对任何数据类型进行转化,故一定要确保指定的数据类型和矩阵中的数据类型相符合。
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);
假设提前已知一幅图像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)此时是定义了两个指针类型: const unsigned char*和 unsigned char*,其中const unsigned char* 中的内容只能够被读取,不能被修改。
{
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;
}
特别需要注意的是:Mat中每一行元素的个数=列数*通道数
如需要打印M,
Mat M(3, 2, CV_8UC3, Scalar(0, 0, 255));打印结果为:验证了每一行元素的个数为: 列数*通道数
cout << M << endl;
另外需要注意的是: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;
}