使用 Sobel 边缘检测去除图像背景

问题描述 投票:0回答:1

我有一堆代表硬币的图像,其中一些具有嘈杂的背景(例如字母或不同的背景颜色)。我试图删除每个硬币图像的背景,只留下硬币本身,但我无法从 OpenCV 获取

cv2.findContours
函数来仅检测硬币的主要轮廓,它也会删除一些其他部分,或者留下一些来自背景的额外噪音。

以下是我正在使用的代码,我遵循的过程是:

  1. 从 bytes 对象中将图像读取为 numpy 数组。
  2. 将其解码为彩色图像。
  3. 转换为灰度图像。
  4. 添加高斯模糊以消除噪音。
  5. 应用索贝尔滤波器检测图像中的边缘
    edgedetect()
    。在这里,它计算 X 和 Y 索贝尔并通过应用 Otsu 阈值转换为阈值。
  6. 计算图像的平均值并将低于其的任何值归零以消除噪声。
  7. 找到重要的轮廓(
    findSignificantContours()
  8. 从轮廓创建蒙版,反转并删除它以获得背景。
  9. 将蒙版设置为 255 以去除原始图像中的背景。
import cv2
import numpy as np
from google.colab.patches import cv2_imshow

def edgedetect(channel):
    sobelX = cv2.Sobel(channel, cv2.CV_64F, 1, 0, ksize = 3, scale = 1)
    sobelY = cv2.Sobel(channel, cv2.CV_64F, 0, 1, ksize = 3, scale = 1)
    sobel = np.hypot(sobelX, sobelY)

    sobel = cv2.convertScaleAbs(sobel)
    sobel[sobel > 255] = 255 # Some values seem to go above 255. However RGB channels has to be within 0-255

    _, sobel_binary = cv2.threshold(sobel, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)

    return cv2.bitwise_not(sobel_binary)

def findSignificantContours (img, edgeImg):

    print(f'edgeimg:')
    cv2_imshow(edgeImg)
    contours, hierarchy = cv2.findContours(edgeImg, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    # Find level 1 contours
    level1 = []
    for i, tupl in enumerate(hierarchy[0]):
        # Each array is in format (Next, Prev, First child, Parent)
        # Filter the ones without parent
        if tupl[3] == -1:
            tupl = np.insert(tupl, 0, [i])
            level1.append(tupl)

    # From among them, find the contours with large surface area.
    significant = []
    tooSmall = edgeImg.size * 5 / 100 # If contour isn't covering 5% of total area of image then it probably is too small
    for tupl in level1:
        contour = contours[tupl[0]]

        area = cv2.contourArea(contour)
        if area > tooSmall:
            significant.append([contour, area])

            # Draw the contour on the original image
            cv2.drawContours(img, [contour], 0, (0, 255, 0), 2, cv2.LINE_8)

    significant.sort(key = lambda x: x[1])

    return [x[0] for x in significant]

def remove_background(bytes_data):
    # Read image.
    image = np.asarray(bytearray(bytes_data.read()), dtype = "uint8")
    img = cv2.imdecode(image, cv2.IMREAD_COLOR)

    print(f'Original:')
    cv2_imshow(img)

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    print(f'Gray:')
    cv2_imshow(gray)

    blurred_gray = cv2.GaussianBlur(gray, (3, 3), 0) # Remove noise.
    print(f'Blurred Gray:')
    cv2_imshow(blurred_gray)

    edgeImg = np.max( np.array([edgedetect(blurred_gray[:, :])]), axis = 0)

    mean = np.mean(edgeImg)

    # Zero any value that is less than mean. This reduces a lot of noise.
    edgeImg[edgeImg <= mean] = 0
    edgeImg_8u = np.asarray(edgeImg, np.uint8)

    # Find contours.
    significant = findSignificantContours(img, edgeImg_8u)

    # Mask.
    mask = edgeImg.copy()
    mask[mask > 0] = 0
    cv2.fillPoly(mask, significant, 255)
    mask = np.logical_not(mask) # Invert mask to get the background.

    # Remove the background.
    img[mask] = 255;

    print(f'FINAL:')
    cv2_imshow(img)

    return img

if __name__ == '__main__':
    imgUrl = 'http://images.numismatics.org/archivesimages%2Farchive%2Fschaefer_clippings_output_383_06_od.jpg/2648,1051,473,453/full/0/default.jpg'
    obvPage = requests.get(imgUrl, stream = True, verify = False, headers = header)

    img_final = remove_background(obvPage.raw)

作为表示,这是原始图像,您可以看到它的右侧写着一些字母,这就是我要删除的内容。其余图像相似,尽管有些图像具有不同的背景颜色而不仅仅是白色。

下图是使用sobels执行

edgedetect()
功能后的边缘图像。

最后一张是带有“删除”背景的最终图像,遗憾的是它仍然包含一些字母,我不知道我做错了什么,也不知道如何改进我的代码以实现我想要的。有人可以帮我解决这个问题吗?

python opencv computer-vision semantic-segmentation
1个回答
0
投票

这是一个处理链示例。根据轮廓的圆度对轮廓进行最终过滤。当然不完美,但也许有一点帮助。

import numpy as np
import cv2

# Load an color image in grayscale
image = cv2.imread('archivesimages_archive_schaefer_clippings_output_383_06_od.jpg',0)

# blur the image
bkz = 10
blurred = cv2.blur(image, (bkz, bkz), 0)

# thresholding
(T, thresh) = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

kernel = np.ones((5, 5), np.uint8)
thresh = cv2.erode(thresh, kernel, iterations=1)
#thresh = cv2.dilate(thresh, kernel, iterations=1)

# find contours
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# filter contours
contour_list = []
for contour in contours:
    approx = cv2.approxPolyDP(contour,0.01*cv2.arcLength(contour,True),True)
    area = cv2.contourArea(contour)
    if ((len(approx) > 8) & (area > 50000)):
        contour_list.append(contour)
print(len(contours))
print(len(contour_list))

# draw contours
thresh = cv2.cvtColor(thresh,cv2.COLOR_GRAY2RGB)
cv2.drawContours(thresh, contour_list, -1, (255,0,0), 3)

image = cv2.cvtColor(image,cv2.COLOR_GRAY2RGB)
cv2.drawContours(image, contour_list, -1, (255,0,0), 3)

# show the image
cv2.imshow('image1',thresh)
cv2.imshow('image2',image)
cv2.waitKey(0)
cv2.destroyAllWindows()

© www.soinside.com 2019 - 2024. All rights reserved.