学习OpenCV范例(二)——OpenCV如何扫描图像、利用查找表和计时

本文介绍使用OpenCV进行图像处理的方法,包括颜色空间缩减、图像扫描的不同方式及其性能对比,并展示了如何利用查找表提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于如何扫描图像的方法实在是太多了,在浏览一些资料的时候也找到了一些好的方法,到最后把这些方法的链接都贴出来,大家有兴趣的可以参考一下,看看哪种方法适合自己,在这里我还是根据OpenCV提供的范例进行分析。

1、建立查找表

颜色缩减方法:如果矩阵元素存储的是单通道像素,使用C或C++的无符号字符类型,那么像素可有256个不同值。但若是三通道图像,这种存储格式的颜色数就太多了(确切地说,有一千六百多万种)。用如此之多的颜色可能会对我们的算法性能造成严重影响。其实有时候,仅用这些颜色的一小部分,就足以达到同样效果。所以其做法是:将现有颜色空间值除以某个输入值,以获得较少的颜色数。例如,颜色值0到9可取为新值0,10到19可取为10,以此类推。其公式为:

                                          I_{new} = (\frac{I_{old}}{10}) * 10

即输入的颜色值为:0-9    输出为:0

                 10-19           10

                 20-29           20

                 ……           ……

这样的话,简单的颜色空间缩减算法就可由下面两步组成:一、遍历图像矩阵的每一个像素;二、对像素应用上述公式。

由此可知,对于较大的图像,有效的方法是预先计算所有可能的值,然后需要这些值的时候,利用查找表直接赋值即可。查找表是一维或多维数组,存储了不同输入值所对应的输出值,其优势在于只需读取、无需计算。

计算查找表代码为:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int divideWith; // convert our input string to number - C++ style  
  2.     stringstream s;  
  3.     s << argv[2];  
  4.     s >> divideWith;  
  5.     if (!s)  
  6.     {  
  7.         cout << "Invalid number entered for dividing. " << endl;   
  8.         return -1;  
  9.     }  
  10.       
  11.     uchar table[256];   
  12.     for (int i = 0; i < 256; ++i)  
  13.        table[i] = divideWith* (i/divideWith);  
这里我们先使用C++的  stringstream  类,把第三个命令行参数由字符串转换为整数。然后,我们用数组和前面给出的公式计算查找表。

2、计算运行时间

OpenCV提供了两个简便的可用于计时的函数 getTickCount() 和 getTickFrequency() 。第一个函数返回你的CPU自某个事件(如启动电脑)以来走过的时钟周期数,第二个函数返回你的CPU一秒钟所走的时钟周期数。这样,我们就能轻松地以秒为单位对某运算计时:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. double t = (double)getTickCount();  
  2. // 做点什么 ...  
  3. t = ((double)getTickCount() - t)/getTickFrequency();  
  4. cout << "Times passed in seconds: " << t << endl;  

3、四种方式扫描图像

代码在范例的基础上做了一些小修改

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include "stdafx.h"  
  2.   
  3. #include <opencv2/core/core.hpp>  
  4. #include <opencv2/highgui/highgui.hpp>  
  5. #include <iostream>  
  6. #include <sstream>  
  7.   
  8. using namespace std;  
  9. using namespace cv;  
  10.   
  11. static void help()  
  12. {  
  13.     cout  
  14.         << "\n--------------------------------------------------------------------------" << endl  
  15.         << "This program shows how to scan image objects in OpenCV (cv::Mat). As use case"  
  16.         << " we take an input image and divide the native color palette (255) with the "  << endl  
  17.         << "input. Shows C operator[] method, iterators and at function for on-the-fly item address calculation."<< endl  
  18.         << "Usage:"                                                                       << endl  
  19.         << "./howToScanImages imageNameToUse divideWith [G]"                              << endl  
  20.         << "if you add a G parameter the image is processed in gray scale"                << endl  
  21.         << "--------------------------------------------------------------------------"   << endl  
  22.         << endl;  
  23. }  
  24.   
  25. Mat& ScanImageAndReduceC(Mat& I, const uchar* table);//通过高效的C风格运算符[](指针)  
  26. Mat& ScanImageAndReduceIterator(Mat& I, const uchar* table);//通过安全的迭代法  
  27. Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar * table);//通过相关返回值的On-the-fly地址计算  
  28.   
  29. int main( int argc, char* argv[])  
  30. {  
  31.     help();  
  32.     namedWindow("The original picture",1);//命名显示原图像窗口  
  33.     namedWindow("The change picture", 1);//命名显示改变图像窗口  
  34.     //选择显示彩色or灰色  
  35.     char ch=NULL;  
  36.     printf("GRAYSCALE OR COLOR?please enter G/C");  
  37.     scanf("%c",&ch);  
  38.     Mat I, J;  
  39.     if( ch== 'G' )  
  40.         //读取灰色图片  
  41.         I = imread("F:\\QTproject\\how_to_scan_images\\Lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);  
  42.     else  
  43.         //读取彩色图片  
  44.         I = imread("F:\\QTproject\\how_to_scan_images\\Lena.jpg", CV_LOAD_IMAGE_COLOR);  
  45.   
  46.     if (!I.data)  
  47.     {  
  48.         cout << "The image could not be loaded." << endl;  
  49.         return -1;  
  50.     }  
  51.   
  52.     int divideWith = 0;   
  53.     // 把输入的字符串转为整数  
  54.     stringstream s;  
  55.     s << "50";  
  56.     s >> divideWith;  
  57.     if (!s || !divideWith)  
  58.     {  
  59.         cout << "Invalid number entered for dividing. " << endl;  
  60.         return -1;  
  61.     }  
  62.     //根据颜色空间缩减,建立查找表  
  63.     uchar table[256];  
  64.     for (int i = 0; i < 256; ++i)  
  65.         table[i] = (uchar)(divideWith * (i/divideWith));  
  66.     //执行次数为100次  
  67.     const int times = 100;  
  68.     double t;  
  69.   
  70.     t = (double)getTickCount();  
  71.   
  72.     for (int i = 0; i < times; ++i)  
  73.     {  
  74.         cv::Mat clone_i = I.clone();  
  75.         J = ScanImageAndReduceC(clone_i, table);  
  76.     }  
  77.     //计算100次的平均时间  
  78.     t = 1000*((double)getTickCount() - t)/getTickFrequency();  
  79.     t /= times;  
  80.     //显示原图片和改变图片  
  81.     imshow("The original picture",I);  
  82.     imshow("The change picture",J);  
  83.     waitKey(0);  
  84.     cout << "Time of reducing with the C operator [] (averaged for "  
  85.         << times << " runs): " << t << " milliseconds."<< endl;  
  86.   
  87.   
  88.     t = (double)getTickCount();  
  89.   
  90.     for (int i = 0; i < times; ++i)  
  91.     {  
  92.         cv::Mat clone_i = I.clone();  
  93.         J = ScanImageAndReduceIterator(clone_i, table);  
  94.     }  
  95.   
  96.     t = 1000*((double)getTickCount() - t)/getTickFrequency();  
  97.     t /= times;  
  98.   
  99.     cout << "Time of reducing with the iterator (averaged for "  
  100.         << times << " runs): " << t << " milliseconds."<< endl;  
  101.   
  102.     t = (double)getTickCount();  
  103.   
  104.     for (int i = 0; i < times; ++i)  
  105.     {  
  106.         cv::Mat clone_i = I.clone();  
  107.         ScanImageAndReduceRandomAccess(clone_i, table);  
  108.     }  
  109.   
  110.     t = 1000*((double)getTickCount() - t)/getTickFrequency();  
  111.     t /= times;  
  112.   
  113.     cout << "Time of reducing with the on-the-fly address generation - at function (averaged for "  
  114.         << times << " runs): " << t << " milliseconds."<< endl;  
  115.   
  116.     //通过核心函数LUT  
  117.     Mat lookUpTable(1, 256, CV_8U);  
  118.     uchar* p = lookUpTable.data;  
  119.     forint i = 0; i < 256; ++i)  
  120.         p[i] = table[i];  
  121.   
  122.     t = (double)getTickCount();  
  123.   
  124.     for (int i = 0; i < times; ++i)  
  125.         LUT(I, lookUpTable, J);  
  126.   
  127.     t = 1000*((double)getTickCount() - t)/getTickFrequency();  
  128.     t /= times;  
  129.   
  130.     cout << "Time of reducing with the LUT function (averaged for "  
  131.         << times << " runs): " << t << " milliseconds."<< endl;  
  132.     char ch1=NULL;  
  133.     scanf("%c",&ch1);  
  134.     while (ch1!='c')  
  135.     {  
  136.         ch1=getchar();  
  137.     }  
  138.     return 0;  
  139. }  
  140.   
  141. Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)  
  142. {  
  143.     // accept only char type matrices  
  144.     CV_Assert(I.depth() != sizeof(uchar));  
  145.       
  146.     int channels = I.channels();//得到通道数  
  147.   
  148.     int nRows = I.rows;//获得行数  
  149.     int nCols = I.cols * channels;//行数*通道数=一行中有多少个数值  
  150.     //如果图像矩阵存储空间是连续的  
  151.     if (I.isContinuous())  
  152.     {  
  153.         nCols *= nRows;//得到存储矩阵的大小  
  154.         nRows = 1;  
  155.     }  
  156.   
  157.     int i,j;  
  158.     uchar* p;  
  159.     for( i = 0; i < nRows; ++i)  
  160.     {  
  161.         p = I.ptr<uchar>(i);//得到存储矩阵的起始地址  
  162.         for ( j = 0; j < nCols; ++j)  
  163.         {  
  164.             p[j] = table[p[j]];//赋值  
  165.         }  
  166.     }  
  167.     //整个过程起始是将矩阵看出是一个一行nCols列的矩阵,由p指针指向矩阵,再进行赋值。  
  168.     return I;  
  169. }  
  170.   
  171. Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)  
  172. {  
  173.     // accept only char type matrices  
  174.     CV_Assert(I.depth() != sizeof(uchar));  
  175.   
  176.     const int channels = I.channels();  
  177.     switch(channels)  
  178.     {  
  179.     case 1:  
  180.         {  
  181.             MatIterator_<uchar> it, end;//定义uchar的迭代器  
  182.             for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)  
  183.                 *it = table[*it];  
  184.             break;  
  185.         }  
  186.     case 3:  
  187.         {  
  188.             MatIterator_<Vec3b> it, end;//定义Vec3b的迭代器  
  189.             for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)  
  190.             {  
  191.                 (*it)[0] = table[(*it)[0]];  
  192.                 (*it)[1] = table[(*it)[1]];  
  193.                 (*it)[2] = table[(*it)[2]];  
  194.             }  
  195.         }  
  196.     }  
  197.   
  198.     return I;  
  199. }  
  200.   
  201. Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)  
  202. {  
  203.     // accept only char type matrices  
  204.     CV_Assert(I.depth() != sizeof(uchar));  
  205.   
  206.     const int channels = I.channels();  
  207.     switch(channels)  
  208.     {  
  209.     case 1:  
  210.         {  
  211.             forint i = 0; i < I.rows; ++i)  
  212.                 forint j = 0; j < I.cols; ++j )  
  213.                     I.at<uchar>(i,j) = table[I.at<uchar>(i,j)];//对行列循环对每个数据点进行赋值  
  214.             break;  
  215.         }  
  216.     case 3:  
  217.         {  
  218.             Mat_<Vec3b> _I = I;//使用Mat_类进行运算,较为方便  
  219.   
  220.             forint i = 0; i < I.rows; ++i)  
  221.                 forint j = 0; j < I.cols; ++j )  
  222.                 {  
  223.                     _I(i,j)[0] = table[_I(i,j)[0]];  
  224.                     _I(i,j)[1] = table[_I(i,j)[1]];  
  225.                     _I(i,j)[2] = table[_I(i,j)[2]];  
  226.                 }  
  227.                 I = _I;  
  228.                 break;  
  229.         }  
  230.     }  
  231.   
  232.     return I;  
  233. }  

4、运行图片结果为:

      
           图1、原图片                        图2、颜色缩减图片

时间对比如下:


5、结论:

Efficient Way 2.27585 milliseconds
Iterator 54.8902 milliseconds
On-The-Fly RA 116.674 milliseconds
LUT function 1.35165 milliseconds
①调用LUT 函数可以获得最快的速度. 这是因为OpenCV库可以通过英特尔线程架构启用多线程

从安全性角度来考虑,迭代法是更佳的选择,但是效率不是很明显

③On-The-Fly RA方法扫描全图是一个最浪费资源的方法

④Efficient Way方法是比较有效的方法,使用起来也方便,推荐使用

下面网址是其他博客收集或者自己编写的代码,大家可以参考一下


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

【OpenCV】访问Mat中每个像素的值(新)

OpenCV学习笔记(四十二)——Mat数据操作之普通青年、文艺青年、暴力青年

6、用到的类:

Mat_类:

这个类来源于Mat类,数据结构为:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. template<typename _Tp> class Mat_ : public Mat  
  2. {  
  3. public:  
  4.     // ... some specific methods  
  5.     //         and  
  6.     // no new extra fields  
  7. };  
这个类只封装了Mat的头部,没有数据空间,也没有Mat的虚拟函数,所以他和Mat之间的引用和指针之间的转换非常容易,例如:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // create a 100x100 8-bit matrix  
  2. Mat M(100,100,CV_8U);  
  3. // this will be compiled fine. no any data conversion will be done.  
  4. Mat_<float>& M1 = (Mat_<float>&)M;  
一般情况下有Mat类就足够了,但是当你对元素进行大量的操作时,Mat_类会给你带来更大的方便, Mat::at<_Tp>(int y, int x)  和  Mat_<_Tp>::operator ()(int y, int x)两个操作是一样的,后面的函数会显得简短一些。

通过vec作为参数,可以使用Mat_多通道矩阵,如:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // allocate a 320x240 color image and fill it with green (in RGB space)  
  2. Mat_<Vec3b> img(240, 320, Vec3b(0,255,0));  
  3. // now draw a diagonal white line  
  4. for(int i = 0; i < 100; i++)  
  5.     img(i,i)=Vec3b(255,255,255);  
  6. // and now scramble the 2nd (red) channel of each pixel  
  7. for(int i = 0; i < img.rows; i++)  
  8.     for(int j = 0; j < img.cols; j++)  
  9.         img(i,j)[2] = 255;  

LUT函数:

数据结构:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void LUT(InputArray src, InputArray lut, OutputArray dst)  
src:8位的源矩阵

lut:256个元素的查找表

dst:输出矩阵,和src有着同样的size和同样的channel,和lut有着同样的depth

功能是为目标矩阵填充查找表数据,原理是如下:

                           \texttt{dst} (I)  \leftarrow \texttt{lut(src(I) + d)}
                           d =  \fork{0}{if \texttt{src} has depth \texttt{CV\_8U}}{128}{if \texttt{src} has depth \texttt{CV\_8S}}

将src的数据加上d再通过查找表查找数据,最后赋值给dst。



网址:http://blog.youkuaiyun.com/chenjiazhou12/article/details/21052849
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值