• 对图像进行读入和简单的处理


    这次我们主要学习使用opencv的图片读取。

    Opencv 提供了imread 函数能够很快的读取图片,你可以用Mat类型的对象进行接收,imread函数的原型是:

    1 Mat imread(const string& filename, int flags);

    第一个参数是文件名称第二个是读取的方式,Imread函数使用是:

    1 Mat img = imread(filename);

    如果你读入一个jpg文件,缺省情况下将创建一个3通道图像。如果你需要灰度(单通道)图像,使用如下语句:

    Mat img = imread(filename,0);

    现在你会读入图像了,那么下一步必定想知道如何对图像进行处理,要进行处理就必须先知道如何遍历图像,会遍历图像的话那么你就会将复杂的算法和逻辑运用到你要处理的图像上去了,然后你就可以完成你要实现的功能。

    回归到正题,如果你要对图像进行操作那你就先必须用一个指针指向你要遍历的图像地址。然后对图像的每一个像素进行遍历(在此我们进行行遍历,也就是一行行的扫描每一个像素),然而我们又知道图片的每一个像素都是由三个通道(对应自然界的三原色

    RGB)构成的,在opencv中这三个通道的顺序并不是RGB而是BGR,至于为什么,额~,我也不知道,以后知道了补上。然后计算机会通过RGB(R,G,B)这个函数求出一个值这就是RGB颜色空间的值,也就是这个点像素的值。所以我们遍历像素时如果需要对某

    一个通道进行操作的话,那必须把列的数量*3。如果不需要对通道进行操作,那么图像的列和行就是你用鼠标在windows资源管理器中所看到的值(xxx*xxx)。

    好了,说了这么多关于图像的基础知识,我们实现一个例子,这个例子网上都是有的,功能是,用颜色空间缩减的方法来提高对图像操作的效率。做法是:将现有颜色空间值除以某个输入值,以获得较少的颜色数。例如,颜色值0到9可取为新值0,10到19可取为10,以此类推。

     我们的例子提供了两种做法,一种就是直接自己读取,一种则是用opencv的函数库进行处理。两两正好可以用来对比。先看例子:

     1 #include <opencv2/opencv.hpp>
     2 
     3 using namespace std;
     4 using namespace cv;
     5 #define  divideWith 50 
     6 Mat& ScanImageAndReduceIterator(Mat& I, const uchar* table);
     7 int main(int argc, char* argv[])
     8 {
     9     Mat I, J;
    10     const char* imagename = "../test.jpg";
    11 
    12     //! read a image
    13     I = imread(imagename, CV_LOAD_IMAGE_COLOR);
    14     //! read image fail
    15     if (I.empty())
    16     {
    17         fprintf(stderr, "Can not load image %s
    ", imagename);
    18         return -1;
    19     }
    20     //! show image
    21     imshow("image", I);
    22     waitKey();
    23     //! define a table to store the preprocessed data
    24     uchar table[256];
    25     for (int i = 0; i < 256; ++i)
    26         table[i] = divideWith* (i / divideWith);//example:Iold=14; Inew=(Iold/10)*10=(14/10)*10=1*10=10;
    27     double t;
    28     t = (double)getTickCount();
    29     //! use The iterator (safe) method to realize this function 
    30     J = ScanImageAndReduceIterator(I.clone(), table);
    31     t = 1000 * ((double)getTickCount() - t) / getTickFrequency();
    32     cout << "Time of reducing with the iterator :" << t << " milliseconds." << endl;
    33     imshow("image1", J);
    34     waitKey();
    35     //! use the lookUpTable which provided by opencv to realize this function 
    36     t = (double)getTickCount();
    37     Mat lookUpTable(1, 256, CV_8U);
    38     uchar* p = lookUpTable.data;
    39     for (int i = 0; i < 256; ++i)
    40         p[i] = table[i];
    41     LUT(I, lookUpTable, J);
    42     t = 1000 * ((double)getTickCount() - t) / getTickFrequency();
    43     cout << "Time of use lookUpTable :" << t << " milliseconds." << endl;
    44     imshow("image2", J);
    45     waitKey();
    46     system("pause");
    47     return 0;
    48 }
    49 //! The iterator (safe) method
    50 Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
    51 {
    52     //! accept only char type matrices
    53     CV_Assert(I.depth() != sizeof(uchar));
    54 
    55     const int channels = I.channels();
    56     switch (channels)
    57     {
    58     case 1:
    59     {
    60         MatIterator_<uchar> it, end;
    61         for (it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)
    62             *it = table[*it];
    63         break;
    64     }
    65     case 3:
    66     {
    67         MatIterator_<Vec3b> it, end;
    68         for (it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
    69         {
    70 (*it)[0] = table[(*it)[0]]; 71 (*it)[1] = table[(*it)[1]]; 72 (*it)[2] = table[(*it)[2]]; 73 } 74 } 75 } 76 77 return I; 78 }

    上面例子中ScanImageAndReduceIterator函数就是遍历图像,channels就是我们上面说的通道数,这加了判断,对不同通道数的图像进行不同的处理,比如单通道数就直接用uchar(0~256)接收,而三通道则用到了向量模板<Vec3b>这个很好理解,就是每个像素下都存在一个向量,这个向量包含三个元素也就是三个通道,因此得到这个像素后,可以通过<Vec3b>方便的进行操作。MatIterator_<Vec3b>这是个迭代器,指定I.begin<Vec3b>()头,和I.end<Vec3b>()尾就相当于for循环进行操作了,非常方便。这样图像的遍历就完成了。

    上面说我们我们程序的功能就是要实现对图像颜色的压缩,压缩公式是:,所以我们把处理好的数据都放到一个table[256]中,然后对每个像素重新进行赋值。大家应该也看到了我们先用Mat的构造函数new出了也Mat对象lookUpTable,然后把我们之前处理好的table[256]直接赋值给到lookUpTable对象的data中,如下:

    1 Mat lookUpTable(1, 256, CV_8U);
    2 uchar* p = lookUpTable.data;
    3     for (int i = 0; i < 256; ++i)
    4         p[i] = table[i];
    5 LUT(I, lookUpTable, J);

    然后调用LUT(原图像,查找表,输出图像);直接到达我们的效果,好我们自己遍历的效果一样。我们看看结果~

    原图:

    1、自己扫描赋值

    2、LUT函数处理

    时间是:,由此可见我们的效率还差很多,所以我们最好能用到指针来操作,以后我会实现~

    最后,我有个想法就是接下来我会写一个系列,关于图像处理的,具体的思路是先把基础的图像操作实现一遍,然后将我的研究和大家分享~希望大家能帮我指正错误。

  • 相关阅读:
    C# 实现任务栏图标程序
    C#实现的木马之客户端
    sql基本语法
    水晶报表引用DataSet做数据源
    解决多线程操作控件时可能出现的异常:“在某个线程上创建的控件不能成为在另一个线程上创建的控件的父级”
    电子书籍制作工具软件大全
    C#实现的木马之服务端
    2进制、8进制、10进制、16进制...各种进制间的轻松转换
    VC# .Net中使用Crystal Report水晶报表
    P2P技术学习
  • 原文地址:https://www.cnblogs.com/xiaoding/p/4886248.html
Copyright © 2020-2023  润新知