简体   繁体   English

如何检测形状的轮廓,然后从图像中裁剪形状?

[英]How to detect the outline of a shape and then crop the shape from image?

  • I am attempting to only keep the part of the image bounded by the orange/greenish line in lot #17.我试图只保留第 17 批中橙色/绿色线所限定的图像部分。

这里

  • 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)
  • My code only works for right side up rectangular shapes (which a good bit of them are) but it will fail when it comes to something like in the example.我的代码仅适用于右侧向上的矩形(其中有很多是这样的),但是当涉及到示例中的内容时它会失败。
  • I've thought about using the code written this far to then 'walk the line' from the pixel location provided via a 9x9 array of pixels and only selecting the ones that:我已经考虑过使用写这么远的代码,然后从通过 9x9 像素阵列提供的像素位置“走线”,并且只选择那些:
  1. aren't previously selected之前没有被选中
  2. match the prerecorded color values匹配预先记录的颜色值
  3. are closest to the anchor pixel location最接近锚点像素位置
  • 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM