该文章参考毛星云著《OpenCV3编程入门》,电子工业出版社。
图像是以像素为单位储存的,访问图像中的每个像素是处理图像的基础,一般有三种访问图像像素的方法:
方法一:指针访问:C操作符[ ]
方法二:迭代器iterator
方法三:动态地址计算
下面直接上代码,代码的目的是减少图像的颜色种类数,即减少颜色的细分。
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
void colorreduce(Mat& inputImage, Mat& outputImage, int div);
int main()
{
Mat srcimage = imread("1.jpg");
imshow("srcimage", srcimage);
Mat dstimage;
dstimage.create(srcimage.rows, srcimage.cols, srcimage.type());
double time0 = static_cast<double>(getTickCount());
colorreduce(srcimage, dstimage, 32);
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "Time = " << time0 << "seconds" << endl;
imshow("dstimage", dstimage);
waitKey(0);
}
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++)
{
uchar* data = outputImage.ptr<uchar>(i); //获取行地址
for (int j = 0; j < colNumber; j++)
{
data[j] = data[j] / div*div + div / 2;
}
//*data++ = *data / div*div + div / 2; //所有方法中最快的
}
/*
//方法二:迭代器
outputImage = inputImage.clone();
Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>();
Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>();
for (; it != itend; ++it)
{
(*it)[0] = (*it)[0] / div*div + div / 2;
(*it)[1] = (*it)[1] / div*div + div / 2;
(*it)[2] = (*it)[2] / div*div + div / 2;
}
*/
/*
//方法三:动态地址计算
outputImage = inputImage.clone();
int rowNumber = outputImage.rows;
int colNumber = outputImage.cols;
for (int i = 0; i < rowNumber; i++)
{
for (int j = 0; j < colNumber; j++)
{
outputImage.at<Vec3b>(i, j)[0] = outputImage.at<Vec3b>(i, j)[0] / div*div + div / 2;
outputImage.at<Vec3b>(i, j)[1] = outputImage.at<Vec3b>(i, j)[1] / div*div + div / 2;
outputImage.at<Vec3b>(i, j)[2] = outputImage.at<Vec3b>(i, j)[2] / div*div + div / 2;
}
}
*/
}
程序运行的效果如下:
从结果上看,减小颜色种类后,图像变得更模糊了。
在程序中,需要注意的是colorreduce函数的形参是Mat&,而不是Mat,这是C++的知识,因为需要传入函数的图片在主函数中也发生变化,即函数外部的数据随函数内部的操作而变化。