簡體   English   中英

如何在圖像中找到矩形輪廓?

[英]how to find a rectangular contour in an image?

我目前正在開展一項從衛星圖像識別足球場的項目。

這是足球場的頂視圖衛星圖像 這是足球場的頂視圖衛星圖像

我使用mediumblur函數清除了此圖像中的所有小雜質。 后來我只選擇了圖像的綠色部分,並使用命令cv2.inRange(hsv_img,light_green,dark_green)作了蒙版,其中light_green和dark_green是我在hsv中綠色的范圍。 等我戴上口罩之后 我得到這個作為輸出:
蒙版后輸出

由於它包含一些雜質,因此我使用了mediumblur函數,其中位數= cv2.medianBlur(image,45),我得到的輸出是:
使用中模糊后

如您所見,我有很多輪廓,中間有主要的矩形輪廓。 我需要一種從圖像中選擇矩形輪廓的算法,而我不得不忽略其余的輪廓。 之后該怎么辦?

我的方法是:

  1. 從輸入圖像中找到形狀類似“正方形”或“矩形”的形狀
  2. 找到最大面積的那個。

假設輸入是您計算出的“中位數”結果:

在此處輸入圖片說明

中位數圖片(輸入):

  1. 首先,導入必要的庫並凈化圖像。

     import cv2 import numpy as np # assuming you have the result image store in median # median = cv2.imread("abc.jpg", 0) image_gray = median image_gray = np.where(image_gray > 30, 255, image_gray) image_gray = np.where(image_gray <= 30, 0, image_gray) image_gray = cv2.adaptiveThreshold(image_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 115, 1) 
  2. 找到輪廓,然后根據其形狀應用過濾器功能。

     _, contours, _ = cv2.findContours(image_gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) rect_cnts = [] for cnt in contours: peri = cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, 0.04 * peri, True) (x, y, w, h) = cv2.boundingRect(cnt) ar = w / float(h) if len(approx) == 4: # shape filtering condition rect_cnts.append(cnt) 
  3. 找到面積最大的一個,然后繪制結果。

     max_area = 0 football_square = None for cnt in rect_cnts: (x, y, w, h) = cv2.boundingRect(cnt) if max_area < w*h: max_area = w*h football_square = cnt # Draw the result image = cv2.cvtColor(image_gray, cv2.COLOR_GRAY2RGB) cv2.drawContours(image, [football_square], -1, (0, 0,255), 5) cv2.imshow("Result Preview", image) cv2.waitKey() 

結果預覽:

在此處輸入圖片說明

這個答案來自PyImageSearch上的一篇很棒的文章“ OpenCV形狀檢測 ”。 謝謝。

因此,請注意:

  1. 我相信有更好的方法可以做到這一點(可能有一個圖書館,我沒有真正檢查過)。
  2. 該代碼僅在查找封閉區域(例如矩形)時有效。 使用此代碼不會(完全)找到星形,甜甜圈和其他“復雜”形狀。
  3. 這只會找到最大的單個區域,它還會返回區域列表,您可能可以編寫一些代碼來檢查特定區域是否具有正確的尺寸並且自己是否足夠矩形

該程序的工作原理如下:

首先,它讀取圖像並確定每個像素是黑色還是白色。 接下來,它按行讀取白色區域的開始和結束位置。 此后,它會聚集需要至少一個像素重疊且每個區域必須在連續線上的區域。 請注意,它僅連接每行一個區域,例如,如果您有一個星形,其中兩個部分在較低點連接,則此代碼將無法正常工作,您將需要進行一些重做(請參見以下示例)我的意思是) 最后,它檢查哪個區域最大,並在其周圍添加一條紅色的粗線。

在此處輸入圖片說明

from PIL import Image
from copy import copy


def area(lst):
    '''
    :param lst: a list of tuples where each subsequent tuple indicates a row and the first two values indicate the start and end values of the row 
    :return: the total area of the shape described by these tuples
    '''
    pixels_counted = 0
    for i in lst:
        pixels_counted += i[1] - i[0]
    return pixels_counted


def get_image(location):
    '''
    :param location: where your image is saved
    :return:
        - an Image class
        - a list of lists where everything is either a 1 (white) or 0 (black)
        - a picture class
    '''
    picture = Image.open(location)
    rgb_im = picture.convert('RGB')
    w, y = picture.size
    rgb = [[1 if sum(rgb_im.getpixel((i, j))) < 255 * 1.5 else 0 for i in range(w)] for j in range(y)]
    return picture, rgb, rgb_im


def get_borders(rgb):
    borders = []
    for i in range(len(rgb)):
        border = []
        if 0 in rgb[i]:
            start = rgb[i].index(0)
            for j in range(start, len(rgb[i])):
                if start != -1 and rgb[i][j] == 1:
                    border.append((start, j - 1, i))
                    start = -1
                if start == -1:
                    if rgb[i][j] == 0:
                        start = j
            if start != -1:
                border.append((start, j - 1, i))

        borders.append(copy(border))
    return borders


def get_rectangles(borders):
    '''
    :param borders: a list of lists, for each row it lists where an area starts or ends
    :return: a list of areas

    This function reads from the top to the bottom. it tries to group the largest areas together. This will work
    as long as these areas are relatively simple, however, if they split up (like a donut for instance) this will
    definitely raise issues.
    '''
    rectangles = []
    started = []
    for i in range(len(borders)):
        started_new = []
        started_borders = [z[1] for z in sorted([(z[1] - z[0], z) for z in borders[i]], reverse=True)]
        for region in started_borders:
            existing = False
            left = region[0]
            right = region[1]
            started_areas = [z[1] for z in sorted([(area(z), z) for z in started], reverse=True)]

            # If in the previous row an area existsed in that overlaps with this region, this region is connected to it
            for k in started_areas:
                if right < k[-1][0] or left > k[-1][1]:
                    continue
                started_new.append(k + [region])
                existing = True
                del started[started.index(k)]
            # If there was no part in the previous row that already connects to it, it will be added to the list of
            # shapes as a new area of itself
            if not existing:
                started_new.append([region])
        for k in started:
            rectangles.append(copy(k))
        started = copy(started_new)

    # Add any remaining areas to the list
    for i in started_new:
        rectangles.append(i)
    return rectangles


def get_biggest_rectangle(rectangles):
    areas = []
    for i in rectangles:
        areas.append((area(i), i))

    probable_rectangle = sorted(areas)[-1][1]
    return probable_rectangle


def show_image(rgb, rgb_im, probable_rectangle):
    # I honestly cannot figure out how to change the picture variable, so I just make a new figure
    w, y = len(rgb[0]), len(rgb)
    img = Image.new('RGB', (w, y), "black")
    pixels = img.load()

    for i in range(w):
        for j in range(y):
            pixels[i, j] = rgb_im.getpixel((i, j))  # set the colour accordingly

    for i in probable_rectangle:
        pixels[i[0], i[-1]] = (255, 0, 0)
        pixels[i[1], i[-1]] = (255, 0, 0)
        for y in range(-10, 10):
            for z in range(-10, 10):
                pixels[i[0] + y, i[-1] + z] = (255, 0, 0)
                pixels[i[1] + y, i[-1] + z] = (255, 0, 0)

    img.show()


if __name__ == '__main__':
    picture, rgb, rgb_im = get_image('C:\\Users\\Nathan\\Downloads\\stack.jpg')
    borders = get_borders(rgb)
    rectangles = get_rectangles(borders)
    probable_rectangle = get_biggest_rectangle(rectangles)
    show_image(rgb, rgb_im, probable_rectangle)

返回:

在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM