检测四边形(多边形)

来源:互联网 发布:淘宝开店考试 编辑:程序博客网 时间:2024/06/08 03:08

 如何用检测由直线构成的四边形(多边形)?  这个问题自己困扰了十来天,  查了相关的算法书

(a modern approach, algorithms and applications, computer and machine vision,

Feature Extraction & Image Processing ),  并未找到直接的内容可以使用

    这阵子看了一些论文,  有用霍夫变换的,  有通过edge找vanishing point的,  有用ransac或最小二乘法拟合直线的,

印象里,  在我搜到有关quadrangle, quad, rectangle, rectilinear 之类关键词的高引用次数论文中, 

前面两个方法应用的比较多. 也不知道我关键词选的是否合适.检测多边形的原理和检测四边形类似.


下面进入正题 先说怎么直接调用opencv来检测四边形(多边形)

     目前我只能想到下面几个

1.approxPolyDP

2.findContours中的CV_CHAIN_APPROX_SIMPLE

3.hough变换

下面介绍opencv函数的使用

最简单的要属approxPolyDP,   算法解释: 看wiki的动图

https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm    

    std::vector<std::vector<cv::Point>> roi_point;
    cv::findContours(roi_enlarged,roi_point,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);
    std::vector<cv::Point> roi_point_approx;
    cv::Mat roi_approx(roi_enlarged.size(),CV_8UC3,cv::Scalar(0,0,0));
    auto i = roi_point.begin();
    approxPolyDP( *i, roi_point_approx, 7, 1 );
    for(auto a : roi_point_approx)
        cv::circle(roi_approx,a,2,cv::Scalar(0,0,255));
    
    cv::imshow("roi_approx",roi_approx);

原图

输出

不用findContours行不行?  我想说要是能自己提取最外层轮廓也好, 多花时间多写写函数呗

之前我想用adaptiveThreshold得到的轮廓直接来用,  但是后面发现从adaptiveThreshold中得到的是里外两层,

没有办法直接用

直接用成了这样

CV_CHAIN_APPROX_NONE改为CV_CHAIN_APPROX_SIMPLE也可以,

findContours中的几个参数解释在这里

判断是几边形去看roi_point_approx.size()就好了, 是几就是几边


    我对这样的结果并不是满意,  觉得通过这种方法忽略了很多点,   不那么精确, 

所以一直就想通过其他方法. 老实说我所需要的并不那么精确,  就当多学东西了, 结果这几天净瞎搞,  浪费不少时间, 心理还受挫


hough变换

    要使用hough变换,  首先得明确四边形的定义,  也就是说对四条边进行一定的限制. 

这个定义是由你自己决定的,  不是唯一的.下面是我的定义

1. 边的定义:  每条边最短为20

2. 相邻边的定义:  相邻两边不小于20度

3. 对边的定义:   对边倾斜角度不大于20度,  且距离最短的两点间距要大于20像素


要使用的函数就是houghLinesP,  通过上述定义(条件)将边数缩减到4. 难点在于如何理清这些线段的关系


    还有一种hough变换,  就是对点集求出最高的peak,  然后将peak对应直线在给定的距离内,  

将点进行删除、再hough,  循环四次,  便得到了需要的直线.  需要对hough函数进行修改, 且计算量较大.

 原图  

 输出

       代码传送门 :   我的github

之前总以为越是细分参数,  结果越精确,  然而后面发现,  得票最高的直线, 不一定是直观上最符合的直线, 

反而由于计算量大拖慢了程序,找错了直线.



再进一步的hough变换

    按照hough变换的原理,  检测一个四边形,  由于是四个角点,  所以应该是创建一个 八维的参数空间. 

这个花了我很长时间思考,  四边形的一般方程是什么?  很久都想不出来,  总是想往直线方程上面靠,  

结果还是不得不放弃.  想到它的计算量,  我就放心了,  光是 ρ的精度为0.2, theta 为1 度,程序就差不多运行了2秒, 搞不出来就搞不出来吧



    另外opencv还有个fitline函数, 我这里一直报错不能试验, 所以也不清楚是否能够使用

若能使用的话, 我觉得首先要对每个边进行标记(labeling),  因为最小二乘法本身没有对无关的点进行排除.

若是fitline有能够排除无关点的效果是最好,  没有的话,  要对每个边进行labeling不是那么容易,  

因为

1. edge基本上是破碎的, 是不相连的, 即便相连也有可能占到了两个边,  甚至可能整体被当作一个边.

即使用了某种方法得到完整四边形的edge,  边与边并不好区分

2. 四边形的四个顶点,  若是通过找极限位置,  有可能只能找到三个; 有些情况甚至极限位置可能也不是角点

3. 不知道有没有同学和我做过一样事情,  先想办法将edge连接成一个整体,  然后通过找角点进行打断. 然而,

对于某些极端的状况, 某条边上的"锯齿"被认为是个角点, 尽管可以调整参数让neighborhood大一些, 

但是消去了一些边,甚至可能消去整条边也不行,且没有普适的参数. 当然也可以把图像缩放至指定大小, 

然后用固定的参数检测角点, 但是总感觉不太稳妥.

我对labeling的理解不够, 但是没什么好办法这么直接告诉电脑说四条边分别是哪一条

关于line labeling 算法 

Huffman 1971; Clowes 1971; Waltz 1975; Rosenfeld, Hummel, and Zucker 1976; Kanade 1980

及一本教材 a guide tour of computer vision ,Nalwa 1993

 

http://www.cs.ubc.ca/~mack/Publications/AI73.pdf   这个是我随便找的  还没有看




RANSAC 

    本人写的 ransac函数, c++, 根据computer and machine vision写的

    仅供参考, 效率有待优化  ,    我的github : 传送门

    ransac比较符合人的视觉系统:  随机抽取两个点, 然后相对这两个点所确定的直线,  在给定距离之内的每个点

都算作一票,   票数最高的直线即为所求,  然后再将这些点删除.  如此循环四次,  便得到所求的四边形.

或者循环n次,  直至票数小于某阈值,即得到了所求的n边形.  或小于最大循环次数时,  得到随机取样最符合的直线.

  相较于霍夫变换,  这个计算量貌似小多了?  


寻找vanishing point

    vanishing point 在 Algorithms and applications这本书里的位置是255页4.3.3

很抱歉没有细看,   暂时不继续写了