「OpenCV」颜色空间缩减
2020-05-21 本文已影响0人
叨码
文中内容大多来自大神浅墨_毛星云 所著【OpenCV3编程入门】,在此基础上加入了自己的理解和补充内容。
我们知道,若矩阵元素存储的单通道像素,使用c或c++的无符号字符类型,那么像素可有256个不同值,但如果是三通道图像,这种存储格式的颜色数就达到了1千六百多万种(256256256),如此之多的颜色进行处理,对算法性能的影响就比较大了。怎么优化,此刻颜色空间缩减就派上用场了,它在很多应用中可以大大降低运算复杂度。
颜色空间缩减的做法是:将现有颜色空间值除以某个值,以获得较少的颜色数。
计算公式:
【其中为缩减后的值,为缩减前的原始值, div 就是缩减的倍数 ,除法操作 自动截余操作】
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
void colorReduceByDoubleLoop(Mat &inputImage, Mat &outputImage, int div);
void colorReduceByIterator(Mat &inputImage, Mat &outputImage, int div);
void colorReduceByDynamicalcul(Mat &inputImage, Mat &outputImage, int div);
int main() {
std::cout << "Hello, World!" << std::endl;
Mat srcImage = imread("/Users/*/Pictures/img/Frame.jpg");
imshow("原始图像", srcImage);
Mat dstImage;
dstImage.create(srcImage.rows, srcImage.cols, srcImage.type());//效果图的大小,类型与原图片相同
//记录起始时间
//getTickCount()函数返回cpu某个事件以来走过的时钟周期数
double time0 = static_cast<double >(getTickCount());
//调用颜色空间缩减函数
colorReduceByDoubleLoop(srcImage, dstImage, 32);
//操作耗时多少秒 -- getTickFrequency()返回cpu一秒钟所走的时钟周期数
time0 = (double) (getTickCount() - time0) / getTickFrequency();
cout << "ByDoubleLoop此方法运行时间为:" << time0 << "秒" << endl;
Mat srcImage1 = imread("/Users/*/Pictures/img/Frame.jpg");
Mat dstImage1;
double time1 = static_cast<double>(getTickCount());
colorReduceByIterator(srcImage1, dstImage1, 32);
time1 = ((double) getTickCount() - time1) / getTickFrequency();
cout << "ByIterator此方法运行时间为:" << time1 << "秒" << endl;
Mat srcImage2 = imread("/Users/*/Pictures/img/Frame.jpg");
Mat dstImage2;
double time2 = static_cast<double>(getTickCount());
colorReduceByDynamicalcul(srcImage2, dstImage2, 32);
time2 = ((double) getTickCount() - time2) / getTickFrequency();
cout << "ByDynamicalcul此方法运行时间为:" << time2 << "秒" << endl;
return 0;
}
/**
* 双重循环 指针访问像素 ,进行颜色空间缩减操作
* @param inputImage
* @param outputImage
* @param div 缩减倍数
*/
void colorReduceByDoubleLoop(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);//获取i行的首地址
for (int j = 0; j < colNumber; j++) {
//处理每个像素
//data[i]/div*div 是用于将颜色缩减为原来的div倍,而加上div/2是为了取像素范围值的中间,使像素各值偏差不会太大,未改变像素个数,实现压缩颜色空间
data[j] = data[j] / div * div + div / 2;
//等同于
// *data++ = *data / div * div + div / 2;
}
}
}
/**
* 迭代器方式 对彩色图像进行颜色空间缩减操作
* @param inputImage
* @param outputImage
* @param div 缩减倍数
*/
void colorReduceByIterator(Mat &inputImage, Mat &outputImage, int div) {
//参数准备
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;
}
}
/**
* 动态地址运算配合at 对彩色图片进行颜色空间缩减操作
* @param inputImage
* @param outputImage
* @param div
*/
void colorReduceByDynamicalcul(Mat &inputImage, Mat &outputImage, int div) {
//参数准备
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;//绿色通道
}
}
}
运行结果:
/Users/*/Documents/learn/opencv/colorReduce/cmake-build-debug/colorReduce
Hello, World!
ByDoubleLoop此方法运行时间为:0.00556575秒
ByIterator此方法运行时间为:0.0170568秒
ByDynamicalcul此方法运行时间为:0.0129035秒
进程已结束,退出代码 0
效率一目了然,第一种方式最快
理解难点:
data[j] = data[j] / div * div + div / 2;
这里为什么还要+div/2呢,按我的理解是:
data[i]/div*div 是用于将颜色缩减为原来的div倍,而加上div/2是为了取像素范围值的中间,使像素各值偏差不会太大,未改变像素个数,实现压缩颜色空间