彩色图像由三通道组成,用8位无符号整型表示时颜色总数为 256∗256∗256=16777216 256 ∗ 256 ∗ 256 = 16777216 种,非常巨大,有时为了降低分析的复杂性会做减少颜色数量的处理。
Method1—利用整数除法特性
一种处理方法是将RGB空间细分到大小相等的方块中。例如,如果把每种颜色数量减少到1/8,那么颜色总数就变为
32∗32∗32
32
∗
32
∗
32
。将原图像中每个颜色值划分到一个方块,该方块的中间值就是新的颜色值;新图像使用新的颜色值,颜色数就变少了。
具体地,假设N是减色因子,将图像中每个像素的值除以
N
N
(使用整数除法,不保留余数。实际上,该算法就是利用了整数除法的特性来实现减色的)。然后将结果乘以,得到
N
N
的倍数,再加上,就得到相邻的
N
N
的倍数之间的中间值。对所有8位通道值重复这个过程,就会得到种可能的颜色值。
实现方法如下,
减色函数:
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
/* 假设输入输出都是三通道图像,如果是灰度图处理方法类似 */
/* 首先检查输出图像宽、高、数据类型是否与输入一致,
如果一致则使用原始输出图像,否则创建新的输出图像*/
void colorReduce(const cv::Mat &image, cv::Mat &result, int div=64)
{
/* 在调用create时会自动检查要求创建的宽、高、数据类型是否与result匹配,
如果匹配则create不会创建新的Mat直接使用result,否则会创建新的Mat赋给result*/
result.create(cv::Size(image.cols, image.rows), image.type());
for (int i = 0; i != image.rows; ++i)
{
const cv::Vec3b * const ptr_image = image.ptr<cv::Vec3b>(i);
cv::Vec3b * ptr_result = result.ptr<cv::Vec3b>(i);
/* 利用整数除法特性进行减色 */
for (int j = 0; j != image.cols; ++j)
{
ptr_result[j] = ptr_image[j] / div*div + cv::Vec3b(div/2, div/2, div/2);
}
}
}
使用demo:
int main()
{
cv::Mat img = cv::imread("ALBT9701_.jpg", 1);
cv::resize(img, img, cv::Size(img.cols, img.rows));
cv::imshow("img", img);
/* colorReduce()中create()会创建新的Mat赋给result */
cv::Mat result;
#if 0
/* colorReduce()中create()不会创建新的Mat而是直接使用result */
cv::Mat result(img.rows, img.cols, img.type());
#endif
/* 调用减色函数 */
colorReduce(img, result,128);
cv::imshow("result", result);
cv::waitKey(0);
}
效果如图1所示:
Method2—取模运算
使用取模运算符实现减色算法。
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
/* 假设输入输出都是三通道图像,如果是灰度图处理方法类似 */
/* 首先检查输出图像宽、高、数据类型是否与输入一致,
如果一致则使用原始输出图像,否则创建新的输出图像*/
void colorReduce(const cv::Mat &image, cv::Mat &result, int div=64)
{
/* 在调用create时会自动检查要求创建的宽、高、数据类型是否与result匹配,
如果匹配则create不会创建新的Mat直接使用result,否则会创建新的Mat赋给result*/
result.create(cv::Size(image.cols, image.rows), image.type());
for (int i = 0; i != image.rows; ++i)
{
const cv::Vec3b * const ptr_image = image.ptr<cv::Vec3b>(i);
cv::Vec3b * ptr_result = result.ptr<cv::Vec3b>(i);
/* 利用取模运算符进行减色 */
for (int j = 0; j != image.cols; ++j)
{
ptr_result[j][0] = ptr_image[j][0] - ptr_image[j][0] % div + div / 2;
ptr_result[j][1] = ptr_image[j][1] - ptr_image[j][1] % div + div / 2;
ptr_result[j][2] = ptr_image[j][2] - ptr_image[j][2] % div + div / 2;
}
}
}
int main()
{
cv::Mat img = cv::imread("ALBT9701_.jpg", 1);
cv::resize(img, img, cv::Size(img.cols, img.rows));
cv::imshow("img", img);
/* colorReduce()中create()会创建新的Mat赋给result */
cv::Mat result;
#if 0
/* colorReduce()中create()不会创建新的Mat而是直接使用result */
cv::Mat result(img.rows, img.cols, img.type());
#endif
/* 调用减色函数 */
colorReduce(img, result,128);
cv::imshow("result", result);
cv::waitKey(0);
}
效果如图2所示:
Method3—位运算符实现减色算法
如果把减色因子限定为2的指数,即 div=pow(2,n) d i v = p o w ( 2 , n ) ,那么把像素值的前n位掩码后就能得到最接近的div的倍数,可以用简单的移位操作获得掩码。
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
/* 假设输入输出都是三通道图像,如果是灰度图处理方法类似 */
/* 首先检查输出图像宽、高、数据类型是否与输入一致,
如果一致则使用原始输出图像,否则创建新的输出图像*/
void colorReduce(const cv::Mat &image, cv::Mat &result, int n = 6)//div=pow(2,n)=128
{
cv::Vec3b mask(0xFF << n, 0xFF << n, 0xFF << n);
/* 在调用create时会自动检查要求创建的宽、高、数据类型是否与result匹配,
如果匹配则create不会创建新的Mat直接使用result,否则会创建新的Mat赋给result*/
result.create(cv::Size(image.cols, image.rows), image.type());
for (int i = 0; i != image.rows; ++i)
{
const cv::Vec3b * const ptr_image = image.ptr<cv::Vec3b>(i);
cv::Vec3b * ptr_result = result.ptr<cv::Vec3b>(i);
for (int j = 0; j != image.cols; ++j)
{
/* 利用位运算符实现减色 */
ptr_result[j][0] = ptr_image[j][0] & mask[0]; ptr_result[j][0] += pow(2,n)/2;
ptr_result[j][1] = ptr_image[j][1] & mask[1]; ptr_result[j][1] += pow(2,n)/2;
ptr_result[j][2] = ptr_image[j][2] & mask[2]; ptr_result[j][2] += pow(2,n)/2;
}
}
}
int main()
{
cv::Mat img = cv::imread("ALBT9701_.jpg", 1);
cv::resize(img, img, cv::Size(img.cols, img.rows));
cv::imshow("img", img);
/* colorReduce()中create()会创建新的Mat赋给result */
cv::Mat result;
#if 0
/* colorReduce()中create()不会创建新的Mat而是直接使用result */
cv::Mat result(img.rows, img.cols, img.type());
#endif
/* 调用减色函数 */
colorReduce(img, result,7);
cv::imshow("result", result);
cv::waitKey(0);
}
效果如图3所示:
参考文献
OpenCV Tutorials
Robert Laganiere著、相银初译. OpenCV 3 Computer Vision, Application Programming Cookbook, Third Edition[D]. 人民邮电出版社, 2018.