「OpenCV」颜色空间缩减

2020-05-21  本文已影响0人  叨码

文中内容大多来自大神浅墨_毛星云 所著【OpenCV3编程入门】,在此基础上加入了自己的理解和补充内容。

我们知道,若矩阵元素存储的单通道像素,使用c或c++的无符号字符类型,那么像素可有256个不同值,但如果是三通道图像,这种存储格式的颜色数就达到了1千六百多万种(256256256),如此之多的颜色进行处理,对算法性能的影响就比较大了。怎么优化,此刻颜色空间缩减就派上用场了,它在很多应用中可以大大降低运算复杂度。
颜色空间缩减的做法是:将现有颜色空间值除以某个值,以获得较少的颜色数。

计算公式:
I_n = \left(\frac{I_o}{div}\right)* div
【其中I_n为缩减后的值,I_o为缩减前的原始值, 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是为了取像素范围值的中间,使像素各值偏差不会太大,未改变像素个数,实现压缩颜色空间

上一篇下一篇

猜你喜欢

热点阅读