【问题标题】:How to split an image by identifying single or multiple horizontal lines in Python?如何通过在 Python 中识别单个或多个水平线来分割图像?
【发布时间】:2021-06-30 10:01:25
【问题描述】:

我想使用 Python 根据问题之间的微弱灰线将图像拆分为多个部分。(如下图所示)。有没有办法这样做?

【问题讨论】:

    标签: python image opencv image-segmentation


    【解决方案1】:

    您可以创建水平线的遮罩,然后使用cv2.reduce 将图像缩小为使用@987654328 的 @ 价值。通过检测轮廓,您可以计算缩小蒙版中线条的起始垂直坐标,最后,crop 使用此信息的图像。像这样的:

    # Set image path
    imagePath = "D://opencvImages//"
    imageName = "zlSGu.jpg"
    
    # Read image:
    inputImage = cv2.imread(imagePath + imageName)
    # Store a copy for results:
    inputCopy = inputImage.copy()
    
    # Convert BGR to grayscale:
    grayInput = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)
    
    # Set a lower and upper range for the threshold:
    lowerThresh = 230
    upperThresh = 235
    
    # Get the lines mask:
    mask = cv2.inRange(grayInput, lowerThresh, upperThresh)
    

    这会给你线条掩码:

    有点吵,你的图片被压缩了。让我们应用一个最小面积为 50 的areaFilter 来过滤掉这个噪音:

    # Set a filter area on the mask:
    minArea = 50
    mask = areaFilter(minArea, mask)
    

    这是过滤后的掩码:

    现在,使用MAX (255) 强度值将图像缩小为一列:

    # Reduce matrix to a n row x 1 columns matrix:
    reducedImage = cv2.reduce(mask, 1, cv2.REDUCE_MAX)
    

    这是缩小的图像,在这里有点难以看到,但只显示了灰线(缩小为一列)。现在,让我们检测这些线的起点和终点——它们实际上只是一个垂直坐标。我们可以从线的边界框计算这个坐标:

    # Find the big contours/blobs on the filtered image:
    contours, hierarchy = cv2.findContours(mask, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
    
    # Store the lines here:
    separatingLines = []
    
    # We need some dimensions of the original image:
    imageHeight = inputCopy.shape[0]
    imageWidth = inputCopy.shape[1]
    
    # Look for the outer bounding boxes:
    for _, c in enumerate(contours):
    
        # Approximate the contour to a polygon:
        contoursPoly = cv2.approxPolyDP(c, 3, True)
    
        # Convert the polygon to a bounding rectangle:
        boundRect = cv2.boundingRect(contoursPoly)
    
        # Get the bounding rect's data:
        [x, y, w, h] = boundRect
    
        # Start point and end point:
        lineCenter = y + (0.5 * h)
        startPoint = (0,int(lineCenter))
        endPoint = (int(imageWidth), int(lineCenter))
    
        # Store the end point in list:
        separatingLines.append( endPoint )
    
        # Draw the line using the start and end points:
        color = (0, 255, 0)
        cv2.line(inputCopy, startPoint, endPoint, color, 2)
    
        # Show the image:
        cv2.imshow("inputCopy", inputCopy)
        cv2.waitKey(0)
    

    我还在separatingLines 列表中存储了该行的数据。此外,仅出于显示目的,我在原始输入上绘制了线条。这是已识别线的图像:

    现在,这些行未排序。让我们根据他们的垂直坐标sort他们。行正确排序后,我们可以在循环遍历行列表时crop 每个部分。像这样:

    # Sort the list based on ascending Y values:
    separatingLines = sorted(separatingLines, key=lambda x: x[1])
    
    # The past processed vertical coordinate:
    pastY = 0
    
    # Crop the sections:
    for i in range(len(separatingLines)):
    
        # Get the current line width and starting y:
        (sectionWidth, sectionHeight) = separatingLines[i]
    
        # Set the ROI:
        x = 0
        y = pastY
        cropWidth = sectionWidth
        cropHeight = sectionHeight - y
    
        # Crop the ROI:
        currentCrop = inputImage[y:y + cropHeight, x:x + cropWidth]
        cv2.imshow("Current Crop", currentCrop)
        cv2.waitKey(0)
    
        # Set the next starting vertical coordinate:
        pastY = sectionHeight
    

    这些是图像的裁剪部分。请注意,这些是单独的图像:

    这是areaFilter函数的定义和实现:

    def areaFilter(minArea, inputImage):
        # Perform an area filter on the binary blobs:
        componentsNumber, labeledImage, componentStats, componentCentroids = \
        cv2.connectedComponentsWithStats(inputImage, connectivity=4)
    
        # Get the indices/labels of the remaining components based on the area stat
        # (skip the background component at index 0)
        remainingComponentLabels = [i for i in range(1, componentsNumber) if componentStats[i][4] >= minArea]
    
        # Filter the labeled pixels based on the remaining labels,
        # assign pixel intensity to 255 (uint8) for the remaining pixels
        filteredImage = np.where(np.isin(labeledImage, remainingComponentLabels) == True, 255, 0).astype('uint8')
    
        return filteredImage
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-15
      • 2019-02-01
      • 2012-05-15
      • 1970-01-01
      相关资源
      最近更新 更多