初步思路
通过边缘检测的算法能够将图像中的线条提取出来,每一个曲线对应的是无数条直线,只要找到一种方法统计在“同一条直线”上的小直线的数目,就能将图片中的“肉眼可见的直线”统计出来并在图上画出来
第一步提取边缘的曲线(无数小直线)的方法是在三个通道上去计算相邻像素的导数值,将这个区域的权重设置为导数值正相关的函数(可能要调整使得能够好分辨)。或着另一种思路是直接对图像二值化,然后直接通过相邻像素相减把边缘标记出来
第二步是将边缘的数据转化为很多条直线,也就是求等高线的切线,只要把梯度求出来做垂直就行了,代表的是这个图像的切线。
第三步是把直线的参数放在一起作为未知数放在坐标系里,这些点都代表着空间上一条唯一的线,那么某个区域的点越多,就说明有越多的“小直线”在同一条直线上。这个方法顺便也能把虚线这种断开的线给检测出来。如何判断这个区域有“很多点”呢?可以考虑区域分隔的统计方法
直线参数的研究
我们都知道斜率作为参数其范围是整个实数,当 k 很大或很小的时候,k 的大小就不能直观的反映出两个直线是不是一样斜了,b 也受 k 牵连,不是很好判断。所以考虑用与“旋转”概念最接近的极坐标表示直线的参数:
ρ=xcosθ+ysinθ
不难得出,ρ 代表着直线到原点的距离,而 θ 代表距离连线的倾斜角
下面考虑验证 ρ 和 θ 对于随机取点形成的直线的随机分布:
对于在 (0,1) 的矩形区域内随机取两个点,记录其参数
可以发现对于上面的方程,随机取点产生的参数 ρ 和 θ 并不是在空间上呈现对称随机分布
推测是由于区域是正方形且取点都是正值造成的,考虑到图片一般为矩形,且在边缘出现直线的概率本身就是会比中心要少的特点,我们将原点移动到中心进行计算:
因为随机点生成的图像在对角线上的概率比其他的都要大,所以这个结果是合理的,同时也导致了在这个时候的距离会比较小(集中在原点了),其概率的集中是完全基于图像的、合理的,故考虑使用这个参数作为判断直线为同一条的判据。
图像处理
我们的目的是提取出图像的边缘,然后通过对边缘求切线得出每个位置的直线情况。同时根据边缘的明不明显可以设置一个权重,减少非边缘区域得出的没有意义的切线对统计结果的影响。这可以通过对图像求梯度实现,这样就能够同时得到边缘的方向信息和变化信息:
成功得到图像的梯度
考虑到一个颜色渐变的图像,每个渐变点的梯度都很大,但是这个位置并不是一个突变的边缘,所以考虑计算二阶重偏导数
可以看到突变的点大多数都明显的区分开了,这表明这个二阶重偏导数可以直接作为权重使用
参数方程的另外解释
前面的方法是提取切向量,对图像处理要求较高,如果在已知仅为边缘的一系列离散的点的情况,仍可以通过根据参数空间的特点求出“可能的直线”。
前面通过的是普通直角坐标系中一条线确定参数空间中的一个点,反过来,如果根据原来坐标系中的点,可以确定参数空间内的一条正弦曲线(参数用极坐标直线方程表示),这条线代表的是经过这个点的所有直线。而正弦曲线的交点就代表了原来坐标系内两个点形成的直线的参数。那么只要根据原来坐标系内的所有的点,画出所有的参数曲线,求出所有的交点,也就是每两个点连成的所有可能直线,再统计聚集的位置就能求出直线。
不过这个方法相当于遍历所有的点对(两个点),把很多相距很远、不相关的点也纳入考虑了,所以不是很好。
下面对这个方法进行测试:
可以看出这里有两个聚集点,分别对应两个直线;而在两个聚集点外仍有很多交点,这些交点代表的是不是同一个边缘的点连成的直线的参数,所以是多余的。故下面采用最前面提到的方法
对图像的直线检测实现
首先计算图片的梯度和二阶导,根据梯度求出 θ 和 ρ,根据二阶导的权重放入统计中,做出热力图。
θ=arctan(ΔxΔy)
ρ=xcosθ+ysinθ
但是实验的结果并不理想。使用二阶导作为权重会导致统计的梯度不在边缘地区而是有偏移,而且默认的梯度为 0,导致结果中有很多垃圾数据。而这一切的原因都是我们使用的是二阶导来判断边缘
故考虑使用其他方法判断边缘
Canny 边缘检测算法
canny 边缘检测算法分为 5 个步骤:
- 对图像的噪声进行抑制
- 就是对图像进行平滑操作,其实就是高斯模糊
- 计算图像的梯度和方向
- 用差分算法,和上边一样,对八个方向相邻像素分析
- 非极大值抑制
- 当梯度达到局部的最大值的时候,我们就认为它是图像的边缘
- 而对于非极大值的点,即使超过了我们设定的“边缘梯度阈值”,也不认为它是边缘
- 实现具体操作:
- 沿着梯度的方向判断就可以了
- 双阈值检测
- 定义两种阈值,分成不是边缘、弱边缘、强边缘三种
- 边缘连接
- 不和强边缘连接的弱边缘就归零
- 否则升为强边缘
hough 变换验证可行性
替换上面的方法为 canny 检测,只统计边缘的点
初步验证了可行性
对图像进行映射
进过实际操作,考虑到映射到原图上的难度,一开始的原点还是定在左下角了
最终效果
未解决的问题
求出来的直线是跨越整个图像的,如何求出特定的线段呢?