繁体   English   中英

如何从图像中删除或清除轮廓?

[英]How to delete or clear contours from image?

我正在使用车牌,我所做的是对其应用一系列过滤器,例如:

  1. 灰度
  2. 模糊
  3. 门槛
  4. 二进制

问题是当我这样做时,边界上有一些像这张图片一样的轮廓,我该如何清除它们? 或者让它只是黑色(蒙面)? 我使用了这段代码,但有时它会掉下来。

# invert image and detect contours
inverted = cv2.bitwise_not(image_binary_and_dilated)
contours, hierarchy = cv2.findContours(inverted,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

# get the biggest contour
biggest_index = -1
biggest_area = -1
i = 0
for c in contours:
    area = cv2.contourArea(c)
    if area > biggest_area:
        biggest_area = area
        biggest_index = i
    i = i+1

print("biggest area: " + str(biggest_area) + " index: " + str(biggest_index))

cv2.drawContours(image_binary_and_dilated, contours, biggest_index, [0,0,255])
center, size, angle = cv2.minAreaRect(contours[biggest_index])

rot_mat = cv2.getRotationMatrix2D(center, angle, 1.)

#cv2.warpPerspective()
print(size)
dst = cv2.warpAffine(inverted, rot_mat, (int(size[0]), int(size[1])))

mask = dst * 0
x1 = max([int(center[0] - size[0] / 2)+1, 0])
y1 = max([int(center[1] - size[1] / 2)+1, 0])
x2 = int(center[0] + size[0] / 2)-1
y2 = int(center[1] + size[1] / 2)-1

point1 = (x1, y1)
point2 = (x2, y2)
print(point1)
print(point2)

cv2.rectangle(dst, point1, point2, [0,0,0])
cv2.rectangle(mask, point1, point2, [255,255,255], cv2.FILLED)

masked = cv2.bitwise_and(dst, mask)

#cv2_imshow(imgg)
cv2_imshow(dst)
cv2_imshow(masked)
#cv2_imshow(mask)

一些结果:

在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

原来的盘子是:

  1. 好结果 1
  2. 好成绩2
  3. 好结果 3
  4. 好成绩 4
  5. 结果不好 1
  6. 坏结果 2

二进制板是:

  1. 图 1
  2. 图 2
  3. 图 3
  4. 图 4
  5. 图 5 - 坏结果 1
  6. 图 6 - 不良结果 2

如何修复此代码? 只是我想避免那个糟糕的结果或改进它。

介绍

你的问题开始变得复杂,我相信不再有正确错误的答案,只有不同的方法来做到这一点。 几乎所有这些都会产生积极和消极的结果,很可能是不同的比率。 获得 100% 的肯定结果是一项非常具有挑战性的任务,我相信我的答案没有达到。 然而,它可以成为实现这一目标的更复杂工作的基础。

我的建议

所以,我想在这里提出一个不同的建议。 我不是 100% 确定您为什么要执行所有步骤,我相信其中一些可能是不必要的。 让我们从问题开始:您要删除边框上的白色部分(不是数字)。 因此,我们需要了解如何将它们与字母区分开来,以便正确处理它们。 如果我们只是尝试进行轮廓和变形,它可能适用于某些图像而不适用于其他图像,因为并非所有图像看起来都相同。 这是拥有适用于许多图像的通用解决方案的最困难的问题。

数字的特征和边界的特征(和其他小点?)有什么区别:在考虑之后,我会说:形状,这意味着,如果你想象一个字母周围的边界框/数字,它看起来像一个矩形。 其大小与图像大小有关,而在边框的情况下,它们通常又大又窄。 或太小而不能被视为字母/数字(随机点)。

因此,我的猜测是分割,通过形状划分特征。 所以我们拍摄二进制图像,我们使用轴上的投影删除一些部分(正如您在上一个问题中正确提出的,我相信我们应该使用),我们得到一个图像,其中每个字母都与白色边框分开。 然后我们可以分割并检查每个分割的object的形状,如果我们认为这些是字母,我们保留它们,否则我们丢弃它们。

编码

我之前编写了代码作为您数据的示例。 在这组图像上调整了一些参数,因此对于更大的数据集可能必须放宽它们。

import cv2
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
import scipy.ndimage as ndimage

# do this for all the images
num_images = 6
plt.figure(figsize=(16,16))
for k in range(num_images):

    # read the image
    binary_image = cv2.imread("binary_image/img{}.png".format(k), cv2.IMREAD_GRAYSCALE)
    # just for visualization purposes, I create another image with the same shape, to show what I am doing
    new_intermediate_image = np.zeros((binary_image.shape), np.uint8)
    new_intermediate_image += binary_image
    # here we will copy only the cleaned parts
    new_cleaned_image = np.zeros((binary_image.shape), np.uint8)

    ### THIS CODE COMES FROM THE PREVIOUS ANSWER: 
    # https://stackoverflow.com/questions/62127537/how-to-clean-binary-image-using-horizontal-projection?noredirect=1&lq=1
    (rows,cols)=binary_image.shape
    h_projection = np.array([ x/rows for x in binary_image.sum(axis=0)])
    threshold_h = (np.max(h_projection) - np.min(h_projection)) / 10
    print("we will use threshold {} for horizontal".format(threshold))
    # select the black areas
    black_areas_horizontal = np.where(h_projection < threshold_h)
    for j in black_areas_horizontal:
        new_intermediate_image[:, j] = 0

    v_projection = np.array([ x/cols for x in binary_image.sum(axis=1)])
    threshold_v = (np.max(v_projection) - np.min(v_projection)) / 10
    print("we will use threshold {} for vertical".format(threshold_v))
    black_areas_vertical = np.where(v_projection < threshold_v)
    for j in black_areas_vertical:
        new_intermediate_image[j, :] = 0
    ### UNTIL HERE

    # define the features we are looking for
    # this parameters can also be tuned
    min_width = binary_image.shape[1] / 14
    max_width = binary_image.shape[1] / 2
    min_height = binary_image.shape[0] / 5
    max_height = binary_image.shape[0]
    print("we look for feature with width in [{},{}] and height in [{},{}]".format(min_width, max_width, min_height, max_height))
    # segment the iamge
    labeled_array, num_features = ndimage.label(new_intermediate_image)

    # loop over all features found
    for i in range(num_features):
        # get a bounding box around them
        slice_x, slice_y = ndimage.find_objects(labeled_array==i)[0]
        roi = labeled_array[slice_x, slice_y]
        # check the shape, if the bounding box is what we expect, copy it to the new image
        if roi.shape[0] > min_height and \
            roi.shape[0] < max_height and \
            roi.shape[1] > min_width and \
            roi.shape[1] < max_width:
            new_cleaned_image += (labeled_array == i)

    # print all images on a grid
    plt.subplot(num_images,3,1+(k*3))
    plt.imshow(binary_image)
    plt.subplot(num_images,3,2+(k*3))
    plt.imshow(new_intermediate_image)
    plt.subplot(num_images,3,3+(k*3))
    plt.imshow(new_cleaned_image)

生成 output (在网格中,左侧图像是输入图像,中间是基于直方图投影的掩码后的图像,右侧是清理后的图像):

在此处输入图像描述 在此处输入图像描述

结论:

如上所述,这种方法不会产生 100% 的阳性结果。 最后一张图片质量较低,部分部分未连接,在此过程中丢失。 我个人认为这是为了获得更干净的图像而付出的代价,如果你有很多图像,这不是问题,你可以删除那些图像。 总的来说,我认为这种方法返回的图像非常清晰,所有其他不是字母或数字的部分都被正确删除。

优点

  • 图像很干净,只保留字母或数字

  • 参数可以调整,并且应该在图像之间保持一致

  • 如果出现问题,在选择要保留的功能的循环上使用一些打印或一些调试应该更容易理解问题出在哪里并更正它们

限制

  • 在某些情况下,如果字母和数字接触到白色边框,它可能会失败,这似乎很有可能。 它是从使用投影创建的 black_areas 处理的,但我不太相信这会在 100% 的时间内起作用。

  • 在此过程中,数字的一些小部分可能会丢失,如上一张图片所示。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM