图像的点或矩阵中的元素,是我们进行运算时的基本元素,所以遍历图像的操作是经常要用到的,本文的代码用四种方式实现图像的遍历。
我们通过元素遍历实现对图像降色彩处理,因为256*256*256种颜色实在太多了,在图像颜色聚类或绘制彩色直方图时,我们需要用一些代表性的颜色代替丰富的色彩空间,此时可以将每个通道的256种颜色用64种代替,即将原来256种颜色划分64个颜色段,每个颜色段取中间的颜色值作为代表色。
假设某点某个通道的值为value,则将原来256种颜色划分64个颜色段,每个颜色段取中间的颜色值作为代表色的数学运算为:
value/64*64+64/2,注意“/”是取整运算,不是除法运算哈!
接下来,我们介绍四种遍历方式的代码:
图像处理开发资料、图像处理开发需求、图像处理接私活挣零花钱,可以搜索公众号"qxsf321",并关注!
代码中用到的图像下载链接:http://pan.baidu.com/s/1kVLx5v5 密码:3ikr1 下标遍历法 at<typename>(i,j)
代码如下:
//opencv版本:OpenCV3.0
//VS版本:VS2013
//Author:qxsf321.net
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>
int colorReduce(cv::Mat& image, cv::Mat& outImage, int div)
{
outImage.create(image.size(), image.type());
for (int i = 0; i<image.rows; i++)
{
for (int j = 0; j<image.cols; j++)
{
outImage.at<cv::Vec3b>(i, j)[0] = image.at<cv::Vec3b>(i, j)[0] / div*div + div / 2;
outImage.at<cv::Vec3b>(i, j)[1] = image.at<cv::Vec3b>(i, j)[1] / div*div + div / 2;
outImage.at<cv::Vec3b>(i, j)[2] = image.at<cv::Vec3b>(i, j)[2] / div*div + div / 2;
}
}
return 0;
}
int main()
{
// 装载图像
cv::Mat srcImage = cv::imread("02.jpg");
cv::Mat dstImage;
if (!srcImage.data)
return -1;
cv::imshow("srcImage", srcImage);
colorReduce(srcImage, dstImage, 64);
cv::imshow("dstImage", dstImage);
cv::waitKey(0);
return 0;
}
关键代码说明:
image.at<cv::Vec3b>(i,j):取出灰度图像中i行j列的点。
image.at<Vec3b>(i,j)[k]:取出彩色图像中i行j列第k通道的颜色点,Vec3b都是图像像素值的类型。2 效率较低的指针遍历法
指针遍历法比第1种的下标遍历法要高效!
代码如下:
//opencv版本:OpenCV3.0
//VS版本:VS2013
//Author:qxsf321.net
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>
void colorReduce(const cv::Mat& image, cv::Mat& outImage, int div)
{
// 创建与原图像等尺寸的图像
outImage.create(image.size(), image.type());
int nr = image.rows;
// 将3通道转换为1通道
int nl = image.cols*image.channels();
for (int k = 0; k<nr; k++)
{
// 每一行图像的指针
const uchar* inData = image.ptr<uchar>(k);
uchar* outData = outImage.ptr<uchar>(k);
for (int i = 0; i<nl; i++)
{
outData[i] = inData[i] / div*div + div / 2;
}
}
}
int main()
{
// 装载图像
cv::Mat srcImage = cv::imread("02.jpg");
cv::Mat dstImage;
if (!srcImage.data)
return -1;
cv::imshow("srcImage", srcImage);
colorReduce(srcImage, dstImage, 64);
cv::imshow("dstImage", dstImage);
cv::waitKey(0);
return 0;
}
关键代码说明:
image.ptr<uchar>(i):取出图像中第i行数据的指针
程序中将三通道的数据转换为1通道,在建立在每一行数据元素之间在内存里是连续存储的,每个像素三通道像素按顺序存储。也就是一幅图像数据最开始的三个值,是最左上角的那像素的三个通道的值。但是这种用法不能直接用在行与行之间,因为图像在OpenCV里的存储机制问题,行与行之间可能有空白单元。当行存储也是连续的时候,有下面这种更高效的指针遍历方法。3 高效的指针遍历法
当一个矩阵或一幅图像是行连续时,可以用这种方法。具体的操作是先用函数isContinuous()。当图像连通时,我们就可以把图像完全展开,看成是一行。
代码如下:
//opencv版本:OpenCV3.0
//VS版本:VS2013
//Author:qxsf321.net
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>
void colorReduce(const cv::Mat& image, cv::Mat& outImage, int div)
{
int nr = image.rows;
int nc = image.cols;
outImage.create(image.size(), image.type());
if (image.isContinuous() && outImage.isContinuous())
{
nr = 1;
nc = nc*image.rows*image.channels();
}
for (int i = 0; i<nr; i++)
{
const uchar* inData = image.ptr<uchar>(i);
uchar* outData = outImage.ptr<uchar>(i);
for (int j = 0; j<nc; j++)
{
*outData++ = *inData++ / div*div + div / 2;
}
}
}
int main()
{
// 装载图像
cv::Mat srcImage = cv::imread("02.jpg");
cv::Mat dstImage;
if (!srcImage.data)
return -1;
cv::imshow("srcImage", srcImage);
colorReduce(srcImage, dstImage, 64);
cv::imshow("dstImage", dstImage);
cv::waitKey(0);
return 0;
}
4 用迭代器来遍历
声明一个迭代器的方法如下:
MatIterator_<Vec3b> it;
Mat_<Vec3b>::iterator it;
如果迭代器指向一个const图像,则可以用下面的声明:
MatConstIterator<Vec3b> it; 或者
Mat_<Vec3b>::const_iterator it;示例代码如下:
//opencv版本:OpenCV3.0
//VS版本:VS2013
//Author:qxsf321.net
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>
void colorReduce(const cv::Mat& image, cv::Mat& outImage, int div)
{
outImage.create(image.size(), image.type());
cv::MatConstIterator_<cv::Vec3b> it_in = image.begin<cv::Vec3b>();
cv::MatConstIterator_<cv::Vec3b> itend_in = image.end<cv::Vec3b>();
cv::MatIterator_<cv::Vec3b> it_out = outImage.begin<cv::Vec3b>();
cv::MatIterator_<cv::Vec3b> itend_out = outImage.end<cv::Vec3b>();
while (it_in != itend_in)
{
(*it_out)[0] = (*it_in)[0] / div*div + div / 2;
(*it_out)[1] = (*it_in)[1] / div*div + div / 2;
(*it_out)[2] = (*it_in)[2] / div*div + div / 2;
it_in++;
it_out++;
}
}
int main()
{
// 装载图像
cv::Mat srcImage = cv::imread("02.jpg");
cv::Mat dstImage;
if (!srcImage.data)
return -1;
cv::imshow("srcImage", srcImage);
colorReduce(srcImage, dstImage, 64);
cv::imshow("dstImage", dstImage);
cv::waitKey(0);
return 0;
}
代码说明:如果你想从第二行开始,则可以从image.begin<Vec3b>()+image.rows开始。
四种方法的运行结果都是一样的,运行结果截图如下:
在上面四种方法中,第三种方法即“高效的指针遍历法”程序运行效率是最高的!
图像处理开发资料、图像处理开发需求、图像处理接私活挣零花钱,可以搜索公众号"qxsf321",并关注!