【问题标题】:Count number of the blues lines on white background in the image计算图像中白色背景上蓝调线的数量
【发布时间】:2020-06-09 00:12:17
【问题描述】:

我有 1000 张这样的图片

我通过tutorial 尝试了 cv2 库和霍夫线变换,但我不明白这是我的情况吗?我有 1000 张图像,也就是说,我几乎无法手动输入任何数据(如宽度或坐标)。

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

【问题讨论】:

    标签: python image opencv machine-learning image-processing


    【解决方案1】:

    你可以数行数并除以二...

    #!/usr/bin/env python3
    
    import numpy as np
    from PIL import Image
    from scipy.ndimage import generic_filter
    
    # Line ends filter
    def lineEnds(P):
        global ends
        # Central pixel and one other must be 255 for line end
        if (P[4]==255) and np.sum(P)==510:
            ends += 1
            return 255
        return 0
    
    # Global count of line ends
    ends = 0
    
    # Open image and make into Numpy array
    im = Image.open('lines.png').convert('L')
    im = np.array(im)
    
    # Invert and threshold for white lines on black
    im = 255 - im
    im[im>0] = 255
    
    # Save result, just for debug
    Image.fromarray(im).save('intermediate.png')
    
    # Find line ends
    result = generic_filter(im, lineEnds, (3, 3))
    
    print(f'Line ends: {ends}')
    
    # Save result, just for debug
    Image.fromarray(result).save('result.png')
    

    输出

    Line ends: 16
    

    请注意,这不是生产质量代码。您应该添加额外的检查,例如线端的总数是偶数,并在边缘周围添加 1 像素宽的黑色边框以防线接触边缘等等。

    【讨论】:

    • 除以二的好方法
    【解决方案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))
    
                # https://stackoverflow.com/questions/22801545/opencv-merge-contours-together
                # 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()
    

    结果:

    Number of lines = 8

    thresh_gray(拆分前):

    thresh_gray(拆分后):

    im:

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

    【讨论】:

    • 但它是关于计算行数的吗?与相交的线如何计算?
    • @YunusTemurlenk 是的,我相信在二进制图像上计算行数有一个已知的解决方案(使用霍夫线变换)。
    猜你喜欢
    • 1970-01-01
    • 2016-06-08
    • 1970-01-01
    • 1970-01-01
    • 2020-11-27
    • 1970-01-01
    • 2020-04-03
    • 2017-07-09
    • 1970-01-01
    相关资源
    最近更新 更多