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

2024-06-01 01:07:46 发布

您现在位置:Python中文网/ 问答频道 /正文

  • 我试图仅将图像的部分保留在第17号地块的橙色/绿色线范围内

Here

  • 正如你所看到的,这个形状是相当非标准的,我对图像处理还不熟悉,所以我的方法到目前为止都是蛮力的,而且容易出错

  • 我需要这样做的每个图像都有一个黑点(rgb的(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颜色值,甚至在我感兴趣的线条上有一些洞

  • 有没有办法获得黑点在中心的边界线坐标,并在记录完所有坐标后裁剪图像

提前谢谢


Tags: inimageforiflinerangergbloc
1条回答
网友
1楼 · 发布于 2024-06-01 01:07:46

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

因为你有一些红色的线和黄色的点,我通过减去绿色通道的一部分来校正红色通道,以去除黄色。在进一步强调(红色虚线在红色通道中具有高值)后,新的红色通道如下所示:

Red channel

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

Laplace

从这里开始,只需使用大津的方法进行一些阈值处理,以获得合适的二值图像:

Binary image

最后,我找到所有轮廓,并迭代它们。如果我发现一个内部(!)轮廓-请参见this answer了解关于轮廓层次结构的详细解释-其中包含“黑点”的位置,那一定是感兴趣的形状。因为你可能会从周围得到一些奇怪的、开放的轮廓,你需要坚持内部轮廓。另外,这里有一个假设,即兴趣的形状是封闭的

提取正确的轮廓后,只需设置正确的遮罩,例如,将背景变黑,或使用该遮罩的边框裁剪图像:

Mask

Output

以下是完整的代码:

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
                    

相关问题 更多 >