[英]How to detect the outline of a shape and then crop the shape from image?
As you can see the shape is fairly non standard and I am new to image processing so my approach thus far has been brute forced and error prone.正如您所看到的,形状相当不标准,而且我是图像处理的新手,因此到目前为止我的方法是蛮力且容易出错的。
Each image I need to do this for has a black dot ( rgb
of (77,77,77)) in the center of the shape I want to crop which has been my anchor.我需要执行此操作的每个图像在我想要裁剪的形状的中心都有一个黑点( rgb
of (77,77,77)),它一直是我的锚点。
import PIL
import pandas as pd
image = PIL.Image.open(file)
rgb_im = image.convert('RGB')
color = (77,77,77)
colorindex = pd.DataFrame(data = None,columns = ['X','Y'])
for x in range(image.size[0]):
for y in range(image.size[1]):
r, g, b = rgb_im.getpixel((x, y))
if (r,g,b) == color:
append = [x,y]
append = pd.Series(append,index = colorindex.columns)
colorindex = colorindex.append(append,ignore_index = True)
center = [colorindex.mode()['X'][0],colorindex.mode()['Y'][0]]
line = pd.read_excel('C:/Users/lines RGb.xlsx') ##Prerecorded RGB Values
def findparcelline(CenterX,CenterY,direction):
if direction == 'left':
for x in range(CenterX):
r,g,b = rgb_im.getpixel((CenterX-x,CenterY))
for i in range(len(line)):
if (r,g,b) == (line.loc[i][0],line.loc[i][1],line.loc[i][2]):
pixelsave = CenterX-x
return pixelsave
elif direction == 'right':
for x in range(CenterX):
r,g,b = rgb_im.getpixel((CenterX+x,CenterY))
for i in range(len(line)):
if (r,g,b) == (line.loc[i][0],line.loc[i][1],line.loc[i][2]):
pixelsave = CenterX+x
return pixelsave
elif direction == 'down':
for y in range(CenterY):
r,g,b = rgb_im.getpixel((CenterX,CenterY + y))
for i in range(len(line)):
if (r,g,b) == (line.loc[i][0],line.loc[i][1],line.loc[i][2]):
pixelsave = CenterY + y
return pixelsave
elif direction == 'up':
for y in range(CenterY):
r,g,b = rgb_im.getpixel((CenterX,CenterY - y))
for i in range(len(line)):
if (r,g,b) == (line.loc[i][0],line.loc[i][1],line.loc[i][2]):
pixelsave = CenterY - y
return pixelsave
directions = ['left','down','right','up']
coords =[]
for direction in directions:
coords.append(findparcelline(center[0],center[1],direction))
im1 = image.crop(coords)
But in the example there are even more rgb
color values to and even some holes in the line I'm interested in.但是在这个例子中,我感兴趣的行中有更多的rgb
颜色值,甚至还有一些洞。
Is there a way to obtain the coordinates of the line bounding the black dot in the center and subsequently crop the image after having recording all the coordinates?有没有办法获得包围中心黑点的线的坐标,然后在记录所有坐标后裁剪图像?
Thanks in advance.提前致谢。
First of all: If you have access to the generation of these images, save them as lossless PNGs!首先:如果您可以访问这些图像的生成,请将它们保存为无损 PNG! Those JPG artifacts make it even harder to get proper results.那些 JPG 工件使获得正确结果变得更加困难。 For example, only one pixel of your "black" dot actually has RGB values of (77, 77, 77)
.例如,“黑”点中只有一个像素的 RGB 值实际上是(77, 77, 77)
。 Therefore, I omitted the programmatically finding of the "black" dot, and assumed the image center as the dot location.因此,我省略了“黑”点的编程查找,并假设图像中心为点位置。
Since you have kind of red-ish lines with some kind of yellow-ish dots, I rectified the red channel by subtracting a portion of the green channel to get rid of yellow-ish colors.因为你有一种带有某种黄色点的红色线条,我通过减去绿色通道的一部分来消除黄色来纠正红色通道。 After some further emphasizing (red-ish lines have high values in the red channel), the new red channel looks like this:进一步强调(红色线条在红色通道中具有高值)后,新的红色通道如下所示:
On that new red channel, I use some kind of Laplace operator to detect the (red-ish) lines.在那个新的红色通道上,我使用某种拉普拉斯算子来检测(红色)线。 After some further processing, that'd be the result:经过一些进一步的处理,这就是结果:
From there, it's just some thresholding using Otsu's method to get a proper binary image to work on:从那里开始,它只是使用 Otsu 的方法进行一些阈值处理来获得合适的二进制图像:
Finally, I find all contours, and iterate them.最后,我找到所有的轮廓,并迭代它们。 If I find an inner(!) contour – please see this answer for an extensive explanation on contour hierarchies – which contains the location of the "black" dot, that must be shape of interest.如果我找到了一个内部(!)轮廓——请参阅此答案以获取有关轮廓层次结构的详细解释——其中包含“黑”点的位置,它必须是感兴趣的形状。 Since you might get some odd, open contours from the surrounding, you need to stick to inner contours.由于您可能会从周围获得一些奇怪的开放轮廓,因此您需要坚持内部轮廓。 Also, it's an assumption here, that the shape of interest is closed.此外,这里是一个假设,即感兴趣的形状是封闭的。
After extracting the proper contour, you just need to set up a proper mask, and for example blacken the background, or crop the image using the bounding rectangle of that mask:提取适当的轮廓后,您只需要设置适当的蒙版,例如将背景变黑,或使用该蒙版的边界矩形裁剪图像:
Here's the full code:这是完整的代码:
import cv2
import numpy as np
# Read image, split color channels
img = cv2.imread('5aY7A.jpg')
b, g, r = cv2.split(img)
# Rectify red-ish lines (get rid of yellow-ish dots) by subtracting
# green channel from red channel
r = r - 0.5 * g
r[r < 0] = 0
# Emphasize red-ish lines
r **= 2
r = cv2.normalize(r, 0, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
# Detection of red-ish lines by Laplace operator
r = cv2.Laplacian(r, cv2.CV_64F)
r = cv2.erode(r, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)))
r = cv2.GaussianBlur(r, (5, 5), 0)
r = cv2.normalize(r, 0, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
# Mask red-ish lines
r = cv2.threshold(r, 10, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
r = cv2.morphologyEx(r, cv2.MORPH_CLOSE,
cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)))
# Detection of "black" dot location omitted here due to JPG artifacts...
dot = (916, 389)
# Find contours from masked red-ish lines
cnts, hier = cv2.findContours(r, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# Find some inner(!) contour containing the "black dot"
cnt = None
for i, c in enumerate(cnts):
if cv2.pointPolygonTest(c, dot, True) > 0 and hier[0, i, 3] != -1:
cnt = c
break
if cnt is None:
print('Something went wrong, no contour found.')
else:
mask = cv2.drawContours(np.zeros_like(r), [cnt], -1, 255, cv2.FILLED)
output = cv2.bitwise_xor(img, np.zeros_like(img), mask=mask)
cv2.imshow('Output', output)
cv2.waitKey(0)
cv2.destroyAllWindows()
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.19041-SP0
Python: 3.9.1
PyCharm: 2021.1.2
NumPy: 1.20.3
OpenCV: 4.5.2
----------------------------------------
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.