# Import required packages
import cv2
import numpy as np
import pytesseract
# Mention the installed location of Tesseract-OCR in your system
pytesseract.pytesseract.tesseract_cmd = 'C:\\Program Files\\Tesseract-OCR\\tesseract.exe'
# Read image from which text needs to be extracted
img = cv2.imread("origin.jpg")
height, width,c = img.shape
img = img[60:height-60,:]
#=== 흰색영역 마스크 ===#
mark = np.copy(img)
# BGR 제한 값 설정
blue_threshold = 200
green_threshold = 200
red_threshold = 200
bgr_threshold = [blue_threshold, green_threshold, red_threshold]
# BGR 제한 값보다 작으면 검은색으로
thresholds = (img[:,:,0] < bgr_threshold[0]) \
| (img[:,:,1] < bgr_threshold[1]) \
| (img[:,:,2] < bgr_threshold[2])
mark[thresholds] = [0,0,0]
#======#
#노이즈제거
img = cv2.fastNlMeansDenoisingColored(mark,None,15,15,5,15)
#======#
# morphology
structuringElement = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
imgTopHat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, structuringElement)
imgBlackHat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, structuringElement)
imgGrayscalePlusTopHat = cv2.add(img, imgTopHat)
gray = cv2.subtract(imgGrayscalePlusTopHat, imgBlackHat)
#======#
gray = cv2.cvtColor(gray, cv2.COLOR_BGR2GRAY)
height, width,channel = img.shape
# 윤곽
ret, thresh = cv2.threshold(gray,127,255, cv2.THRESH_BINARY)
contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#======#
# 윤곽 그리기
temp_result = np.zeros((height, width, channel), dtype=np.uint8)
cv2.drawContours(temp_result, contours=contours, contourIdx=-1,
color=(255, 255, 255))
temp_result = np.zeros((height, width, channel), dtype=np.uint8)
contours_dict = []
for contour in contours:
x, y, w, h = cv2.boundingRect(contour)
cv2.rectangle(temp_result, pt1=(x, y), pt2=(x + w, y + h),
color=(255, 255, 255), thickness=2)
contours_dict.append({
'contour': contour,
'x': x,
'y': y,
'w': w,
'h': h,
'cx': x + (w / 2),
'cy': y + (h / 2)
})
#======#
# cv2.imshow("temp_result", temp_result)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
MIN_AREA = 200
MIN_WIDTH, MIN_HEIGHT = 2, 10
MIN_RATIO, MAX_RATIO = 0.25, 1.0
possible_contours = []
cnt = 0
for d in contours_dict:
area = d['w'] * d['h']
ratio = d['w'] / d['h']
if area > MIN_AREA \
and d['w'] > MIN_WIDTH and d['h'] > MIN_HEIGHT \
and MIN_RATIO < ratio < MAX_RATIO:
d['idx'] = cnt
cnt += 1
possible_contours.append(d)
temp_result = np.zeros((height, width, channel), dtype=np.uint8)
for d in possible_contours:
cv2.rectangle(temp_result, pt1=(d['x'], d['y']), pt2=(d['x'] + d['w'], d['y'] + d['h']),
color=(255, 255, 255), thickness=2)
cv2.imshow("temp_result", temp_result)
cv2.waitKey(0)
cv2.destroyAllWindows()
MAX_DIAG_MULTIPLYER = 5
MAX_ANGLE_DIFF = 12.0
MAX_AREA_DIFF = 0.5
MAX_WIDTH_DIFF = 0.8
MAX_HEIGHT_DIFF = 0.1
MIN_N_MATCHED = 3
def find_chars(contour_list):
matched_result_idx = []
for d1 in contour_list:
matched_contours_idx = []
for d2 in contour_list:
if d1['idx'] == d2['idx']:
continue
dx = abs(d1['cx'] - d2['cx'])
dy = abs(d1['cy'] - d2['cy'])
diagonal_length1 = np.sqrt(d1['w'] ** 2 + d1['h'] ** 2)
distance = np.linalg.norm(np.array([d1['cx'], d1['cy']]) - np.array([d2['cx'], d2['cy']]))
if dx == 0:
angle_diff = 90
else:
angle_diff = np.degrees(np.arctan(dy / dx))
area_diff = abs(d1['w'] * d1['h'] - d2['w'] * d2['h']) / (d1['w'] * d1['h'])
width_diff = abs(d1['w'] - d2['w']) / d1['w']
height_diff = abs(d1['h'] - d2['h']) / d1['h']
if distance < diagonal_length1 * MAX_DIAG_MULTIPLYER \
and angle_diff < MAX_ANGLE_DIFF and area_diff < MAX_AREA_DIFF \
and width_diff < MAX_WIDTH_DIFF and height_diff < MAX_HEIGHT_DIFF:
matched_contours_idx.append(d2['idx'])
matched_contours_idx.append(d1['idx'])
if len(matched_contours_idx) < MIN_N_MATCHED:
continue
matched_result_idx.append(matched_contours_idx)
unmatched_contour_idx = []
for d4 in contour_list:
if d4['idx'] not in matched_contours_idx:
unmatched_contour_idx.append(d4['idx'])
unmatched_contour = np.take(possible_contours,
unmatched_contour_idx)
recursive_contour_list = find_chars(unmatched_contour)
for idx in recursive_contour_list:
matched_result_idx.append(idx)
break
return matched_result_idx
result_idx = find_chars(possible_contours)
matched_result = []
for idx_list in result_idx:
matched_result.append(np.take(possible_contours, idx_list))
temp_result = np.zeros((height, width, channel), dtype=np.uint8)
for r in matched_result:
for d in r:
cv2.rectangle(temp_result, pt1=(d['x'], d['y']),
pt2=(d['x'] + d['w'], d['y'] + d['h']),
color=(255, 255, 255), thickness=2)
cv2.imshow("matched_result", temp_result)
cv2.waitKey(0)
cv2.destroyAllWindows()
PLATE_WIDTH_PADDING = 5
PLATE_HEIGHT_PADDING = 5
MIN_PLATE_RATIO = 4
MAX_PLATE_RATIO = 10
plate_imgs = []
plate_infos = []
for i, matched_chars in enumerate(matched_result):
sorted_chars = sorted(matched_chars, key=lambda x: x['cx'])
plate_cx = (sorted_chars[0]['cx'] + sorted_chars[-1]['cx']) / 2
plate_cy = (sorted_chars[0]['cy'] + sorted_chars[-1]['cy']) / 2
plate_width = (sorted_chars[-1]['x'] + sorted_chars[-1]['w'] - sorted_chars[0]['x']) * PLATE_WIDTH_PADDING
sum_height = 0
for d in sorted_chars:
sum_height += d['h']
plate_height = int(sum_height / len(sorted_chars) * PLATE_HEIGHT_PADDING)
triangle_height = sorted_chars[-1]['cy'] - sorted_chars[0]['cy']
triangle_hypotenus = np.linalg.norm(
np.array([sorted_chars[0]['cx'], sorted_chars[0]['cy']]) -
np.array([sorted_chars[-1]['cx'], sorted_chars[-1]['cy']])
)
angle = np.degrees(np.arcsin(triangle_height / triangle_hypotenus))
rotation_matrix = cv2.getRotationMatrix2D(center=(plate_cx, plate_cy), angle=angle, scale=1.0)
img_rotated = cv2.warpAffine(thresh, M=rotation_matrix, dsize=(width, height))
img_cropped = cv2.getRectSubPix(
img_rotated,
patchSize=(int(plate_width), int(plate_height)),
center=(int(plate_cx), int(plate_cy))
)
if img_cropped.shape[1] / img_cropped.shape[0] < MIN_PLATE_RATIO or img_cropped.shape[1] / img_cropped.shape[0] < MIN_PLATE_RATIO > MAX_PLATE_RATIO:
continue
plate_imgs.append(img_cropped)
plate_infos.append({
'x': int(plate_cx - plate_width / 2),
'y': int(plate_cy - plate_height / 2),
'w': int(plate_width),
'h': int(plate_height)
})
cv2.imshow("img_cropped", img_cropped)
cv2.waitKey(0)
cv2.destroyAllWindows()
img_result = cv2.GaussianBlur(img_cropped, ksize=(3, 3), sigmaX=0)
_, img_result = cv2.threshold(img_result, thresh=0.0, maxval=255.0, type=cv2.THRESH_BINARY | cv2.THRESH_OTSU)
img_result = cv2.copyMakeBorder(img_result, top=10, bottom=10, left=10, right=10, borderType=cv2.BORDER_CONSTANT, value=(0,0,0))
img_result = cv2.GaussianBlur(img_result, ksize=(3, 3), sigmaX=0)
_, img_result = cv2.threshold(img_result, thresh=0.0, maxval=255.0, type=cv2.THRESH_BINARY | cv2.THRESH_OTSU)
img_result = cv2.copyMakeBorder(img_result, top=10, bottom=10, left=10, right=10, borderType=cv2.BORDER_CONSTANT, value=(0,0,0))
cv2.imshow("img_thresh", img_result)
cv2.waitKey(0)
cv2.destroyAllWindows()
chars = pytesseract.image_to_string(img_result, lang='eng')
print(chars)