У меня есть следующее изображение:
Я хочу извлечь диаграммы в штучной упаковке следующим образом:
Вот что я пытался:
import cv2
import matplotlib.pyplot as plt
# Load the image
image = cv2.imread('diagram.jpg')
# Convert to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Apply thresholding to create a binary image
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
# Find contours
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Draw the contours
cv2.drawContours(image, contours, -1, (0, 0, 255), 2)
# Show the final image
plt.imshow(image), plt.show()
Однако я понял, что будет сложно извлечь диаграммы, потому что контуры не замкнуты:
Я попытался использовать морфологическое закрытие, чтобы закрыть промежутки в краях коробки:
# Define a rectangular kernel for morphological closing
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# Perform morphological closing to close the gaps in the box edges
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
Но это почти ничего не меняет. Как мне подойти к этой проблеме?
Просто нужно расширить изображение, чтобы сделать прямоугольник закрытым, а затем определить порог для области контуров:
import cv2
# Load the image
image = cv2.imread('diagram.jpg')
# Convert to grayscale
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
# Apply thresholding to create a binary image
ret,thresh = cv2.threshold(gray,200,255,1)
# Need to dilate the image to make the contours closed
dilate = cv2.dilate(thresh,None)
erode = cv2.erode(dilate,None)
# Find contours
contours,hierarchy = cv2.findContours(erode,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
for i,cnt in enumerate(contours):
# Check if it is an external contour and its area is more than 8000
if hierarchy[0,i,3] == -1 and cv2.contourArea(cnt)>8000:
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imwrite('template {0}.jpg'.format(i), image[y:y+h,x:x+w])
cv2.imshow('img',image)
Ты получишь :
Мы можем заменить морфологическое закрытие на расширение, затем размывание, но заполнение контуров между расширением и размыванием.
Для заполнения пробелов размер ядра должен быть намного больше 5х5 (я использовал 51х51).
Предполагая, что рукописные поля раскрашены, мы можем преобразовать BGR в HSV и применить пороговое значение к каналу насыщения HSV:
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # Convert from BGR to HSV color space
gray = hsv[:, :, 1] # Use saturation from HSV channel as "gray".
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU) # Apply automatic thresholding (use THRESH_OTSU).
Примените дилат с большим ядром и используйте drawContours
для заполнения контуров:
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (51, 51)) # Use relatively large kernel for closing the gaps
dilated = cv2.dilate(thresh, kernel) # Dilate with large kernel
contours, hierarchy = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(dilated, contours, -1, 255, -1)
Нанесите эрозию после заполнения контуров Разрушение после расширения эквивалентно закрытию, но здесь мы закрываем после заполнения.
closed = cv2.erode(dilated, kernel)
Пример кода:
import cv2
import numpy as np
# Load the image
image = cv2.imread('diagram.png')
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # Convert from BGR to HSV color space
# Convert to grayscale
#gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = hsv[:, :, 1] # Use saturation from HSV channel as "gray".
# Apply thresholding to create a binary image
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU) # Apply automatic thresholding (use THRESH_OTSU).
thresh = np.pad(thresh, ((100, 100), (100, 100))) # Add zero padding (required due to large dilate kernels).
# Define a rectangular kernel for morphological operations.
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (51, 51)) # Use relatively large kernel for closing the gaps
dilated = cv2.dilate(thresh, kernel) # Dilate with large kernel
# Fill the contours, before applying erode.
contours, hierarchy = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(dilated, contours, -1, 255, -1)
closed = cv2.erode(dilated, kernel) # Apply erode after filling the contours.
closed = closed[100:-100, 100:-100] # Remove the padding.
# Find contours
contours, hierarchy = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Draw the contours
cv2.drawContours(image, contours, -1, (255, 0, 0), 2)
# Show images for testing
# plt.imshow(image), plt.show()
cv2.imshow('gray', gray)
cv2.imshow('thresh', thresh)
cv2.imshow('dilated', dilated)
cv2.imshow('closed', closed)
cv2.imshow('image', image)
cv2.waitKey()
cv2.destroyAllWindows()
Результат:
gray
(канал насыщения):
thresh
:
dilated
(после заполнения):
closed
: