我正在研究视网膜眼底图像。图像由黑色背景上的圆形视网膜组成。通过OpenCV,我设法获得了围绕整个圆形Retina的轮廓。我需要的是从黑色背景中裁剪掉圆形视网膜。
答案 0 :(得分:29)
您的问题中不清楚您是否要实际裁剪出轮廓中定义的信息,或者屏蔽掉与所选轮廓无关的信息。我将探讨在这两种情况下该怎么做。
假设您在图片上运行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()
如果要裁剪图像,则需要定义轮廓定义的区域的最小跨越边界框。您可以找到边界框的左上角和右下角,然后使用索引来裁剪出您需要的内容。代码将与之前相同,但会有一个额外的裁剪步骤:
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()
裁剪代码的工作原理是,当我们定义遮罩以提取轮廓定义的区域时,我们还会找到定义轮廓左上角的最小水平和垂直坐标。我们同样找到了最大的水平和垂直坐标,它们定义了轮廓的左下角。然后,我们使用这些坐标的索引来裁剪我们实际需要的东西。请注意,这会对蒙版图像执行裁剪 - 即删除除最大轮廓中包含的信息之外的所有内容的图像。
应该注意的是,上面的代码假设您使用的是OpenCV 2.4.x.请注意,在OpenCV 3.x中,cv2.drawContours
的定义已更改。具体来说,输出是三元素元组输出,其中第一个图像是源图像,而其他两个参数与OpenCV 2.4.x中的相同。因此,只需更改上面代码中的cv2.findContours
语句即可忽略第一个输出:
_, contours, _ = cv2.findContours(...) # Your call to find contours
答案 1 :(得分:1)
这是裁剪矩形ROI的另一种方法。主要思想是使用Canny边缘检测来找到视网膜的边缘,找到轮廓,然后使用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()
答案 2 :(得分:0)
这是一种非常简单的方法。掩盖图像的透明度。
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)