查找图像内的边缘(矩形的边界)

Posted

技术标签:

【中文标题】查找图像内的边缘(矩形的边界)【英文标题】:Find edges (border of rectangle) inside an image 【发布时间】:2015-05-23 08:45:28 【问题描述】:

我在背景上有一张便签的图像(比如墙壁或笔记本电脑),我想检测便签的边缘(粗略检测也可以正常工作),以便我可以对其进行裁剪.

我计划使用 ImageMagick 进行实际裁剪,但一直在检测边缘。

理想情况下,我的输出应该为我提供 4 个边界点的 4 个坐标,以便我可以对其进行裁剪。

我应该如何处理这个?

【问题讨论】:

你用的是哪个图片处理库? 【参考方案1】:

您可以使用 ImageMagick 做到这一点。

人们可以想出不同的 IM 方法。这是我想到的第一个算法。它假设“便笺”在较大的图像上没有倾斜或旋转:

    第一阶段:使用 canny 边缘检测 来显示便签的边缘。 第二阶段:确定边缘的坐标。

Canny 边缘检测

此命令将创建一个黑白图像,描绘原始图像中的所有边缘:

convert                              \
  http://i.stack.imgur.com/SxrwG.png \
 -canny 0x1+10%+30%                  \
  canny-edges.png

确定边的坐标

假设图像大小为XxY 像素。然后,您可以将图像大小调整为1xY 列和Xx1 像素行,其中每个像素的颜色值是与相应列位于同一行或同一列的所有像素的相应像素的平均值/行像素。

作为可以在下面看到的示例,我将首先将新的 canny-edges.png 调整为 4xYXx4 图像:

identify -format  " %W x %H\n"  canny-edges.png
 400x300

convert canny-edges.png -resize 400x4\!   canny-4cols.png
convert canny-edges.png -resize   4x300\! canny-4rows.png

canny-4cols.png

canny-4rows.png

现在前面的图像已经将图像压缩调整为几列或几行像素将实现什么效果,让我们用单列和单行来实现。同时我们将输出格式改为text,而不是PNG,以便得到这些白色像素的坐标:

convert canny-edges.png -resize 400x1\!   canny-1col.txt
convert canny-edges.png -resize   1x300\! canny-1row.txt

这是canny-1col.txt的部分输出:

# ImageMagick pixel enumeration: 400,1,255,gray
0,0: (0,0,0)  #000000  gray(0)
1,0: (0,0,0)  #000000  gray(0)
2,0: (0,0,0)  #000000  gray(0)
[....]
73,0: (0,0,0)  #000000  gray(0)
74,0: (0,0,0)  #000000  gray(0)
75,0: (10,10,10)  #0A0A0A  gray(10)
76,0: (159,159,159)  #9F9F9F  gray(159)
77,0: (21,21,21)  #151515  gray(21)
78,0: (156,156,156)  #9C9C9C  gray(156)
79,0: (14,14,14)  #0E0E0E  gray(14)
80,0: (3,3,3)  #030303  gray(3)
81,0: (3,3,3)  #030303  gray(3)
[....]
162,0: (3,3,3)  #030303  gray(3)
163,0: (4,4,4)  #040404  gray(4)
164,0: (10,10,10)  #0A0A0A  gray(10)
165,0: (7,7,7)  #070707  gray(7)
166,0: (8,8,8)  #080808  gray(8)
167,0: (8,8,8)  #080808  gray(8)
168,0: (8,8,8)  #080808  gray(8)
169,0: (9,9,9)  #090909  gray(9)
170,0: (7,7,7)  #070707  gray(7)
171,0: (10,10,10)  #0A0A0A  gray(10)
172,0: (5,5,5)  #050505  gray(5)
173,0: (13,13,13)  #0D0D0D  gray(13)
174,0: (6,6,6)  #060606  gray(6)
175,0: (10,10,10)  #0A0A0A  gray(10)
176,0: (10,10,10)  #0A0A0A  gray(10)
177,0: (7,7,7)  #070707  gray(7)
178,0: (8,8,8)  #080808  gray(8)
[....]
319,0: (3,3,3)  #030303  gray(3)
320,0: (3,3,3)  #030303  gray(3)
321,0: (14,14,14)  #0E0E0E  gray(14)
322,0: (156,156,156)  #9C9C9C  gray(156)
323,0: (21,21,21)  #151515  gray(21)
324,0: (159,159,159)  #9F9F9F  gray(159)
325,0: (10,10,10)  #0A0A0A  gray(10)
326,0: (0,0,0)  #000000  gray(0)
327,0: (0,0,0)  #000000  gray(0)
[....]
397,0: (0,0,0)  #000000  gray(0)
398,0: (0,0,0)  #000000  gray(0)
399,0: (0,0,0)  #000000  gray(0)

如您所见,从文本中检测到的边缘也会影响像素的灰度值。所以我们可以在我们的命令中引入一个额外的-threshold 50% 操作,以获得纯黑白输出:

convert canny-edges.png -resize 400x1\!   -threshold 50% canny-1col.txt
convert canny-edges.png -resize   1x300\! -threshold 50% canny-1row.txt

这里就不引用新文本文件的内容了,有兴趣的可以自己试试看。相反,我会做一个捷径:我会将像素颜色值的文本表示输出到<stdout>,并直接对所有非黑色像素进行 grep:

convert canny-edges.png -resize 400x1\!   -threshold 50% txt:- \
| grep -v black

  # ImageMagick pixel enumeration: 400,1,255,srgb
  76,0: (255,255,255)  #FFFFFF  white
  78,0: (255,255,255)  #FFFFFF  white
  322,0: (255,255,255)  #FFFFFF  white
  324,0: (255,255,255)  #FFFFFF  white

convert canny-edges.png -resize   1x300\! -threshold 50% txt:- \
| grep -v black

  # ImageMagick pixel enumeration: 1,300,255,srgb
  0,39: (255,255,255)  #FFFFFF  white
  0,41: (255,255,255)  #FFFFFF  white
  0,229: (255,255,255)  #FFFFFF  white
  0,231: (255,255,255)  #FFFFFF  white

从上面的结果可以得出结论,四个像素坐标 另一张图片内的注释是:

    左下角:(323|40) 右上角:(77|230)

区域的宽度为 246 像素,高度为 190 像素。

(ImageMagick 假定其坐标系的原点位于图像的左上角。)

现在要从原始图像中剪切便笺,您可以这样做:

convert http://i.stack.imgur.com/SxrwG.png[246x190+77+40] sticky-note.png

更多探索选项

autotrace

通过将中间的“canny-edges.png”转换为 SVG 矢量图形,例如通过@987654347 运行它,您可以进一步简化上述过程(如果需要,甚至可以将其转换为自动运行的脚本) @...

如果您的便签倾斜或旋转,这可能会很有用。

霍夫线检测

一旦你有了“canny”线,你也可以对它们应用霍夫线检测算法:

convert              \
  canny-edges.png    \
 -background black   \
 -stroke red         \
 -hough-lines 5x5+20 \
  lines.png

请注意,-hough-lines 运算符将检测到的线从原始图像的一个边缘(具有浮点值)延伸并绘制到另一边缘。

虽然前面的命令最终将行转换为 PNG,但 -hough-lines 运算符确实会生成 MVG 文件(Magick Vector Graphics) 内部。这意味着您实际上可以阅读 MVG 文件的源代码,并确定“红线”图像中描绘的每一行的数学参数:

convert              \
  canny-edges.png    \
 -hough-lines 5x5+20 \
  lines.mvg

这更复杂,也适用于不是严格水平和/或垂直的边缘。

但您的示例图像确实使用水平和垂直边缘,因此您甚至可以使用简单的 shell 命令来发现这些。

生成的MVG文件共有80行描述。您可以识别该文件中的所有水平线

cat lines.mvg                              \
 | while read a b c d e ; do               \
     if [ x$b/0,/ == x$c/400,/ ]; then \
       echo "$a    $b    $c   $d    $e" ;  \
     fi;                                   \
   done

    line     0,39.5    400,39.5    # 249
    line     0,62.5    400,62.5    # 48
    line     0,71.5    400,71.5    # 52
    line     0,231.5   400,231.5   # 249

现在识别所有垂直线

cat lines.mvg                              \
 | while read a b c d e; do                \
     if [ x$b/,0/ == x$c/,300 ]; then  \
        echo "$a    $b    $c   $d    $e" ; \
     fi;                                   \
   done

   line     76.5,0   76.5,300     # 193
   line    324.5,0  324.5,300     # 193

【讨论】:

@navinpai:另请参阅我关于“更多探索选项”的更新【参考方案2】:

上周我遇到了类似的检测图像边界(空白)的问题,花了很多时间尝试各种方法和工具,最终使用熵差计算方法解决了这个问题,所以这里是 JFYI 算法。

假设您想检测您的 200x100px 图像顶部是否有边框:

    获取图片上部25%高度(25px)(0:25,0:200) 从上片端开始,到图像中心更深(25:50、0:200)获取具有相同高度的下片

upper and lower pieces depicted

    计算两部分的熵 查找熵差并将其与当前块高度一起存储 使上半部分减少 1 像素(24 像素)并从第 2 页开始重复,直到我们到达图像边缘(高度 0) - 每次迭代都调整扫描区域的大小,从而向上滑动到图像边缘 找到存储的熵差的最大值及其块高度 - 如果它更靠近边缘而不是图像中心并且最大熵差高于预设阈值(例如 0.5),则这是我们边界的中心)

并将此算法应用于图像的每一面。

这是一段代码,用于检测图像是否有上边框并找到其近似坐标(从顶部偏移),将灰度('L'模式)枕头图像传递给扫描函数:

import numpy as np


MEDIAN = 0.5


def scan(im):
    w, h = im.size
    array = np.array(im)

    center_ = None
    diff_ = None
    for center in reversed(range(1, h // 4 + 1)):
        upper = entropy(array[0: center, 0: w].flatten())
        lower = entropy(array[center: 2 * center, 0: w].flatten())
        diff = upper / lower if lower != 0.0 else MEDIAN
        if center_ is None or diff_ is None:
            center_ = center
            diff_ = diff
        if diff < diff_:
            center_ = center
            diff_ = diff
    top = diff_ < MEDIAN and center_ < h // 4, center_, diff_

带有已处理边框和清晰(无边框)图像示例的完整源代码在此处:https://github.com/embali/enimda/

【讨论】:

嗨,欢迎来到 Stack Overflow!您应该将链接中最重要的部分复制到您的答案中,以使其更有帮助。 :D

以上是关于查找图像内的边缘(矩形的边界)的主要内容,如果未能解决你的问题,请参考以下文章

Python - opencv 在 Canny 边缘图像周围绘制边界框

处理图像并找到外边缘。查找算法

OpenCV 锐化边缘(没有孔的边缘)

图像轮廓之查找并绘制轮廓

图像处理常用边缘检测算子总结

图像处理常用边缘检测算子总结