Найдите область с содержимым и получите ее прямоугольник

Я использую OpenCV 4 — python 3 — для поиска определенной области на черно-белом изображении.

Эта область не является заполненной на 100% фигурой. Это может привести к некоторым пробелам между белыми линиями.

Это базовое изображение, с которого я начинаю обработку: base

Это прямоугольник, который я ожидаю, сделанный с помощью фотошопа: expected

Результаты, которые я получил с помощью нескольких линий преобразования - неточные - неправильный результат

В общем, я начинаю с первого изображения и ожидаю найти то, что вы видите на втором.

Есть идеи, как получить прямоугольник второго изображения?


person Karliky    schedule 20.10.2019    source источник
comment
Пожалуйста, подумайте о том, чтобы пройти экскурсию и прочитать информационные руководства в справочном центре (stackoverflow.com/help). Пользователи с большей вероятностью помогут, если вы (1) проявите некоторые исследовательские усилия самостоятельно и (2) научитесь задавать вопросы. Смотрите мой ответ ниже.   -  person fmw42    schedule 21.10.2019


Ответы (3)


В Python/OpenCV вы можете использовать морфологию, чтобы соединить все белые части вашего изображения, а затем получить внешний контур. Обратите внимание, что я изменил ваше изображение, чтобы удалить части вверху и внизу экрана.

import cv2
import numpy as np

# read image as grayscale
img = cv2.imread('blackbox.png')

# convert to grayscale
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# threshold
_,thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY)

# apply close to connect the white areas
kernel = np.ones((75,75), np.uint8)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)

# get contours (presumably just one around the outside) 
result = img.copy()
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
for cntr in contours:
    x,y,w,h = cv2.boundingRect(cntr)
    cv2.rectangle(result, (x, y), (x+w, y+h), (0, 0, 255), 2)

# show thresh and result    
cv2.imshow("thresh", thresh)
cv2.imshow("Bounding Box", result)
cv2.waitKey(0)
cv2.destroyAllWindows()

# save resulting images
cv2.imwrite('blackbox_thresh.png',thresh)
cv2.imwrite('blackbox_result.png',result)


Вход:

введите здесь описание изображения

Изображение после морфологии:

введите здесь описание изображения

Результат:

введите здесь описание изображения

person fmw42    schedule 20.10.2019
comment
Это работает как шарм, я бы никогда не предложил такой способ решения проблемы, спасибо! - person Karliky; 21.10.2019

Я хотел бы представить подход, который может быть менее затратным в вычислительном отношении, чем решение в ответе fmw42 только с использованием NumPy nonzero. В основном находятся все ненулевые индексы по обеим осям, а затем получаются минимумы и максимумы. Поскольку у нас здесь бинарные изображения, этот подход работает очень хорошо.

Давайте посмотрим на следующий код:

import cv2
import numpy as np

# Read image as grayscale; threshold to get rid of artifacts
_, img = cv2.threshold(cv2.imread('images/LXSsV.png', cv2.IMREAD_GRAYSCALE), 0, 255, cv2.THRESH_BINARY)

# Get indices of all non-zero elements
nz = np.nonzero(img)

# Find minimum and maximum x and y indices
y_min = np.min(nz[0])
y_max = np.max(nz[0])
x_min = np.min(nz[1])
x_max = np.max(nz[1])

# Create some output
output = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
cv2.rectangle(output, (x_min, y_min), (x_max, y_max), (0, 0, 255), 2)

# Show results
cv2.imshow('img', img)
cv2.imshow('output', output)
cv2.waitKey(0)
cv2.destroyAllWindows()

Я позаимствовал обрезанное изображение из ответа fmw42 в качестве входных данных, и мой вывод должен быть таким же (или наиболее похожим):

Вывод

Надеюсь, что (также) поможет!

person HansHirse    schedule 21.10.2019
comment
Это действительно крутой подход! Я собираюсь больше узнать о ненулевой функции. Большое спасибо. - person Karliky; 21.10.2019
comment
@HansHirse. Хороший подход. Расположение Min и Max для ненулевых пикселей — хорошая идея для этого изображения. Но я полагаю, это не сработало бы, если бы на изображении были другие белые области. Потребуется некоторая фильтрация, например, морфология или использование областей контура. - person fmw42; 21.10.2019
comment
@ fmw42 Мех, ты прав - я не думал об этом. К сожалению, я также не смог найти общего решения, использующего этот подход, который также охватывает поля, например, с похожими x, но разными диапазонами y. Итак, мое решение действительно ограничено определенными вариантами использования. - person HansHirse; 22.10.2019

Вот небольшая модификация ответа @fmw42. Идея в том, чтобы соединить нужные области в один контур, очень похожа, однако вы можете найти ограничивающий прямоугольник напрямую, поскольку есть только один объект. Используя то же обрезанное входное изображение, вот результат.

введите здесь описание изображения

При желании мы также можем извлечь ROI

введите здесь описание изображения

import cv2

# Grayscale, threshold, and dilate
image = cv2.imread('3.png')
original = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Connect into a single contour and find rect
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
dilate = cv2.dilate(thresh, kernel, iterations=1)
x,y,w,h = cv2.boundingRect(dilate)
ROI = original[y:y+h,x:x+w]
cv2.rectangle(image, (x, y), (x+w, y+h), (36, 255, 12), 2)

cv2.imshow('image', image)
cv2.imshow('ROI', ROI)
cv2.waitKey()
person nathancy    schedule 21.10.2019
comment
Фантастический подход! Спасибо большое - person Karliky; 22.10.2019