Opencv: вырезать текстовые области из лицензии
У меня есть изображение ниже одной лицензии водителя, я хочу извлечь информацию о водительских правах, имени, DOB и т.д. Мой мыслительный процесс состоит в том, чтобы найти способ сгруппировать их построчно и вырезать один прямоугольник, содержащий имя, лицензии и т.д. для англ и ара. Но я неудачно провалился.
![enter image description here]()
import cv2
import os
import numpy as np
scan_dir = os.path.dirname(__file__)
image_dir = os.path.join(scan_dir, '../../images')
class Loader(object):
def __init__(self, filename, gray=True):
self.filename = filename
self.gray = gray
self.image = None
def _read(self, filename):
rgba = cv2.imread(os.path.join(image_dir, filename))
if rgba is None:
raise Exception("Image not found")
if self.gray:
gray = cv2.cvtColor(rgba, cv2.COLOR_BGR2GRAY)
return gray, rgba
def __call__(self):
return self._read(self.filename)
class ImageScaler(object):
def __call__(self, gray, rgba, scale_factor = 2):
img_small_gray = cv2.resize(gray, None, fx=scale_factor, fy=scale_factor, interpolation=cv2.INTER_AREA)
img_small_rgba = cv2.resize(rgba, None, fx=scale_factor, fy=scale_factor, interpolation=cv2.INTER_AREA)
return img_small_gray, img_small_rgba
class BoxLocator(object):
def __call__(self, gray, rgba):
# image_blur = cv2.medianBlur(gray, 1)
ret, image_binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
image_not = cv2.bitwise_not(image_binary)
erode_kernel = np.ones((3, 1), np.uint8)
image_erode = cv2.erode(image_not, erode_kernel, iterations = 5)
dilate_kernel = np.ones((5,5), np.uint8)
image_dilate = cv2.dilate(image_erode, dilate_kernel, iterations=5)
kernel = np.ones((3, 3), np.uint8)
image_closed = cv2.morphologyEx(image_dilate, cv2.MORPH_CLOSE, kernel)
image_open = cv2.morphologyEx(image_closed, cv2.MORPH_OPEN, kernel)
image_not = cv2.bitwise_not(image_open)
image_not = cv2.adaptiveThreshold(image_not, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 15, -2)
image_dilate = cv2.dilate(image_not, np.ones((2, 1)), iterations=1)
image_dilate = cv2.dilate(image_dilate, np.ones((2, 10)), iterations=1)
image, contours, heirarchy = cv2.findContours(image_dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
x, y, w, h = cv2.boundingRect(contour)
# if w > 30 and h > 10:
cv2.rectangle(rgba, (x, y), (x + w, y + h), (0, 0, 255), 2)
return image_dilate, rgba
def entry():
loader = Loader('sample-004.jpg')
# loader = Loader('sample-004.jpg')
gray, rgba = loader()
imageScaler = ImageScaler()
image_scaled_gray, image_scaled_rgba = imageScaler(gray, rgba, 1)
box_locator = BoxLocator()
gray, rgba = box_locator(image_scaled_gray, image_scaled_rgba)
cv2.namedWindow('Image', cv2.WINDOW_NORMAL)
cv2.namedWindow('Image2', cv2.WINDOW_NORMAL)
cv2.resizeWindow('Image', 600, 600)
cv2.resizeWindow('Image2', 600, 600)
cv2.imshow("Image2", rgba)
cv2.imshow("Image", gray)
cv2.moveWindow('Image', 0, 0)
cv2.moveWindow('Image2', 600, 0)
cv2.waitKey()
cv2.destroyAllWindows()
Когда я запускаю приведенный выше код, я получаю следующую сегментацию. Что не близко к тому, что я хочу
![enter image description here]()
Но ниже, чего я хочу достичь, для всех входных лицензий ![enter image description here]()
Ответы
Ответ 1
Сверху головы я могу придумать два подхода:
Подход 1. Как упомянуто в комментариях, вы можете обрезать символ орла в верхнем левом углу и флаг в верхнем правом углу, использовать их в качестве шаблонов и найти два поля, которые вас интересуют, слева внизу (небольшой прямоугольник) и центр (большой квадрат) относительно позиции найденных шаблонов. Для начала вы можете использовать это:
Шаблон 1
![Template 1]()
Шаблон 2
![Template 2]()
Код:
import numpy as np
import cv2
import matplotlib.pyplot as plt
image = cv2.imread("ID_card.jpg")
template_1 = cv2.imread("template_1.jpg", 0)
w_1, h_1 = template_1.shape[::-1]
template_2 = cv2.imread("template_2.jpg", 0)
w_2, h_2 = template_2.shape[::-1]
res_1 = cv2.matchTemplate(image=image, templ=template_1, method=cv2.TM_CCOEFF)
min_val_1, max_val_1, min_loc_1, max_loc_1 = cv2.minMaxLoc(res_1)
res_2 = cv2.matchTemplate(image=image, templ=template_2, method=cv2.TM_CCOEFF)
min_val_2, max_val_2, min_loc_2, max_loc_2 = cv2.minMaxLoc(res_2)
cv2.rectangle(image, max_loc_1, (max_loc_1[0] + w_1, max_loc_1[1] + h_1), 255, 2)
cv2.rectangle(image, max_loc_2, (max_loc_2[0] + w_2, max_loc_2[1] + h_2), 255, 2)
Результат:
![Result Template]()
Вы можете использовать центры найденных шаблонов, чтобы получить относительное положение нужных полей (маленьких и больших).
Подход 2. Как и в случае с контурами, основная идея заключается в использовании морфологии для получения четких линий в большем поле.
Код:
import numpy as np
import cv2
import matplotlib.pyplot as plt
image = cv2.imread("ID_card.jpg")
imgray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 150, 255, 0)
# cv2.imwrite("thresh.jpg", thresh)
# Morphological operation
thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN,
cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7)))
im2, contours, heirarchy = cv2.findContours(thresh, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
# Sort the contours based on area
cntsSorted = sorted(contours, key=lambda x: cv2.contourArea(x), reverse=True)
approxes = []
for cnt in cntsSorted[1:10]:
peri = cv2.arcLength(cnt, True)
# approximate the contour shape
approx = cv2.approxPolyDP(cnt, 0.04 * peri, True)
approxes.append(approx)
if len(approx) == 4:
# length of 4 means 4 vertices so it should be a quadrilateral
cv2.drawContours(image, approx, -1, (0, 255, 0), 10)
cv2.imwrite("ID_card_contours.jpg", image)
print(approxes)
Результаты:
Пороговое изображение
![Thresholded]()
После морфологического открытия
![Closed]()
Окончательное изображение с соответствующими углами двух предполагаемых прямоугольников, отмеченных зеленым
![Final image]()
Таким образом, этот подход довольно прост, и я уверен, что вы можете сделать все остальное в поиске меньших подмножеств из большого блока. Если нет, напишите мне комментарий, и я буду рад помочь (в основном обрежьте эту область с изображения, используйте HoughlinesP, и у вас все будет хорошо. Или я вижу, что меньшие подмножества имеют равную ширину, поэтому вы можете просто обрезать они основаны на координатах у)
PS. Надеюсь, что "большие", "меньшие" рамки хорошо поняты, извините за мою лень, когда я не показываю, что они на изображениях.
Примечание: учитывая только одно изображение, я не могу точно сказать, работает ли оно для всех изображений в вашем наборе данных. Возможно, вам придется настроить порог и параметры morph_open. Если вы можете загрузить больше изображений, я могу примерить их.
Предоставлено: OpenCV обнаружение формы для определения формы в контурах.
Ответ 2
Из того, что я вижу, лучшим подходом было бы обнаружить края лицензии и обрезать ее. Затем, когда у вас есть координаты краев, вы можете рассчитать угол, на который вы должны повернуть изображение, чтобы оно было плоским.
Оттуда вы можете обрезать фиксированные области (по заданным координатам пикселей). На этом шаге оставьте немного места для ошибки (допустим, вы добавляете 5-10 пикселей к каждой стороне области кадрирования в качестве страховки).
Затем вы можете подавать изображения в Tesseract с опцией --psm 9
. Это будет читать текст в поле более точно, чем настройки по умолчанию.
Надеюсь, это достаточно ясно и поможет вам :)