Я работаю над изображениями сетчатки сетчатки. Изображение состоит из круговой сетчатки на черном фоне. С OpenCV мне удалось получить контур, который окружает всю циркулярную сетчатку. Мне нужно вырезать круговую сетчатку с черного фона.
Как обрезать внутреннюю область контура?
Ответ 1
В вашем вопросе неясно, хотите ли вы на самом деле обрезать информацию, определенную в контуре, или замаскировать информацию, которая не имеет отношения к выбранному контуру. Я выясню, что делать в обеих ситуациях.
Маскировка информации
Предполагая, что вы запустили cv2.findContours
на своем изображении, вы получите структуру, в которой перечислены все контуры, доступные на вашем изображении. Я также предполагаю, что вы знаете индекс контура, который использовался для окружения объекта, который вы хотите. Предполагая, что это сохранено в idx
, сначала используйте cv2.drawContours
, чтобы нарисовать заполненную версию этого контура на пустом изображении, а затем используйте это изображение, чтобы проиндексировать его, чтобы извлечь вне объекта. Эта логика маскирует любую нерелевантную информацию и сохраняет только то, что важно - что определено в выбранном вами контуре. Код для этого выглядел бы примерно следующим образом, предполагая, что ваше изображение - это изображение в градациях серого, хранящееся в img
:
import numpy as np
import cv2
img = cv2.imread('...', 0) # Read in your image
# contours, _ = cv2.findContours(...) # Your call to find the contours using OpenCV 2.4.x
_, contours, _ = cv2.findContours(...) # Your call to find the contours
idx = ... # The index of the contour that surrounds your object
mask = np.zeros_like(img) # Create mask where white is what we want, black otherwise
cv2.drawContours(mask, contours, idx, 255, -1) # Draw filled contour in mask
out = np.zeros_like(img) # Extract out the object and place into output image
out[mask == 255] = img[mask == 255]
# Show the output image
cv2.imshow('Output', out)
cv2.waitKey(0)
cv2.destroyAllWindows()
Если вы действительно хотите обрезать...
Если вы хотите обрезать изображение, вам нужно определить минимальный ограничивающий ограничивающий прямоangularьник области, определенной контуром. Вы можете найти верхний левый и нижний правый angular ограничительной рамки, а затем использовать индексирование, чтобы вырезать то, что вам нужно. Код будет таким же, как и раньше, но будет дополнительный шаг обрезки:
import numpy as np
import cv2
img = cv2.imread('...', 0) # Read in your image
# contours, _ = cv2.findContours(...) # Your call to find the contours using OpenCV 2.4.x
_, contours, _ = cv2.findContours(...) # Your call to find the contours
idx = ... # The index of the contour that surrounds your object
mask = np.zeros_like(img) # Create mask where white is what we want, black otherwise
cv2.drawContours(mask, contours, idx, 255, -1) # Draw filled contour in mask
out = np.zeros_like(img) # Extract out the object and place into output image
out[mask == 255] = img[mask == 255]
# Now crop
(y, x) = np.where(mask == 255)
(topy, topx) = (np.min(y), np.min(x))
(bottomy, bottomx) = (np.max(y), np.max(x))
out = out[topy:bottomy+1, topx:bottomx+1]
# Show the output image
cv2.imshow('Output', out)
cv2.waitKey(0)
cv2.destroyAllWindows()
Код обрезки работает так, что когда мы определяем маску для выделения области, определенной контуром, мы дополнительно находим наименьшие горизонтальные и вертикальные координаты, которые определяют левый верхний angular контура. Мы также находим самые большие горизонтальные и вертикальные координаты, которые определяют левый нижний angular контура. Затем мы используем индексирование с этими координатами, чтобы обрезать то, что нам действительно нужно. Обратите внимание, что выполняется обрезка маскированного изображения - это изображение, которое удаляет все, кроме информации, содержащейся в самом большом контуре.
Заметка с OpenCV 3.x
Следует отметить, что приведенный выше код предполагает, что вы используете OpenCV 2.4.x. Обратите внимание, что в OpenCV 3.x определение cv2.drawContours
изменилось. В частности, выходные данные представляют собой трехэлементный кортеж, в котором первое изображение является исходным, а два других параметра такие же, как в OpenCV 2.4.x. Поэтому просто измените оператор cv2.findContours
в приведенном выше коде, чтобы игнорировать первый вывод:
_, contours, _ = cv2.findContours(...) # Your call to find contours
Ответ 2
Здесь другой подход, чтобы обрезать прямоangularьную область интереса. Основная идея состоит в том, чтобы найти края сетчатки, используя обнаружение краев Канни, найти контуры, а затем извлечь ROI, используя срез Numpy. Предполагая, что у вас есть входное изображение, подобное этому:
Извлеченный ROI
import cv2
# Load image, convert to grayscale, and find edges
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY)[1]
# Find contour and sort by contour area
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
# Find bounding box and extract ROI
for c in cnts:
x,y,w,h = cv2.boundingRect(c)
ROI = image[y:y+h, x:x+w]
break
cv2.imshow('ROI',ROI)
cv2.imwrite('ROI.png',ROI)
cv2.waitKey()
Ответ 3
Это довольно простой способ. Маска изображения с прозрачностью.
Read the image
Make a grayscale version.
Otsu Threshold
Apply morphology open and close to thresholded image as a mask
Put the mask into the alpha channel of the input
Save the output
Вход:
import cv2
import numpy as np
# load image as grayscale
img = cv2.imread('retina.jpeg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold input image using otsu thresholding as mask and refine with morphology
ret, mask = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
kernel = np.ones((9,9), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
# put thresh into
result = img.copy()
result = cv2.cvtColor(result, cv2.COLOR_BGR2BGRA)
result[:, :, 3] = mask
# save resulting masked image
cv2.imwrite('retina_masked.png', result)