使用Python从图像中删除不需要的连接像素

4
我是一名初学者,正在学习使用Python进行图像处理,所以我需要帮助。我试图使用下面发布的代码从我的图片中删除连接像素区域。实际上,它可以工作,但效果不佳。我想要的是从我的图片中删除像红色标记的那些像素区域,以便获得清晰的图片。如果能够设置检测到的连接像素区域的最小和最大尺寸限制,那就更好了。 示例图片1(已标记区域) 示例图片2(已标记区域)

原始图片

这是我目前的代码:

### LOAD MODULES ###
import numpy as np
import imutils
import cv2

def is_contour_bad(c): # Decide what I want to find and its features
    peri=cv2.contourArea(c, True) # Find areas
    approx=cv2.approxPolyDP(c, 0.3*peri, True) # Set areas approximation
    return not len(approx)>2 # Threshold to decide if add an area to the mask for its removing (if>2 remove)


### DATA PROCESSING ###
image=cv2.imread("025.jpg") # Load a picture
gray=cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Convert to grayscale
cv2.imshow("Original image", image) # Plot

edged=cv2.Canny(gray, 50, 200, 3) # Edges of areas detection
cnts=cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) # Find contours: a curve joining all the continuous points (along the boundary), having same color or intensity
cnts=imutils.grab_contours(cnts)

mask=np.ones(image.shape[:2], dtype="uint8")*255 # Setup the mask with white background
# Loop over the detected contours
for c in cnts:
    # If the contour satisfies "is_contour_bad", draw it on the mask
    if is_contour_bad(c):
        cv2.drawContours(mask, [c], -1, 0, -1) # (source image, list of contours, with -1 all contours in [c] pass, 0 is the intensity, -1 the thickness)

image_cleaned=cv2.bitwise_and(image, image, mask=mask) # Remove the contours from the original image
cv2.imshow("Adopted mask", mask) # Plot
cv2.imshow("Cleaned image", image_cleaned) # Plot
cv2.imwrite("cleaned_025.jpg", image_cleaned) # Write in a file

你可以在画图软件中用黑色覆盖它们。 - Hippolippo
你能发布图像的数组表示吗? - Edeki Okoh
谢谢@Hippolippo,但是你的程序不太适合研究目的。 - andmeo
@EdekiOkoh 这是一个 uint8 类型的数组,大小为 (357, 357, 3)。 - andmeo
实际的数组本身。这样我们就可以诊断 is_contour_bad 如何计算“不良”区域。 - Edeki Okoh
@EdekiOkoh 矩阵太大无法在此处绘制,因此我附上了原始图片。您可以使用该图片运行我的代码并检查矩阵。很抱歉,这是我找到的解决方案,以满足您的问题。 - andmeo
1个回答

2
您可以执行以下处理步骤:
  • 使用cv2.threshold将图像阈值化为二进制图像。
    这不是必须的,但在您的情况下,灰度的阴影似乎并不重要。
  • 使用closing形态学操作,关闭二进制图像中的小间隙。
  • 使用带有cv2.RETR_EXTERNAL参数的cv2.findContours,获取围绕白色簇周围的轮廓(周长)。
  • 修改“不良轮廓”的逻辑,仅在面积较大时返回true(假设您只想清除三个大轮廓)。
以下是更新后的代码:
### LOAD MODULES ###
import numpy as np
import imutils
import cv2

def is_contour_bad(c): # Decide what I want to find and its features
    peri = cv2.contourArea(c) # Find areas
    return peri > 50 # Large area is considered "bad"


### DATA PROCESSING ###
image = cv2.imread("025.jpg") # Load a picture
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Convert to grayscale

# Convert to binary image (all values above 20 are converted to 1 and below to 0)
ret, thresh_gray = cv2.threshold(gray, 20, 255, cv2.THRESH_BINARY)

# Use "close" morphological operation to close the gaps between contours
# https://stackoverflow.com/questions/18339988/implementing-imcloseim-se-in-opencv
thresh_gray = cv2.morphologyEx(thresh_gray, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5)));

#Find contours on thresh_gray, use cv2.RETR_EXTERNAL to get external perimeter
_, cnts, _ = cv2.findContours(thresh_gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # Find contours: a curve joining all the continuous points (along the boundary), having same color or intensity

image_cleaned = gray

# Loop over the detected contours
for c in cnts:
    # If the contour satisfies "is_contour_bad", draw it on the mask
    if is_contour_bad(c):
        # Draw black contour on gray image, instead of using a mask
        cv2.drawContours(image_cleaned, [c], -1, 0, -1)


#cv2.imshow("Adopted mask", mask) # Plot
cv2.imshow("Cleaned image", image_cleaned) # Plot
cv2.imwrite("cleaned_025.jpg", image_cleaned) # Write in a file

cv2.waitKey(0)
cv2.destroyAllWindows()

结果:
在此输入图片描述
在测试中找到标记轮廓:
for c in cnts:
    if is_contour_bad(c):
        # Draw green line for marking the contour
        cv2.drawContours(image, [c], 0, (0, 255, 0), 1)

结果:
这里输入图片描述

仍有工作要做...


更新

两次迭代的方法:

  • 第一次迭代 - 移除大轮廓。
  • 第二次迭代 - 移除小但明亮的轮廓。

以下是代码:

import numpy as np
import imutils
import cv2

def is_contour_bad(c, thrs): # Decide what I want to find and its features
    peri = cv2.contourArea(c) # Find areas
    return peri > thrs # Large area is considered "bad"

image = cv2.imread("025.jpg") # Load a picture
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Convert to grayscale

# First iteration - remove the large contour
###########################################################################
# Convert to binary image (all values above 20 are converted to 1 and below to 0)
ret, thresh_gray = cv2.threshold(gray, 20, 255, cv2.THRESH_BINARY)

# Use "close" morphological operation to close the gaps between contours
# https://stackoverflow.com/questions/18339988/implementing-imcloseim-se-in-opencv
thresh_gray = cv2.morphologyEx(thresh_gray, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5)));

#Find contours on thresh_gray, use cv2.RETR_EXTERNAL to get external perimeter
_, cnts, _ = cv2.findContours(thresh_gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # Find contours: a curve joining all the continuous points (along the boundary), having same color or intensity

image_cleaned = gray

# Loop over the detected contours
for c in cnts:
    # If the contour satisfies "is_contour_bad", draw it on the mask
    if is_contour_bad(c, 1000):
        # Draw black contour on gray image, instead of using a mask
        cv2.drawContours(image_cleaned, [c], -1, 0, -1)
###########################################################################


# Second iteration - remove small but bright contours
###########################################################################
# In the second iteration, use high threshold
ret, thresh_gray = cv2.threshold(image_cleaned, 150, 255, cv2.THRESH_BINARY)

# Use "dilate" with small radius
thresh_gray = cv2.morphologyEx(thresh_gray, cv2.MORPH_DILATE, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2,2)));

#Find contours on thresh_gray, use cv2.RETR_EXTERNAL to get external perimeter
_, cnts, _ = cv2.findContours(thresh_gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # Find contours: a curve joining all the continuous points (along the boundary), having same color or intensity

# Loop over the detected contours
for c in cnts:
    # If the contour satisfies "is_contour_bad", draw it on the mask
    # Remove contour if  area is above 20 pixels
    if is_contour_bad(c, 20):
        # Draw black contour on gray image, instead of using a mask
        cv2.drawContours(image_cleaned, [c], -1, 0, -1)
###########################################################################

标记的轮廓:
enter image description here

Rotem,你是最棒的!非常感谢你,我一直在改进发布的代码,但你的版本真是太棒了。再次感谢! - andmeo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接