美文网首页
「OpenCV」颜色空间缩减

「OpenCV」颜色空间缩减

作者: 叨码 | 来源:发表于2020-05-21 18:05 被阅读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是为了取像素范围值的中间,使像素各值偏差不会太大,未改变像素个数,实现压缩颜色空间

    相关文章

      网友评论

          本文标题:「OpenCV」颜色空间缩减

          本文链接:https://www.haomeiwen.com/subject/pdstahtx.html