检测图像中的水平线

Posted

技术标签:

【中文标题】检测图像中的水平线【英文标题】:Detecting a horizontal line in an image 【发布时间】:2020-09-05 15:15:53 【问题描述】:

问题: 我正在使用一个包含许多看起来像这样的图像的数据集:

现在我需要将所有这些图像水平或垂直定向,以便调色板位于图像的底部或右侧。这可以通过简单地旋转图像来完成,但棘手的部分是确定哪些图像应该旋转,哪些不应该。

我尝试过的:

我认为最好的方法是检测将调色板与图像分开的白线。我决定旋转所有底部有调色板的图像,使其位于右侧。

# yes I am mixing between PIL and opencv (I like the PIL resizing more)
# resize image to be 128 by 128 pixels
img = img.resize((128, 128), PIL.Image.BILINEAR)
img = np.array(img)

# perform edge detection, not sure if these are the best parameters for Canny
edges = cv2.Canny(img, 30, 50, 3, apertureSize=3)

has_line = 0

# take numpy slice of the area where the white line usually is 
# (not always exactly in the same spot which probably has to do with the way I resize my image) 
for line in edges[75:80]:

    # check if most of one of the lines contains white pixels
    counts = np.bincount(line)
    if np.argmax(counts) == 255:
        has_line = True

# rotate if we found such a line
if has_line == True:
    s = np.rot90(s)

一个正常工作的例子:

一个不正常工作的例子:

这可能适用于 98% 的图像,但在某些情况下,它会旋转不应旋转的图像或不旋转应旋转的图像。也许有一种更简单的方法可以做到这一点,或者可能有一种更复杂、更一致的方法?我可以手动完成,但我要处理很多图像。感谢您的帮助和/或 cmets。

以下是我的代码在测试时失败的一些图片:

【问题讨论】:

所有图像中彩条的数量是否始终相同? 是的,总是 6 种颜色 是否可以假设对于所有相同类型的图像,白条始终位于相同位置? 是的,但它们并不完全是像素完美的。我可能可以检测到图像底部是否有 5 个垂直条并这样做。 我这样做的方法是为白色区域(水平和垂直)创建两个蒙版,在图像和全白图像之间的蒙版区域中进行绝对差异(基本上,我只会得到带有白条的区域的差异图像),将所有像素增量相加并选择差异最小的那个。 (注意:我在这里写这个而不是作为答案,因为我现在没有时间/机会将其进一步阐述为完整的答案) 【参考方案1】:

您可以通过设置一个非常高的阈值(例如 250)来对图像进行阈值处理,以利用线条为白色的特性。这将使所有背景变黑。现在创建一个特殊的水平内核,其形状类似于(1, 15),并用它腐蚀你的图像。这将做的是从图像中删除垂直线,只留下水平线。

import cv2
import numpy as np

img = cv2.imread('horizontal2.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY)

kernel_hor = np.ones((1, 15), dtype=np.uint8)
erode = cv2.erode(thresh, kernel_hor)

如问题中所述,色板只能位于右侧或底部。所以我们可以测试检查正确区域有多少轮廓。为此,只需将图像分成两半并取正确的部分。在找到轮廓之前,先扩大结果以用正常的(3, 3) 内核填充任何空白。使用cv2.RETR_EXTERNAL查找轮廓并计算我们找到了多少,如果大于某个数字,则图像正确的一面朝上,无需旋转。

right = erode[:, erode.shape[1]//2:]

kernel = np.ones((3, 3), dtype=np.uint8)
right = cv2.dilate(right, kernel)

cnts, _ = cv2.findContours(right, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(cnts) > 3:
    print('No need to rotate')
else:
    print('rotate')
    #ADD YOUR ROTATE CODE HERE

附:我测试了您提供的所有四张图片,效果很好。如果它不适用于任何图像,请告诉我。

【讨论】:

以上是关于检测图像中的水平线的主要内容,如果未能解决你的问题,请参考以下文章

Python 如何使用 HoughLines 和 OpenCV 检测图像中的垂直线和水平线?

Canny 边缘检测未检测到 100% 水平/未旋转的线

5.2 基本边缘检测算子—Sobel

检测水平和垂直边缘opencv

如何使用带有水平线的谷歌视觉检测日语单词或使用 TEXT_DETECTION 检测文件中的文本 (PDF)

删除图像中的水平线(OpenCV、Python、Matplotlib)