【问题标题】:How to detect the outline of a shape and then crop the shape from image?如何检测形状的轮廓然后从图像中裁剪形状?
【发布时间】:2021-09-06 11:49:37
【问题描述】:
  • 我正在尝试仅保留 17 号批次中以橙色/绿色线为界的图像部分。

  • 如您所见,形状相当不标准,而且我是图像处理的新手,所以到目前为止我的方法是蛮力且容易出错的。

  • 我需要对其执行此操作的每张图像在我要裁剪的形状的中心都有一个黑点(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)
  • 我的代码仅适用于正面朝上的矩形形状(其中很多是这样的),但是当涉及到示例中的内容时它会失败。
  • 我考虑过使用写到这里的代码,然后从通过 9x9 像素数组提供的像素位置“走线”,并且只选择那些:
  1. 之前未选择
  2. 匹配预先记录的颜色值
  3. 最接近锚像素位置
  • 但在示例中,我感兴趣的行中有更多 rgb 颜色值,甚至还有一些孔。

  • 有没有办法获取包围中心黑点的线的坐标,然后在记录所有坐标后裁剪图像?

提前致谢。

【问题讨论】:

  • 如果您知道中心(黑点)并知道您想要的矩形区域和角度,您可以计算旋转矩形的角,以便您可以定义蒙版。然后遮盖旋转矩形外的背景和/或裁剪到最小边界 XY 方向的边界框。

标签: python opencv image-processing python-imaging-library crop


【解决方案1】:

首先:如果您有权生成这些图像,请将它们保存为无损 PNG!这些 JPG 伪影使获得正确结果变得更加困难。例如,“黑”点中只有一个像素实际上具有(77, 77, 77) 的 RGB 值。因此,我省略了以编程方式查找“黑”点,并假设图像中心为点位置。

由于您有带有某种黄色点的红色线条,我通过减去绿色通道的一部分来纠正红色通道以消除黄色。经过进一步强调(红色通道中的红色线条具有较高的值),新的红色通道如下所示:

在那个新的红色通道上,我使用某种Laplace operator 来检测(红色)线。经过进一步处理,结果如下:

从那里,它只是使用 Otsu 的方法进行一些阈值处理,以获得正确的二进制图像来处理:

最后,我找到所有轮廓,并对其进行迭代。如果我找到一个内部(!)轮廓——请参阅this answer 以获得关于轮廓层次结构的广泛解释——其中包含“黑”点的位置,它必须是感兴趣的形状。由于您可能会从周围获得一些奇怪的开放轮廓,因此您需要坚持内部轮廓。此外,这里假设感兴趣的形状是封闭的。

提取合适的轮廓后,您只需要设置一个合适的蒙版,例如将背景变黑,或者使用该蒙版的边界矩形裁剪图像:

这是完整的代码:

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
----------------------------------------

【讨论】:

  • 难以置信!非常感谢,这正是我所需要的!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-30
  • 2019-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多