文档章节

OpenCV学习C++接口:图像遍历+像素压缩

moki_oschina
 moki_oschina
发布于 2016/07/08 17:42
字数 2013
阅读 165
收藏 2

学习体会:

  1. 当Mat为多通道时,如3通道,如果我们将其内容输出到终端,则可以看出其列数为Mat::colsn倍,当然nMat的通道数。虽是如此,但是Mat::cols的数值并没有随之改变。
  2. 当复制一副图像时,利用函数cv::Mat::clone(),则将在内存中重新开辟一段新的内存存放复制的图像(图像数据也将全部复制),而如果利用cv::Mat::copyTo()复制图像,则不会在内存中开辟一段新的内存块,同时也不会复制图像数据,复制前后的图像的指针指向同一个内存块。使用的时候需注意两个函数的区别。
  3. 为了避免函数参数传递时出现复制情况,函数的形参多采用传递reference,如cv::Mat &image,传递输入图像的引用,不过这样函数可能会对输入图像进行修改,并反映到输出结果上;如果想避免修改输入图像,则函数形参可传递const reference,这样输入图像不会被修改,同时可以创建一个输出图像Mat,将函数处理的结果保存到输出图像Mat中,例如:void colorReduce4(const cv::Mat &image, cv::Mat &result,int div = 64)。
  4. 采用迭代器iterator来遍历图像像素,可简化过程,比较安全,不过效率较低;如果想避免修改输入图像实例cv::Mat,可采用const_iterator
  5. 遍历图像时,不要采用.at()方式,这种效率最低。
  6. 进行图像像素压缩时,利用位操作的算法效率最高,其次是利用整数除法中向下取整,效率最低的是取模运算。
  7. 设计函数时,需要检查计算效率来提高程序的性能,不过以牺牲程序的可读性来提高代码执行的效率并不是一个明智的选择。
  8. 执行效率情况见程序运行结果。
/***************************************************************
*
*    内容摘要:本例采用8种方法对图像Mat的像素进行扫描,并对像素点的像
*            素进行压缩,压缩间隔为div=64,并比较扫描及压缩的效率,效
*            率最高的是采用.ptr及减少循环次数来遍历图像,并采用位操
*            作来对图像像素进行压缩。
*   作    者:Jacky Liu
*   完成日期:2012.8.10
*   参考资料:《OpenCV 2 computer Vision Application Programming
*              cookbook》
*
***************************************************************/
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>


//利用.ptr和数组下标进行图像像素遍历
void colorReduce0(cv::Mat &image, int div = 64)
{
    int nl = image.rows;
    int nc = image.cols * image.channels();
    
    //遍历图像的每个像素
    for(int j=0; j<nl ;++j)
    {
        uchar *data = image.ptr<uchar>(j);
        for(int i=0; i<nc; ++i)
        {
            data[i] = data[i]/div*div+div/2;     //减少图像中颜色总数的关键算法:if div = 64, then the total number of colors is 4x4x4;整数除法时,是向下取整。
        }
    }
}


//利用.ptr和 *++ 进行图像像素遍历
void colorReduce1(cv::Mat &image, int div = 64)
{
    int nl = image.rows;
    int nc = image.cols * image.channels();
    
    //遍历图像的每个像素
    for(int j=0; j<nl ;++j)
    {
        uchar *data = image.ptr<uchar>(j);
        for(int i=0; i<nc; ++i)
        {
            *data++ = *data/div*div + div/2;
        }
    }
}


//利用.ptr和数组下标进行图像像素遍历,取模运算用于减少图像颜色总数
void colorReduce2(cv::Mat &image, int div = 64)
{
    int nl = image.rows;
    int nc = image.cols * image.channels();
    
    //遍历图像的每个像素
    for(int j=0; j<nl ;++j)
    {
        uchar *data = image.ptr<uchar>(j);
        for(int i=0; i<nc; ++i)
        {
            data[i] = data[i]-data[i]%div +div/2;  //利用取模运算,速度变慢,因为要读每个像素两次
        }
    }
}

//利用.ptr和数组下标进行图像像素遍历,位操作运算用于减少图像颜色总数
void colorReduce3(cv::Mat &image, int div = 64)
{
    int nl = image.rows;
    int nc = image.cols * image.channels();

    int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));   //div=64, n=6
    uchar mask = 0xFF<<n;                                            //e.g. div=64, mask=0xC0
    
    //遍历图像的每个像素
    for(int j=0; j<nl ;++j)
    {
        uchar *data = image.ptr<uchar>(j);
        for(int i=0; i<nc; ++i)
        {
            *data++ = *data&mask + div/2;
        }
    }
}

//形参传入const conference,故输入图像不会被修改;利用.ptr和数组下标进行图像像素遍历
void colorReduce4(const cv::Mat &image, cv::Mat &result,int div = 64)
{
    int nl = image.rows;
    int nc = image.cols * image.channels();

    result.create(image.rows,image.cols,image.type());
    
    //遍历图像的每个像素
    for(int j=0; j<nl ;++j)
    {
        const uchar *data_in = image.ptr<uchar>(j);
        uchar *data_out = result.ptr<uchar>(j);
        for(int i=0; i<nc; ++i)
        {
            data_out[i] = data_in[i]/div*div+div/2;     //减少图像中颜色总数的关键算法:if div = 64, then the total number of colors is 4x4x4;整数除法时,是向下取整。
        }
    }
}

//利用.ptr和数组下标进行图像像素遍历,并将nc放入for循环中(比较糟糕的做法)
void colorReduce5(cv::Mat &image, int div = 64)
{
    int nl = image.rows;
    
    //遍历图像的每个像素
    for(int j=0; j<nl ;++j)
    {
        uchar *data = image.ptr<uchar>(j);
        for(int i=0; i<image.cols * image.channels(); ++i)
        {
            data[i] = data[i]/div*div+div/2;     //减少图像中颜色总数的关键算法:if div = 64, then the total number of colors is 4x4x4;整数除法时,是向下取整。
        }
    }
}

//利用迭代器 cv::Mat iterator 进行图像像素遍历
void colorReduce6(cv::Mat &image, int div = 64)
{
    cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();    //由于利用图像迭代器处理图像像素,因此返回类型必须在编译时知道
    cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();

    for(;it != itend; ++it)
    {
        (*it)[0] = (*it)[0]/div*div+div/2;        //利用operator[]处理每个通道的像素
        (*it)[1] = (*it)[1]/div*div+div/2;
        (*it)[2] = (*it)[2]/div*div+div/2;
    }
}

//利用.at<cv::Vec3b>(j,i)进行图像像素遍历
void colorReduce7(cv::Mat &image, int div = 64)
{
    int nl = image.rows;
    int nc = image.cols;
    
    //遍历图像的每个像素
    for(int j=0; j<nl ;++j)
    {
        for(int i=0; i<nc; ++i)
        {
            image.at<cv::Vec3b>(j,i)[0] = image.at<cv::Vec3b>(j,i)[0]/div*div + div/2;
            image.at<cv::Vec3b>(j,i)[1] = image.at<cv::Vec3b>(j,i)[1]/div*div + div/2;
            image.at<cv::Vec3b>(j,i)[2] = image.at<cv::Vec3b>(j,i)[2]/div*div + div/2;
        }
    }
}

//减少循环次数,进行图像像素遍历,调用函数较少,效率最高。
void colorReduce8(cv::Mat &image, int div = 64)
{
    int nl = image.rows;
    int nc = image.cols;

    //判断是否是连续图像,即是否有像素填充
    if(image.isContinuous())
    {
        nc = nc*nl;
        nl = 1;
    }

    int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));
    uchar mask = 0xFF<<n;
    
    //遍历图像的每个像素
    for(int j=0; j<nl ;++j)
    {
        uchar *data = image.ptr<uchar>(j);
        for(int i=0; i<nc; ++i)
        {
            *data++ = *data & mask +div/2;
            *data++ = *data & mask +div/2;
            *data++ = *data & mask +div/2;
        }
    }
}

const int NumTests = 9;        //测试算法的数量
const int NumIteration = 20;   //迭代次数

int main(int argc, char* argv[])
{
    int64 t[NumTests],tinit;
    cv::Mat image1;
    cv::Mat image2;
    
    //数组初始化
    int i=0;
    while(i<NumTests)
    {
        t[i++] = 0;
    }

    int n = NumIteration;
    
    //迭代n次,取平均数
    for(int i=0; i<n; ++i)
    {
        image1 = cv::imread("../boldt.jpg");

        if(!image1.data)
        {
            std::cout<<"read image failue!"<<std::endl;
            return -1;
        }

        // using .ptr and []
        tinit = cv::getTickCount();
        colorReduce0(image1);
        t[0] += cv::getTickCount() - tinit;
        
        // using .ptr and *++
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce1(image1);
        t[1] += cv::getTickCount()  - tinit;
        
        // using .ptr and [] and modulo
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce2(image1);
        t[2] += cv::getTickCount()  - tinit;
        
        // using .ptr and *++ and bitwise
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce3(image1);
        t[3] += cv::getTickCount()  - tinit;

        //using input and output image
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce4(image1,image2);
        t[4] += cv::getTickCount()  - tinit;
        
        // using .ptr and [] with image.cols * image.channels()
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce5(image1);
        t[5] += cv::getTickCount()  - tinit;
        
        // using .ptr and *++ and iterator
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce6(image1);
        t[6] += cv::getTickCount()  - tinit;
        
        //using at
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce7(image1);
        t[7] += cv::getTickCount()  - tinit;

        //using .ptr and * ++ and bitwise (continuous+channels)
        image1 = cv::imread("../boldt.jpg");
        tinit = cv::getTickCount();
        colorReduce8(image1);
        t[8] += cv::getTickCount()  - tinit;
    }

    cv::namedWindow("Result");
    cv::imshow("Result",image1);
    cv::namedWindow("Result Image");
    cv::imshow("Result Image",image2);

    std::cout<<std::endl<<"-------------------------------------------------------------------------"<<std::endl<<std::endl;
    std::cout<<"using .ptr and [] = "<<1000*t[0]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using .ptr and *++ = "<<1000*t[1]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using .ptr and [] and modulo = "<<1000*t[2]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using .ptr and *++ and bitwise = "<<1000*t[3]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using input and output image = "<<1000*t[4]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using .ptr and [] with image.cols * image.channels() = "<<1000*t[5]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using .ptr and *++ and iterator = "<<1000*t[6]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using at = "<<1000*t[7]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<"using .ptr and * ++ and bitwise (continuous+channels) = "<<1000*t[8]/cv::getTickFrequency()/n<<"ms"<<std::endl;
    std::cout<<std::endl<<"-------------------------------------------------------------------------"<<std::endl<<std::endl;
    cv::waitKey();
    return 0;
}

 

本文转载自:http://www.cnblogs.com/liu-jun/archive/2012/08/12/JackyLiu.html

moki_oschina
粉丝 26
博文 202
码字总数 44864
作品 0
成都
程序员
私信 提问
OpenCV(C++)操作像素总结

OpenCV中提供了许多操作图像的函数,但是有时候我们需要直接操作像素来实现我们的功能,这篇文章总结了OpenCV中常见的操作像素的方法。 像素类型 不同的图像有不同的像素类型,不过对于不同的...

有苦向瓜诉说
2018/12/07
0
0
[Python图像处理] 一.图像处理基础知识及OpenCV入门函数

版权声明:本文为博主原创文章,转载请注明CSDN博客源地址!共同学习,一起进步~ https://blog.csdn.net/Eastmount/article/details/81748802 该系列文章是讲解Python OpenCV图像处理知识,前...

Eastmount
2018/08/16
0
0
图像的膨胀与腐蚀——OpenCV与C++的具体实现

目录 1. 膨胀与腐蚀的原理 2. 膨胀的具体实现 3. 腐蚀的具体实现 1. 膨胀与腐蚀的原理 膨胀与腐蚀是数学形态学在图像处理中最基础的操作。在笔者之前的文章《图像的卷积(滤波)运算(一)——图...

charlee44
03/31
0
0
[Python图像处理] 五.图像融合、加法运算及图像类型转换

版权声明:本文为博主原创文章,转载请注明CSDN博客源地址!共同学习,一起进步~ https://blog.csdn.net/Eastmount/article/details/82347501 该系列文章是讲解Python OpenCV图像处理知识,前...

Eastmount
2018/09/03
0
0
[Python图像处理] 三.获取图像属性、兴趣ROI区域及通道处理

版权声明:本文为博主原创文章,转载请注明CSDN博客源地址!共同学习,一起进步~ https://blog.csdn.net/Eastmount/article/details/82177300 该系列文章是讲解Python OpenCV图像处理知识,前...

Eastmount
2018/08/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

docker中部署的应用,获取含有中文字符的验证码图片时无法正常显示

使用docker过程中遇过的最诡异的问题,服务在本地环境中,通过在IDEA里面运行,或者使用java -jar ***.war运行,获取验证码图片都没有问题,但是运行在docker中,图片正常返回,但是上面的汉...

莫在全
1分钟前
0
0
postgres+socket.io+nodejs实时地图应用实践

nodejs一直以异步io著称,其语言特性尤其擅长于在realtime应用中,如聊天室等。在进行实时应用开发时,必不可少的需要用到 socket.io库,可以说,nodejs+socket.io在实时应用中具有较好的表现...

dragon_tech
7分钟前
1
0
Java开发面试题汇总

目前流行的开发技术、常见的面试问题以及问题的答案都已经写的特别清楚了,今天我在之前的基础上,再基于个人的经验继续精选一些面试题给大家阅读参考。 1,Java的反射 Java 反射机制是在运行...

花漾年华
11分钟前
0
0
聊聊flink jdbc的ParameterValuesProvider

序 本文主要研究一下flink jdbc的ParameterValuesProvider ParameterValuesProvider flink-jdbc_2.11-1.8.0-sources.jar!/org/apache/flink/api/java/io/jdbc/split/ParameterValuesProvide......

go4it
12分钟前
0
0
UserInputControls用户输入控制

enum UserInputControls { kGovernedByOrthoMode = 0x0001,//正交模式管理 kNullResponseAccepted = 0x0002,//允许输入空 kDontEchoCancelForCtrlC = 0x0004,//ctrl C 模式不能重复......

一个小妞
33分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部