обнаруживать флажки в форме с помощью opencv python - PullRequest
3 голосов
/ 08 июля 2020

учитывая стоматологическую форму в качестве входных данных, необходимо найти все флажки, присутствующие в форме, с помощью обработки изображений. Я ответил на свой текущий подход ниже. Есть ли лучший способ найти флажки для документов низкого качества?

пример ввода:

замаскированное входное изображение

Ответы [ 2 ]

7 голосов
/ 08 июля 2020

Это один из подходов, который позволяет решить проблему:

import cv2
import numpy as np
image=cv2.imread('path/to/image.jpg')

### binarising image
gray_scale=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
th1,img_bin = cv2.threshold(gray_scale,150,225,cv2.THRESH_BINARY)

binary

Defining vertical and horizontal kernels

lineWidth = 7
lineMinWidth = 55
kernal1 = np.ones((lineWidth,lineWidth), np.uint8)
kernal1h = np.ones((1,lineWidth), np.uint8)
kernal1v = np.ones((lineWidth,1), np.uint8)

kernal6 = np.ones((lineMinWidth,lineMinWidth), np.uint8)
kernal6h = np.ones((1,lineMinWidth), np.uint8)
kernal6v = np.ones((lineMinWidth,1), np.uint8)

Обнаружение горизонтальных линий

img_bin_h = cv2.morphologyEx(~img_bin, cv2.MORPH_CLOSE, kernal1h) # bridge small gap in horizonntal lines
img_bin_h = cv2.morphologyEx(img_bin_h, cv2.MORPH_OPEN, kernal6h) # kep ony horiz lines by eroding everything else in hor direction

horizontal

finding vertical lines

## detect vert lines
img_bin_v = cv2.morphologyEx(~img_bin, cv2.MORPH_CLOSE, kernal1v)  # bridge small gap in vert lines
img_bin_v = cv2.morphologyEx(img_bin_v, cv2.MORPH_OPEN, kernal6v)# kep ony vert lines by eroding everything else in vert direction

vertical image

merging vertical and horizontal lines to get blocks. Adding a layer of dilation to remove small gaps

### function to fix image as binary
def fix(img):
    img[img>127]=255
    img[img<127]=0
    return img

img_bin_final = fix(fix(img_bin_h)|fix(img_bin_v))

finalKernel = np.ones((5,5), np.uint8)
img_bin_final=cv2.dilate(img_bin_final,finalKernel,iterations=1)

final binary

Apply Connected component analysis on the binary image to get the blocks required.

ret, labels, stats,centroids = cv2.connectedComponentsWithStats(~img_bin_final, connectivity=8, ltype=cv2.CV_32S)

### skipping first two stats as background
for x,y,w,h,area in stats[2:]:
    cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2)

окончательное изображение

1 голос
/ 09 июля 2020

Вы также можете использовать контуры для этой задачи.

# Reading the image in grayscale and thresholding it
Image = cv2.imread("findBox.jpg", 0)
ret, Thresh = cv2.threshold(Image, 100, 255, cv2.THRESH_BINARY)

Теперь дважды выполните растяжение и эрозию, чтобы соединить пунктирные линии внутри прямоугольников.

kernel = np.ones((3, 3), dtype=np.uint8)
Thresh = cv2.dilate(Thresh, kernel, iterations=2)
Thresh = cv2.erode(Thresh, kernel, iterations=2)

Найдите контуры на изображении с помощью флага cv2.RETR_TREE, чтобы получить все контуры с отношениями родитель-потомок. Для получения дополнительной информации об этом .

Contours, Hierarchy = cv2.findContours(Thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

Теперь обнаруживаются все поля вместе со всеми алфавитами на изображении. Мы должны удалить обнаруженные алфавиты, очень маленькие контуры (из-за шума), а также те прямоугольники, которые содержат внутри себя меньшие прямоугольники.

Для этого я запускаю для l oop итерацию по всем контуры обнаружены, и используя это l oop, я сохраняю 3 значения для каждого контура в 3 разных списках.

  • 1-е значение: Площадь контура (количество пикселей, которые охватывает контур)
  • 2-е значение: ограничивающий прямоугольник контура информация.
  • 3-е значение: отношение площади контура к площади его ограничивающего прямоугольника.
Areas = []
Rects = []
Ratios = []
for Contour in Contours:
    # Getting bounding rectangle
    Rect = cv2.boundingRect(Contour)

    # Drawing contour on new image and finding number of white pixels for contour area
    C_Image = np.zeros(Thresh.shape, dtype=np.uint8)
    cv2.drawContours(C_Image, [Contour], -1, 255, -1)
    ContourArea = np.sum(C_Image == 255)

    # Area of the bounding rectangle
    Rect_Area = Rect[2]*Rect[3]
    
    # Calculating ratio as explained above
    Ratio = ContourArea / Rect_Area
   
    # Storing data
    Areas.append(ContourArea)
    Rects.append(Rect)
    Ratios.append(Ratio)

Фильтрация нежелательных контуров:

  • Получение индексов тех контуров, которые имеют площадь меньше 3600 (пороговое значение для этого изображения) и имеют Ratio> = 0,99. Отношение определяет степень перекрытия контура с его ограничивающим прямоугольником. Поскольку в этом случае желаемые контуры имеют форму прямоугольника, это соотношение для них ожидается равным «1.0» (0,99 для сохранения порога небольшого шума).
BoxesIndices = [i for i in range(len(Contours)) if Ratios[i] >= 0.99 and Areas[i] > 3600]
  • Теперь окончательные контуры - это те из контуров с индексами «BoxesIndices», которые не имеют дочернего контура (это будет извлекать самые внутренние контуры), и если у них есть дочерний контур, то этот дочерний контур не должен быть одним из контуров с индексами «BoxesIndices» .
FinalBoxes = [Rects[i] for i in BoxesIndices if Hierarchy[0][i][2] == -1 or BoxesIndices.count(Hierarchy[0][i][2]) == 0]

Окончательное выходное изображение

...