Как удалить внешние полые контуры с изображения, не затрагивая внутренние контуры - PullRequest
2 голосов
/ 19 октября 2019

Это продолжение проблемы при другом подходе.

Я хочу избавиться от коробок и всего, что находится за его пределами, и оставить все, что находится внутри коробки,как есть. Или, по крайней мере, только поле.

Поле:

img

Как я хочу, чтобы это выглядело после некоторой магии:

img

Вот откуда он был обрезан:

img

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

Это наивный и мой первый подход. Проблема в следующем:

  • Он не может учитывать слишком большую ширину линии, не влияя на символы внутри. Скажем, вы добавили -9 в качестве отступа. Он будет работать в основном достаточно хорошо для блоков с шириной линии ~ 9px, но все, что находится за пределами ~ 9px, остается внутриЭто вызывает некоторые оставшиеся пиксели, что в конечном итоге влияет на точность моего приложения.
  • С другой стороны, любое поле в штучной упаковке с шириной линии, значительно меньшей, чем 9, может также уничтожить символы внутри.

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

Код для извлечения контуров очень длинный, поэтому предположим, что stats содержит contours, возвращенный из contours, _ = cv2.findContours

# loop in each contour in stats
for i in range(len(stats)):
    # get the stats of the bounding rectangle 
    x, y, w, h = cv2.boundingRect(stats[i])

    # draw the field contour into the mask
    cv2.drawContours(mask, [stats[i]], -1, 0, -1)

    # remove the contour from the original image
    section = cv2.bitwise_and(section, section, mask=mask)

    # crop the boxed field
    field = crop_by_origin(x, y, w, h, padding=p)

Вот что я получаю:

img

Я не понимаю, почему это не сработало? Может, потому что на сайте его пример на черном фоне? Может быть, это не работает с "прозрачными" контурами? Это вообще возможно?

Как это исправить? Любые другие возможные решения здесь?

ОБНОВЛЕНИЕ

Я попробовал другое изображение с ответом @ nathancy, и вот что пришло:

img

Результат:

img

Я пытался поиграться с ядром горизонтальной линии, но это не сработало, как задумано, есть ли способ сделать это более динамичным?

1 Ответ

2 голосов
/ 19 октября 2019

Эта проблема может быть разбита на два отдельных этапа. Во-первых, мы хотим выделить прямоугольники, что можно сделать с помощью аппроксимации контура + фильтрации. Затем мы удаляем горизонтальные и вертикальные линии с реализацией, заимствованной из предыдущего ответа в , удаляя горизонтальные линии в изображении . Вот общий подход:

  • Преобразование изображения в оттенки серого и размытие по Гауссу
  • Порог Оцу для получения двоичного изображения
  • Поиск контуров и использование аппроксимации контуров для поискапрямоугольные прямоугольники

    • Если контур проходит этот фильтр и область минимального порога, используйте Numpy Slice для копирования и вставки области интереса на пустую белую маску
  • Удалите горизонтальные линии

  • Удалите вертикальные линии
  • Битовый - и маска с входным изображением для получения результата

Порог Оцу -> Обнаруженопрямоугольники -> прямоугольники, нарисованные на маске -> перевернутые

image image image image

удаление горизонтальных линий -> удаление вертикальных линий -> поразрядно-и получить результат

image image image

import cv2
import numpy as np

image = cv2.imread('1.png')
mask = np.ones(image.shape, dtype=np.uint8) * 255
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (7,7), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.05 * peri, True)
    if len(approx) == 4 and area > 500:
        x,y,w,h = cv2.boundingRect(approx)
        mask[y:y+h, x:x+w] = image[y:y+h, x:x+w]

# Remove horizontal lines
mask = cv2.cvtColor(255 - mask, cv2.COLOR_BGR2GRAY)
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (50,2))
detect_horizontal = cv2.morphologyEx(mask, cv2.MORPH_OPEN, horizontal_kernel, iterations=1)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(mask, [c], -1, (0,0,0), 6)

# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,45))
detect_vertical = cv2.morphologyEx(mask, cv2.MORPH_OPEN, vertical_kernel, iterations=1)
cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(mask, [c], -1, (0,0,0), 6)

# Bitwise mask with input image
result = cv2.bitwise_and(image, image, mask=mask)
result[mask==0] = (255,255,255)

cv2.imshow('mask', mask)
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.waitKey()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...