用一个简单的任务举例如何遍历图像:减少一幅图像的颜色数.
彩色图像是由三个通道的像素组成的.每个通道的亮度值分别对应三原色(红绿蓝).因为这些值是8位unsigned char类型的,总共的颜色数为256×256×256,总共超过了一千六百万种颜色.因此,为了减少分析图像的复杂性,有时减少图像的颜色数是有用的.一个简单的方法是把RGB颜色空间再分成相等大小的空间.例如,如果图像颜色减少为每8个像素取一个像素值,你最终只会得到32×32×32种颜色.原始图像中的每一种颜色被减少后的图像新的颜色值代替,这个新的颜色值是,它原来属于小的颜色空间的中间值.
因此,这个基本的颜色缩减算法是很简单的.如果N是缩减因子,然后对图像中的每个像素,然后这个像素的每个通道的值除以N(整除,因此需要注意有数据丢失).然后把这个结果乘以N,最后的结果会略小于输入的像素值.然后再加加上N/2,会得到在两个与N相乘相邻区间的中间值.对于每个8位通道重复这个处理,你会得到总共256/N×256/N×256/N可能的颜色值.
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <iostream>
void colorReduce(cv::Mat& image, int div = 64)
{
int nr = image.rows; //行数
int nc = image.cols * image.channels(); //每行元素的个数
int n = static_cast<int>(cv::log(static_cast<double>(div)) / cv::log(2.0));
uchar mask = 0xFF << n; //用来对像素取整的掩膜
for (int i = 0; i<nr; i++)
{
uchar* data = image.ptr<uchar>(i);//得到对i行的首地址
for (int j = 0; j<nc; j++) //处理每一个像素
{
//*data++ = *data - *data % div + div / 2;
*data++ = (*data & mask) + div / 2;
}
}
}
int main(int argc, char** argv)
{
cv::Mat img = cv::imread("8.jpg");
cv::Mat img2 = img.clone();
double duration;
duration = static_cast<double>(cv::getTickCount());
colorReduce(img2);
duration = static_cast<double>(cv::getTickCount()) - duration;
duration /= cv::getTickFrequency();
cv::namedWindow("original");
cv::imshow("original", img);
cv::namedWindow("altered");
cv::imshow("altered", img2);
std::cout << duration << std::endl;
cv::waitKey(0);
return 0;
}
原始图像:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2Lc1TPB5EeFpmTwMGVPpHOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TN5ITNzcjMwETOxcDM4EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
运行结果如下: