【OPENCV】近段时间学习札记


        按照每个星期做的东西来写吧。

1、看代码,自己根据所学的内容做个小demo

        主要就是看的老大丢给我们的博客咯。http://www.cnblogs.com/updateofsimon/archive/2013/12/19/3482732.html

        大概理解了一下Mat、 IplImage、 cvMat这三种数据容器。三者的区别到现在也没怎么理解,大致的印象就是Mat偏重计算,剩下的倆偏重“图形”,而且Mat是最新的,不用手动管理内存。

        个人的感觉是,对不同容器的常用函数都不熟悉,当要实现一个功能的时候感到无从下手。网上有各种代码,但是可能前后使用的容器不一样,转换的时候又会出现指针类型与非指针类型之间不能转的情况。比如IplImage一般都会使用指针变量初始化,但是Mat则不会,Mat转换成IplImage不成功。对于三种数据容器尤其Mat的了解还有待加深。

        同时此阶段还知道了迭代器iterator,查了一些资料得出的理解是,迭代器提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。用起来就像一个循环函数,遍历容器当中的对象一样。

使用的东西:

        ①访问各个类型图片的每个像素或行

         image.at<uchar>(i,j):取出灰度图像中i行j列的点。

         image.at<Vec3b>(i,j)[k]:取出彩色图像中i行j列第k通道的颜色点

         image.ptr<uchar>(image.rows): 每行数据的指针,每行有个地址 image.cols*image.channels()

         MatIterator_<Vec3b> it_out=outImage.begin<Vec3b>(), itend_out=outImage.end<Vec3b>(): 迭代器访问每个点

        ②迭代器示例

        Mat_<Vec3b>::iterator it = MatLab.begin<Vec3b>();
        Mat_<Vec3b>::iterator itout = result.begin<Vec3b>();

2、类似大家来找茬,找不同

        Mat本身有subtract函数使得两幅图相减得到RGB值,因此这个实现起来非常方便,减完不为0的像素就是不同的地方。但是这个函数有个值得注意的一点就是,当减完得到的为负值时,会自动变为0,因此需要将两幅图正负减两次

        不过我当时因为没注意到这个bug,所以采用的不是这个方法,而是用迭代器遍历了每个像素来对比,差值大于阈值定为不同,并把不同的地方用绿色标注了出来。我这样做有优点也有缺点,缺点一个是代码麻烦(我也不会分析什么程序效率啦这些东西),一个是阈值的设定拿不准。按时也有优点,就是可以在原图的基础上显示出来不同的地方,而相减的方法得到的图片当中,相同的地方全为黑色。

使用的东西:

        ①subtract函数

        subtract(tmp1, tmp2, result1);

        ②rect类型

        可以选定一个特定的矩形框。创造一个rect类型的时候,如Rect rect1(0, 0, ceil_width, ceil_height); 前两个参数为左上角坐标,后两个参数为矩形的长与宽。

3、找出图片里的圆圈,并画出来

        在这里我接触到了cvHoughCircles 函数,该函数可以根据设定的条件返回圆心与半径。但是这个函数不能同时找出同心圆,而是每次先找出一个圆。

使用的东西:

        ①cvHoughCircles函数

        cvHoughCircles(CvArr *image,             //输入8bit(灰度)图像
                                   void *circle_storage,   //检测到的圆存储仓
                                   int method,                 //变换方式,目前只支持CV_HOUGH_GRADIENT,
                                   double dp,                  //寻找圆弧圆心的累计分辨率,这个参数允许创建一个比输入图像分辨率低的累加器。
                                                                        //如果dp设置为1,则分辨率是相同的;
                                                                       //如果设置为更大的值(比如2),累加器的分辨率受此影响会变小(此情况下为一半)。

                                                                       //dp的值不能比1小。
                                    double min_dist,        //该参数是让算法能明显区分的两个不同圆之间的最小距离。
                                    double param1,         //用于Canny的边缘阀值上限,下限被置为上限的一半。
                                    double param2,         //累加器的阀值。
                                    int min_radius,           //最小圆半径
                                    int max_radius)          //最大圆半径     
  

        成功时返回CvSeq指针。

②cvGetSeqElem()

访问Seq的元素,cvGetSeqElem( name,i ),访问名为name的第i个元素。

4、二值图像连通域标记ing

        参考的博客是 http://www.cnblogs.com/ronny/p/img_aly_01.html

        文中介绍了两种标记连通域的方法

A:现在matlab中连通区域标记函数bwlabel中使的算法,它一次遍历图像,并记下每一行(或列)中连续的团(run)和标记的等价对,然后通过等价对对原来的图像进行重新标记。

         这个有点像我们学数据结构的Kruskal的最小生成树算法。首先把每个连续的团标号,再将等价对依次加进来,并统一其标号

        感觉该算法的思路是这样的:

        (1) 找出并标记所有的团(run)

        此处每个run都有stRun标记这个run的起始坐标,enRun标记这个run的终点坐标,rowRun标记这个run处于哪一行。这里比较难理解的是这三个属性并没有放在一起来解释run,而是所有run的stRun放在vector<int> stRun 这个矢量里面,stRun[第i个run] = 第i个run的起始坐标。同理对enRun和rowRun。

        (2) 完成团的标记与等价对列表的生成

        第一步是找出了所有的团,这一步则对团进行标号,连通在一起的团将享有同一个标号,若一个团与上一行的多个团同时连通,则标为上一行最小号的团的标号。并将上一团的这几个标号写入等价对,说明它们是连通的。

        (3)处理等价对,将所有连通的团放到同一个等价列表当中

        这一步操作的对象可以看成是每个团标号(runlabel)。

        vector<vector<bool>> eqTab(maxLabel, vector<bool>(maxLabel, false));      //相当于一个连通矩阵记录任意两个run是否连通

初始化这个矩阵,即根据第二步生成的等价对更改这个连通矩阵。

        遍历所有的团标号,来看每个标号应该加入哪个连通域,并赋连通域标号(labelFlag)。

        若当前的标号不属于任何一个连通域,则新给它一个连通域标号(labelFlag),并把它加入当前的连通域(tempList.push_back()),遍历当前连通域内的所有标号,将包含其中标号的所有等价对都加入到当前连通域内。代码如下:

for (int i = 1; i <= maxLabel; i++)
  {
          if (labelFlag[i - 1])
          {
              continue;
          }
          labelFlag[i - 1] = equaList.size() + 1;
          tempList.push_back(i);
          for (vector<int>::size_type j = 0; j < tempList.size(); j++)
          {
              for (vector<bool>::size_type k = 0; k != eqTab[tempList[j] - 1].size(); k++)
              {
                  if (eqTab[tempList[j] - 1][k] && !labelFlag[k])                          //说明标号k+1与j连通,并且k+1没有被表示过,不属于任何连通域
                  {
                      tempList.push_back(k + 1);
                      labelFlag[k] = equaList.size() + 1;
                  }
              }
          }
          equaList.push_back(tempList);
          tempList.clear();
      }


B. 现在开源库cvBlob中使用的标记算法,它通过定位连通区域的内外轮廓来标记整个图像。

        opencv里面直接就有轮廓的搜索算法。

        但是,这个轮廓搜索算法首先是基于二值图,同时搜出的来的是 黑底白图 的轮廓。所以在转化成二值图的时候,注意背景得是黑色。

        这个比较简单,直接调用findContours()即可,外轮廓的数量便是连通域的数量。

使用的东西:

        ①向量:

        vector<T> v(n,i)形式,v包含n 个值为 i 的元素。

        访问方法:v[k],若为双层,比如vector<vector<int>> p,则为p[i][j]来访问。

        ②string::size_type 

        引用:在标准库string类型中,最容易令人产生误解就是size()成员函数的返回值了,如果不深入分析的话,大多人都会认为size()的返回值为int类型,其实不然。事实上,size操作返回的是string::size_type类型的值。 string类类型和许多其他库类型都定义了一些配套类型(companion type)。通过这些配套类型,库类型的使用就能和机器无关(machine-independent)。size_type就是这些配套类型中的一种。它定义为与unsigned型(unsigned int 或 unsigned long)具有相同的含义,而且可以保证足够大能够存储任意string对象的长度。为了使用由string类型定义的size_type类型,程序员必须加上作用域操作符来说明所使用的size_type类型是由string类定义的。

        ③findContours()

        void findContours( InputOutputArray image,              //二值图
                                       OutputArrayOfArrays contours,      //检测的轮廓数组,每一个轮廓用一个point类型的vector表示

                                                                                                //( vector<vector<Point>>contours;)
                                       OutputArray hierarchy,                   //参数和轮廓个数相同,

                                                                                                //每个轮廓contours[ i ]对应4个hierarchy元素hierarchy[ i ][ 0 ] ~hierarchy[ i ][ 3 ]

                                                                                               //分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,

                                                                                                //如果没有对应项,该值设置为负数。(vector<Vec4i>hierarchy>)
                                        int mode, //轮廓的检索模式
                                        int method,//轮廓的近似办法
                                        int offset=Point())//轮廓点的偏移量,可以设置为任意值。

 

        ④drawContours()

        void cvDrawContours( CvArr *img,
                                              CvSeq* contour,                        //输入的轮廓组,每一组轮廓由点vector构成
                                              int contourIdx,                          //画第几个轮廓,如果该参数为负值,则画全部轮廓
                                              CvScalar color,                          //轮廓颜色
                                              int thickness=1,                        //轮廓线宽。如果为负值或CV_FILLED表示填充轮廓内部
                                              int line_type=8,                        //线型
                                              CvPoint offset=cvPoint(0,0) ,
                                               int max_level)



任重道远。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值