Python 如何裁剪轮廓的内部区域?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28759253/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to crop the internal area of a contour?
提问by Gaurav Patil
I am working on Retinal fundus images.The image consists of a circular retina on a black background. With OpenCV, I have managed to get a contour which surrounds the whole circular Retina. What I need is to crop out the circular retina from the black background.
我正在研究视网膜眼底图像。图像由黑色背景上的圆形视网膜组成。使用 OpenCV,我设法获得了围绕整个圆形 Retina 的轮廓。我需要的是从黑色背景中裁剪出圆形视网膜。
采纳答案by rayryeng
It is unclear in your question whether you want to actually crop out the information that is defined within the contour or mask out the information that isn't relevant to the contour chosen. I'll explore what to do in both situations.
在您的问题中不清楚您是要实际裁剪轮廓内定义的信息还是屏蔽与所选轮廓无关的信息。我将探讨在这两种情况下该怎么做。
Masking out the information
屏蔽信息
Assuming you ran cv2.findContours
on your image, you will have received a structure that lists all of the contours available in your image. I'm also assuming that you know the indexof the contour that was used to surround the object you want. Assuming this is stored in idx
, first use cv2.drawContours
to draw a filledversion of this contour onto a blank image, then use this image to index into your image to extract out the object. This logic masksout any irrelevant information and only retain what is important - which is defined within the contour you have selected. The code to do this would look something like the following, assuming your image is a grayscale image stored in img
:
假设您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()
If you actually want to crop...
如果你真的想裁剪...
If you want to cropthe image, you need to define the minimum spanning bounding box of the area defined by the contour. You can find the top left and lower right corner of the bounding box, then use indexing to crop out what you need. The code will be the same as before, but there will be an additional cropping step:
如果要裁剪图像,则需要定义轮廓定义的区域的最小跨度边界框。您可以找到边界框的左上角和右下角,然后使用索引裁剪出您需要的内容。代码将与之前相同,但会有一个额外的裁剪步骤:
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()
The cropping code works such that when we define the mask to extract out the area defined by the contour, we additionally find the smallest horizontal and vertical coordinates which define the top left corner of the contour. We similarly find the largest horizontal and vertical coordinates that define the bottom left corner of the contour. We then use indexing with these coordinates to crop what we actually need. Note that this performs cropping on the maskedimage - that is the image that removes everything but the information contained within the largest contour.
裁剪代码的工作原理是,当我们定义掩码以提取轮廓定义的区域时,我们还会找到定义轮廓左上角的最小水平和垂直坐标。我们类似地找到定义轮廓左下角的最大水平和垂直坐标。然后我们使用这些坐标的索引来裁剪我们实际需要的内容。请注意,这会对蒙版图像执行裁剪- 即移除除最大轮廓内包含的信息之外的所有内容的图像。
Note with OpenCV 3.x
注意 OpenCV 3.x
It should be noted that the above code assumes you are using OpenCV 2.4.x. Take note that in OpenCV 3.x, the definition of cv2.findContours
has changed. Specifically, the output is a three element tuple output where the first image is the source image, while the other two parameters are the same as in OpenCV 2.4.x. Therefore, simply change the cv2.findContours
statement in the above code to ignore the first output:
应该注意的是,上面的代码假设您使用的是 OpenCV 2.4.x。请注意,在 OpenCV 3.x 中, 的定义cv2.findContours
已更改。具体来说,输出是一个三元素元组输出,其中第一个图像是源图像,而其他两个参数与 OpenCV 2.4.x 中的相同。因此,只需cv2.findContours
将上面代码中的语句更改为忽略第一个输出:
_, contours, _ = cv2.findContours(...) # Your call to find contours
回答by nathancy
Here's another approach to crop out a rectangular ROI. The main idea is to find the edges of the retina using Canny edge detection, find contours, and then extract the ROI using Numpy slicing. Assuming you have an input image like this:
这是另一种裁剪矩形 ROI 的方法。主要思想是使用Canny边缘检测找到视网膜的边缘,找到轮廓,然后使用Numpy切片提取ROI。假设您有这样的输入图像:
Extracted 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()
回答by fmw42
This is a pretty simple way. Mask the image with transparency.
这是一个非常简单的方法。用透明度掩盖图像。
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
Input:
输入:
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)