我有一堆代表硬币的图像,其中一些具有嘈杂的背景(例如字母或不同的背景颜色)。我试图删除每个硬币图像的背景,只留下硬币本身,但我无法从 OpenCV 获取
cv2.findContours
函数来仅检测硬币的主要轮廓,它也会删除一些其他部分,或者留下一些来自背景的额外噪音。
以下是我正在使用的代码,我遵循的过程是:
edgedetect()
。在这里,它计算 X 和 Y 索贝尔并通过应用 Otsu 阈值转换为阈值。findSignificantContours()
。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()
功能后的边缘图像。
最后一张是带有“删除”背景的最终图像,遗憾的是它仍然包含一些字母,我不知道我做错了什么,也不知道如何改进我的代码以实现我想要的。有人可以帮我解决这个问题吗?
这是一个处理链示例。根据轮廓的圆度对轮廓进行最终过滤。当然不完美,但也许有一点帮助。
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()