计算图像中白色背景上的蓝调线数

Дав*_*ико 7 python opencv image machine-learning image-processing

我有 1000 张这样的图片ctf capcha 样本

我在本教程中尝试了 cv2 库和 Hough Line Transform ,但我不明白这是我的情况吗?我有 1000 张图像,即我几乎不可能手动输入任何数据(如宽度或坐标)。

按照逻辑,我必须找到图像中的每个蓝色像素并检查邻居的像素是否为白色所以为此我必须知道 PNG 图像的像素格式。我必须如何读取图像,如普通文件,open (path, 'r') as file_object或者它必须是某种带有库的特殊方法?

Rot*_*tem 2

乍一看,问题看起来很简单 - 转换为二进制图像,使用Hough Line Transform,并计算行数,但它不起作用......

注意:
我找到的解决方案是基于查找和合并轮廓,但使用霍夫变换可能更稳健。
您可能会发现许多短线,而不是合并轮廓,并根据接近的角度和边缘接近度将它们合并为长线。

下面的解决方案使用以下阶段:

  • 将图像转换为黑色背景上带有白线的二值图像。
  • 分割线之间的交叉点(用黑色填充交叉点)。
  • 查找二值图像中的轮廓(并删除小轮廓)。
  • 合并具有闭合角度和闭合边缘的轮廓。

这是一个工作代码示例:

import cv2
import numpy as np


def box2line(box):
    """Convert rotated rectangle box into two array of two points that defines a line"""
    b = box.copy()
    for i in range(2):
        p0 = b[0]
        dif0 = (b[1:, 0] - p0[0])**2 + (b[1:, 1] - p0[1])**2
        min_idx = np.argmin(dif0, 0)
        b = np.delete(b, min_idx+1, 0)
    return b


def minlinesdist(line, line2):
    """Finds minimum distance between any two edges of two lines"""
    a0 = line[0, :]
    a1 = line[1, :]
    b0 = line2[0, :]
    b1 = line2[1, :]
    d00 = np.linalg.norm(a0 - b0)
    d01 = np.linalg.norm(a0 - b1)
    d10 = np.linalg.norm(a1 - b0)
    d11 = np.linalg.norm(a1 - b1)
    min_dist = np.min((d00, d01, d10, d11))
    return min_dist


def get_rect_box_line_and_angle(c):
    """Return minAreaRect, boxPoints, line and angle of contour"""
    rect = cv2.minAreaRect(c)
    box = cv2.boxPoints(rect)
    line = box2line(box)
    angle = rect[2]
    return rect, box, line, angle



(cv_major_ver, cv_minor_ver, cv_subminor_ver) = (cv2.__version__).split('.')  # Get version of OpenCV

im = cv2.imread('BlueLines.png')  # Read input image


# Convert image to binary image with white lines on black background
################################################################################
gray = im[:, :, 1]  # Get only the green color channel (the blue lines should be black).

# Apply threshold
ret, thresh_gray = cv2.threshold(gray, 10, 255, cv2.THRESH_BINARY)

# Invert polarity
thresh_gray = 255 - thresh_gray
################################################################################


# Split intersection points between lines (fill crossing points with black).
################################################################################
thresh_float = thresh_gray.astype(float) / 255  # Convert to float with range [0, 1]
thresh_float = cv2.filter2D(thresh_float, -1, np.ones((3, 3)))  # Filter with ones 5x5

# Find pixels with "many" neighbors
thresh_intersect = np.zeros_like(thresh_gray)
thresh_intersect[(thresh_float > 3)] = 255;  # Image of intersection points only.

thresh_gray[(thresh_float > 3)] = 0;
################################################################################


# Find contours in thresh_gray, and remove small contours.
################################################################################
if int(cv_major_ver) < 4:
    _, contours, _ = cv2.findContours(thresh_gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
else:
    contours, _ = cv2.findContours(thresh_gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)


# Remove small contours, because their angle is not well defined
fcontours = []
for i in range(len(contours)):
    c = contours[i]
    if c.shape[0] > 6:  # Why 6?
        fcontours.append(c)

contours = fcontours

# Starting value.
n_lines = len(contours)
################################################################################


# Merge contours with close angles, and close edges
# Loop decreases n_lines when two lines are merged.
# Note: The solution is kind of "brute force" solution, and can be better.
################################################################################
# https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_contours/py_contour_features/py_contour_features.html
# Fitting a Line
rows,cols = im.shape[:2]
for i in range(len(contours)):
    c = contours[i]
    rect, box, line, angle = get_rect_box_line_and_angle(c)

    for j in range(i+1, len(contours)):
        c2 = contours[j]
        rect2 = cv2.minAreaRect(c2)
        box2 = cv2.boxPoints(rect2)
        line2 = box2line(box2)
        angle2 = rect2[2]
        angle_diff = (angle - angle2 + 720) % 180  # Angle difference in degrees (force it to be positive number in range [0, 180].
        angle_diff = np.minimum(angle_diff, 180 - angle_diff)
        min_dist = minlinesdist(line, line2)  # Minimum distance between any two edges of line and line2

        if (angle_diff < 3) and (min_dist < 20):
            color = (int((i+3)*100 % 255),int((i+3)*50 % 255), int((i+3)*70 % 255))

            # /sf/ask/1596108181/
            # Merge contours together
            tmp = np.vstack((c, c2))
            c = cv2.convexHull(tmp)

            # Draw merged contour (for testing)
            im = cv2.drawContours(im, [c], 0, color, 2)

            # Replace contour with merged one.
            contours[j] = c

            n_lines -= 1 # Subtract lines counter

            break
################################################################################

print('Number of lines = {}'.format(n_lines))

# Display result (for testing):
cv2.imshow('thresh_gray', thresh_gray)
cv2.imshow('im', im)
cv2.waitKey(0)
cv2.destroyAllWindows()
Run Code Online (Sandbox Code Playgroud)

结果:

Number of lines = 8

thresh_gray(分割前):
在此输入图像描述

thresh_gray(分割后):
在此输入图像描述

im:
在此输入图像描述

注意:
我知道该解决方案并不完美,并且不会在所有 1000 张图像上找到完美的结果。
我认为有一个更好的改变,使用霍夫变换和合并线将给出完美的结果。