转自:http://blog.csdn.net/xiaowei_cqu/article/details/19839019

在《OpenCV 2 Computer Vision Application Programming Cookbook》看到的例子,非常不错,算是对之前的文章<访问Mat图像中每个像素的值>的回顾和补充。

Color Reduce

还是使用经典的Reduce Color的例子,即对图像中的像素表达进行量化。如常见的RGB24图像有256×256×256中颜色,通过Reduce Color将每个通道的像素减少8倍至256/8=32种,则图像只有32×32×32种颜色。假设量化减少的倍数是N,则代码实现时就是简单的value/N*N,通常我们会再加上N/2以得到相邻的N的倍数的中间值,最后图像被量化为(256/N)×(256/N)×(256/N)种颜色。

方法零:.ptr和[]操作符

Mat最直接的访问方法是通过.ptr<>函数得到一行的指针,并用[]操作符访问某一列的像素值。

  1. // using .ptr and []
  2. void colorReduce0(cv::Mat &image, int div=64) {
  3. int nr= image.rows; // number of rows
  4. int nc= image.cols * image.channels(); // total number of elements per line
  5. for (int j=0; j<nr; j++) {
  6. uchar* data= image.ptr<uchar>(j);
  7. for (int i=0; i<nc; i++) {
  8. data[i]= data[i]/div*div + div/2;
  9. }
  10. }
  11. }

方法一:.ptr和指针操作

除了[]操作符,我们可以移动指针*++的组合方法访问某一行中所有像素的值。

  1. // using .ptr and * ++
  2. void colorReduce1(cv::Mat &image, int div=64) {
  3. int nr= image.rows; // number of rows
  4. int nc= image.cols * image.channels(); // total number of elements per line
  5. for (int j=0; j<nr; j++) {
  6. uchar* data= image.ptr<uchar>(j);
  7. for (int i=0; i<nc; i++) {
  8. *data++= *data/div*div + div/2;
  9. } // end of row
  10. }
  11. }

方法二:.ptr、指针操作和取模运算

方法二和方法一的访问方式相同,不同的是color reduce用模运算代替整数除法

  1. // using .ptr and * ++ and modulo
  2. void colorReduce2(cv::Mat &image, int div=64) {
  3. int nr= image.rows; // number of rows
  4. int nc= image.cols * image.channels(); // total number of elements per line
  5. for (int j=0; j<nr; j++) {
  6. uchar* data= image.ptr<uchar>(j);
  7. for (int i=0; i<nc; i++) {
  8. int v= *data;
  9. *data++= v - v%div + div/2;
  10. } // end of row
  11. }
  12. }

方法三:.ptr、指针运算和位运算

由于进行量化的单元div通常是2的整次方,因此所有的乘法和除法都可以用位运算表示。

  1. // using .ptr and * ++ and bitwise
  2. void colorReduce3(cv::Mat &image, int div=64) {
  3. int nr= image.rows; // number of rows
  4. int nc= image.cols * image.channels(); // total number of elements per line
  5. int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
  6. // mask used to round the pixel value
  7. uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
  8. for (int j=0; j<nr; j++) {
  9. uchar* data= image.ptr<uchar>(j);
  10. for (int i=0; i<nc; i++) {
  11. *data++= *data&mask + div/2;
  12. } // end of row
  13. }
  14. }

方法四:指针运算

方法四和方法三量化处理的方法相同,不同的是用指针运算代替*++操作。

  1. // direct pointer arithmetic
  2. void colorReduce4(cv::Mat &image, int div=64) {
  3. int nr= image.rows; // number of rows
  4. int nc= image.cols * image.channels(); // total number of elements per line
  5. int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
  6. int step= image.step; // effective width
  7. // mask used to round the pixel value
  8. uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
  9. // get the pointer to the image buffer
  10. uchar *data= image.data;
  11. for (int j=0; j<nr; j++) {
  12. for (int i=0; i<nc; i++) {
  13. *(data+i)= *data&mask + div/2;
  14. } // end of row
  15. data+= step;  // next line
  16. }
  17. }

方法五:.ptr、*++、位运算以及image.cols * image.channels()

这种方法就是没有计算nc,基本是个充数的方法。

  1. // using .ptr and * ++ and bitwise with image.cols * image.channels()
  2. void colorReduce5(cv::Mat &image, int div=64) {
  3. int nr= image.rows; // number of rows
  4. int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
  5. // mask used to round the pixel value
  6. uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
  7. for (int j=0; j<nr; j++) {
  8. uchar* data= image.ptr<uchar>(j);
  9. for (int i=0; i<image.cols * image.channels(); i++) {
  10. *data++= *data&mask + div/2;
  11. } // end of row
  12. }
  13. }

方法六:连续图像

Mat提供了isContinuous()函数用来查看Mat在内存中是不是连续存储,如果是则图片被存储在一行中。

  1. // using .ptr and * ++ and bitwise (continuous)
  2. void colorReduce6(cv::Mat &image, int div=64) {
  3. int nr= image.rows; // number of rows
  4. int nc= image.cols * image.channels(); // total number of elements per line
  5. if (image.isContinuous())  {
  6. // then no padded pixels
  7. nc= nc*nr;
  8. nr= 1;  // it is now a 1D array
  9. }
  10. int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
  11. // mask used to round the pixel value
  12. uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
  13. for (int j=0; j<nr; j++) {
  14. uchar* data= image.ptr<uchar>(j);
  15. for (int i=0; i<nc; i++) {
  16. *data++= *data&mask + div/2;
  17. } // end of row
  18. }
  19. }

方法七:continuous+channels

与方法六基本相同,也是充数的。

  1. // using .ptr and * ++ and bitwise (continuous+channels)
  2. void colorReduce7(cv::Mat &image, int div=64) {
  3. int nr= image.rows; // number of rows
  4. int nc= image.cols ; // number of columns
  5. if (image.isContinuous())  {
  6. // then no padded pixels
  7. nc= nc*nr;
  8. nr= 1;  // it is now a 1D array
  9. }
  10. int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
  11. // mask used to round the pixel value
  12. uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
  13. for (int j=0; j<nr; j++) {
  14. uchar* data= image.ptr<uchar>(j);
  15. for (int i=0; i<nc; i++) {
  16. *data++= *data&mask + div/2;
  17. *data++= *data&mask + div/2;
  18. *data++= *data&mask + div/2;
  19. } // end of row
  20. }
  21. }

方法八:Mat _iterator

真正有区别的方法来啦,用Mat提供的迭代器代替前面的[]操作符或指针,血统纯正的官方方法~

  1. // using Mat_ iterator
  2. void colorReduce8(cv::Mat &image, int div=64) {
  3. // get iterators
  4. cv::Mat_<cv::Vec3b>::iterator it= image.begin<cv::Vec3b>();
  5. cv::Mat_<cv::Vec3b>::iterator itend= image.end<cv::Vec3b>();
  6. for ( ; it!= itend; ++it) {
  7. (*it)[0]= (*it)[0]/div*div + div/2;
  8. (*it)[1]= (*it)[1]/div*div + div/2;
  9. (*it)[2]= (*it)[2]/div*div + div/2;
  10. }
  11. }

方法九:Mat_ iterator 和位运算

把方法八中的乘除法换成位运算。

  1. // using Mat_ iterator and bitwise
  2. void colorReduce9(cv::Mat &image, int div=64) {
  3. // div must be a power of 2
  4. int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
  5. // mask used to round the pixel value
  6. uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
  7. // get iterators
  8. cv::Mat_<cv::Vec3b>::iterator it= image.begin<cv::Vec3b>();
  9. cv::Mat_<cv::Vec3b>::iterator itend= image.end<cv::Vec3b>();
  10. for ( ; it!= itend; ++it) {
  11. (*it)[0]= (*it)[0]&mask + div/2;
  12. (*it)[1]= (*it)[1]&mask + div/2;
  13. (*it)[2]= (*it)[2]&mask + div/2;
  14. }
  15. }

方法十:MatIterator_

和方法八基本相同。

  1. // using MatIterator_
  2. void colorReduce10(cv::Mat &image, int div=64) {
  3. cv::Mat_<cv::Vec3b> cimage= image;
  4. cv::Mat_<cv::Vec3b>::iterator it=cimage.begin();
  5. cv::Mat_<cv::Vec3b>::iterator itend=cimage.end();
  6. for ( ; it!= itend; it++) {
  7. (*it)[0]= (*it)[0]/div*div + div/2;
  8. (*it)[1]= (*it)[1]/div*div + div/2;
  9. (*it)[2]= (*it)[2]/div*div + div/2;
  10. }
  11. }

方法十一:图像坐标

  1. // using (j,i)
  2. void colorReduce11(cv::Mat &image, int div=64) {
  3. int nr= image.rows; // number of rows
  4. int nc= image.cols; // number of columns
  5. for (int j=0; j<nr; j++) {
  6. for (int i=0; i<nc; i++) {
  7. image.at<cv::Vec3b>(j,i)[0]=     image.at<cv::Vec3b>(j,i)[0]/div*div + div/2;
  8. image.at<cv::Vec3b>(j,i)[1]=     image.at<cv::Vec3b>(j,i)[1]/div*div + div/2;
  9. image.at<cv::Vec3b>(j,i)[2]=     image.at<cv::Vec3b>(j,i)[2]/div*div + div/2;
  10. } // end of row
  11. }
  12. }

方法十二:创建输出图像

之前的方法都是直接修改原图,方法十二新建了输出图像,主要用于后面的时间对比。

  1. // with input/ouput images
  2. void colorReduce12(const cv::Mat &image, // input image
  3. cv::Mat &result,      // output image
  4. int div=64) {
  5. int nr= image.rows; // number of rows
  6. int nc= image.cols ; // number of columns
  7. // allocate output image if necessary
  8. result.create(image.rows,image.cols,image.type());
  9. // created images have no padded pixels
  10. nc= nc*nr;
  11. nr= 1;  // it is now a 1D array
  12. int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
  13. // mask used to round the pixel value
  14. uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
  15. for (int j=0; j<nr; j++) {
  16. uchar* data= result.ptr<uchar>(j);
  17. const uchar* idata= image.ptr<uchar>(j);
  18. for (int i=0; i<nc; i++) {
  19. *data++= (*idata++)&mask + div/2;
  20. *data++= (*idata++)&mask + div/2;
  21. *data++= (*idata++)&mask + div/2;
  22. } // end of row
  23. }
  24. }

方法十三:重载操作符

Mat重载了+&等操作符,可以直接将两个Scalar(B,G,R)数据进行位运算和数学运算。

  1. // using overloaded operators
  2. void colorReduce13(cv::Mat &image, int div=64) {
  3. int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
  4. // mask used to round the pixel value
  5. uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
  6. // perform color reduction
  7. image=(image&cv::Scalar(mask,mask,mask))+cv::Scalar(div/2,div/2,div/2);
  8. }

时间对比

通过迭代二十次取平均时间,得到每种方法是运算时间如下。

可以看到,指针*++访问和位运算是最快的方法;而不断的计算image.cols*image.channles()花费了大量重复的时间;另外迭代器访问虽然安全,但性能远低于指针运算;通过图像坐标(j,i)访问时最慢的,使用重载操作符直接运算效率最高。

(转)OpenCV 访问Mat中每个像素的值的更多相关文章

  1. opencv-访问Mat中每个像素的值

    参考:[OpenCV]访问Mat中每个像素的值(新)   膜拜大佬 以下例子代码均针对8位单通道灰度图. 1 .ptr和[]操作符 Mat最直接的访问方法是通过.ptr<>函数得到一行的指 ...

  2. 【OpenCV】访问图像中每个像素的值

    http://blog.csdn.net/xiaowei_cqu/article/details/7557063

  3. OpenCV(2)-Mat数据结构及访问Mat中像素

    Mat数据结构 一开始OpenCV是基于C语言的,在比较早的教材例如<学习OpenCV>中,讲解的存储图像的数据结构还是IplImage,这样需要手动管理内存.现在存储图像的基本数据结构是 ...

  4. 【opencv学习笔记七】访问图像中的像素与图像亮度对比度调整

    今天我们来看一下如何访问图像的像素,以及如何改变图像的亮度与对比度. 在之前我们先来看一下图像矩阵数据的排列方式.我们以一个简单的矩阵来说明: 对单通道图像排列如下: 对于双通道图像排列如下: 那么对 ...

  5. 【opencv基础】OpenCV从Mat中提取某些行或列

    这两个函数返回的是指向原矩阵内部位置的指针,类似于浅拷贝: code cv::Mat align_mean(cv::Mat mean, cv::Rect facebox, float scaling_ ...

  6. 访问图像中的像素[OpenCV 笔记16]

    再更一发好久没更过的OpenCV,不过其实写到这个部分对计算机视觉算法有所了解的应该可以做到用什么查什么了,所以后面可能会更的慢一点吧,既然开了新坑,还是机器学习更有研究价值吧... 图像在内存中的存 ...

  7. nginx访问日志中添加接口返回值

    因为nginx作为web服务器时,会代理后端的一些接口,这时访问日志中只能记录访问接口的status码,也就是说,只能获得200.404 这些的值 那么如何获得接口返回的response值呢? 下面开 ...

  8. 【OpenCV】访问Mat中的每个像素值

    其中注意i,j的位置,写错了好几次.第二个for循环中的if语句用异或来判断图像中哪些像素点发生了改变.

  9. opencv 3 core组件进阶(1 访问图像中的像素)

    访问图像像素的三类方法 ·方法一 指针访问:C操作符[ ]; ·方法二 迭代器iterator; ·方法三 动态地址计算. #include <opencv2/core/core.hpp> ...

随机推荐

  1. mysql数据库----下载安装、操作

    一.mysql概述 1.什么是数据库 ? 答:数据的仓库,如:在ATM的示例中我们创建了一个 db 目录,称其为数据库 2.什么是 MySQL.Oracle.SQLite.Access.MS SQL ...

  2. Myeclipse下配置SVN报错问题 svn: E175002: java.lang.RuntimeException: Could not generate DH keypair,缺少subclipse插件的javaHL

    在myeclipse10.0下安装svn插件,出现了Could not generate DH keypair,这么一个错误. 看到了一篇博客说是svn接口选择问题,可是我myeclipse没有那个接 ...

  3. 洛谷P1637 三元上升子序列

    P1637 三元上升子序列 48通过 225提交 题目提供者该用户不存在 标签云端 难度提高+/省选- 时空限制1s / 128MB 提交  讨论  题解 最新讨论更多讨论 为什么超时啊 a的数据比较 ...

  4. [JLOI2014] 松鼠的新家 (lca/树上差分)

    [JLOI2014]松鼠的新家 题目描述 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在 ...

  5. kotlin Hello World 以及关键字

    hello world:(支持函数式编程,不需要放到 class 中) fun main(args: Array<String>) { println("Hello World& ...

  6. Linux中怎么终止正在运行的后台程序

    linux 任务管理-后台运行与终止fg.bg.jobs.&.ctrl + z命令一. &加在一个命令的最后,可以把这个命令放到后台执行 ,如gftp &,二.ctrl + z ...

  7. 【题解】征途 SDOI 2016 BZOJ 4518

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4518 首先推式子,我们用$x_i$表示第$i$段的路程,$sum$表示总路程,根据方差和平均 ...

  8. UVA-11582 数学

    UVA-11582 题意: 求f[a^b]%n ,其中f是斐波那契数列,1<=n<=1000,0<=a,b<=2^64; 代码: //这题重点是要发现 f[i]%n会出现循环, ...

  9. Asp.Net MVC 自定义登录过滤器

    1.新建类BaseController用于统一所有控制器继承扩展,方便扩展登录等过滤器.示例如下: using CloudWave.JustBeHere.JBH_H5.Controllers.Attr ...

  10. Google guava 中的Monitor

    synchronized 自从Java提供了多线程编程,我们经常需要处理这样的情况:在特定的时间,我们需要限制访问,确保只有一个线程访问我们的代码.Java提供了同步关键字synchronized来实 ...