123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704 |
- # @Author : lightXu
- # @File : choice_line_box.py
- # @Time : 2018/11/22 0022 下午 16:01
- import time
- import re
- import cv2
- import os
- import random
- import traceback
- import numpy as np
- import xml.etree.cElementTree as ET
- from segment.sheet_resolve.tools.brain_api import get_ocr_text_and_coordinate
- from segment.sheet_resolve.tools import tf_settings, utils
- from segment.sheet_resolve.analysis.choice.choice_m_row_column import get_choice_m_row_and_col
- from . import get_title_number_by_choice_m
- def get_interval(word_result_list):
- all_char_str = ''
- location = []
- for i, chars_dict in enumerate(word_result_list):
- chars_list = chars_dict['chars']
- for ele in chars_list:
- all_char_str = all_char_str + ele['char']
- location.append(ele['location'])
- pattern1 = re.compile(r"\]\[")
- pattern2 = re.compile(r"\[[ABCD]")
- def intervel(pattern):
- group_list = []
- for i in pattern.finditer(all_char_str):
- # print(i.group() + str(i.span()))
- group_list.append(list(i.span()))
- # print(group_list)
- sum_intervel = 0
- size = 0
- for group in group_list:
- left_x, right_x = location[group[0]]['left'] \
- + location[group[0]]['width'], location[group[1] - 1]['left']
- if abs(location[group[0]]['top'] - location[group[1]]['top']) < location[group[0]]['height']:
- if right_x - left_x > 0:
- sum_intervel = sum_intervel + right_x - left_x
- size += 1
- # print(sum_intervel // size)
- return sum_intervel // size
- intervel_width1 = intervel(pattern1)
- intervel_width2 = intervel(pattern2)
- return (intervel_width1 + intervel_width2) * 2 // 3
- def preprocess(image0, xe, ye):
- scale = 0
- dilate = 1
- blur = 5
- # 预处理图像
- img = image0
- # rescale the image
- if scale != 0:
- img = cv2.resize(img, None, fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC)
- # Convert to gray
- img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
- # # Apply dilation and erosion to remove some noise
- # if dilate != 0:
- # kernel = np.ones((dilate, dilate), np.uint8)
- # img = cv2.dilate(img, kernel, iterations=1)
- # img = cv2.erode(img, kernel, iterations=1)
- # Apply blur to smooth out the edges
- # if blur != 0:
- # img = cv2.GaussianBlur(img, (blur, blur), 0)
- # Apply threshold to get image with only b&w (binarization)
- img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
- # cv2.namedWindow('image', cv2.WINDOW_NORMAL)
- # cv2.imshow('image', img)
- # if cv2.waitKey(0) == 27:
- # cv2.destroyAllWindows()
- # cv2.imwrite('otsu.jpg', img)
- kernel = np.ones((ye, xe), np.uint8) # y轴膨胀, x轴膨胀
- dst = cv2.dilate(img, kernel, iterations=1)
- # cv2.imshow('dilate', dst)
- # if cv2.waitKey(0) == 27:
- # cv2.destroyAllWindows()
- return dst
- def contours(image):
- _, cnts, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
- bboxes = []
- for cnt_id, cnt in enumerate(reversed(cnts)):
- x, y, w, h = cv2.boundingRect(cnt)
- bboxes.append((x, y, x + w, y + h))
- return bboxes
- def box_coordinates(img):
- img_arr = np.asarray(img)
- def axis_break_point(img, tolerance_number, axis):
- sum_x_axis = img.sum(axis=axis)
- sum_x_axis[sum_x_axis > 255 * tolerance_number] = 1 # 白色有字
- sum_x_axis[sum_x_axis != 1] = 0 # 黑色无字
- sum_x_axis_list = list(sum_x_axis)
- sum_x_axis_list.append(0) # 最后几行到结束有字时,使索引值增加最后一位
- split_x_index = []
- num = 1
- for index, ele in enumerate(sum_x_axis_list):
- num = num % 2
- if ele == num:
- # print(i)
- num = num + 1
- split_x_index.append(index)
- # print('length: ', len(split_x_index), split_x_index)
- return split_x_index
- y_break_points_list = axis_break_point(img_arr, 1, axis=1)
- x_break_points_list = axis_break_point(img_arr, 1, axis=0)
- all_coordinates = []
- for i in range(0, len(y_break_points_list), 2): # y轴分组
- ymin = y_break_points_list[i]
- ymax = y_break_points_list[i + 1]
- for j in range(0, len(x_break_points_list), 2):
- xmin = x_break_points_list[j]
- xmax = x_break_points_list[j + 1]
- all_coordinates.append([xmin, ymin, xmax, ymax])
- return all_coordinates
- def get_choice_box_coordinate(left, top, word_result_list, choice_img, cv_box_list, choice_bbox_list):
- shape = choice_img.shape
- y, x = shape[0], shape[1]
- # cv2.imshow('ocr_region', ocr_region)
- # if cv2.waitKey(0) == 27:
- # cv2.destroyAllWindows()
- all_digital_list = []
- digital_model = re.compile(r'\d')
- for i, chars_dict in enumerate(word_result_list):
- chars_list = chars_dict['chars']
- for ele in chars_list:
- if digital_model.search(ele['char']):
- all_digital_list.append(ele)
- new_all_digital_list = []
- i = 1
- while i <= len(all_digital_list):
- pre_one = all_digital_list[i - 1]
- if i == len(all_digital_list):
- new_all_digital_list.append(pre_one)
- break
- rear_one = all_digital_list[i]
- condition1 = abs(pre_one['location']['top'] - rear_one['location']['top']) < pre_one['location'][
- 'height'] # 两字高度差小于一字高度
- condition2 = pre_one['location']['left'] + 2 * pre_one['location']['width'] > rear_one['location'][
- 'left'] # 某字宽度的2倍大于两字间间隔
- if condition1:
- if condition2:
- new_char = pre_one['char'] + rear_one['char']
- new_location = {'left': pre_one['location']['left'],
- 'top': min(pre_one['location']['top'], rear_one['location']['top']),
- 'width': rear_one['location']['left'] + rear_one['location']['width'] -
- pre_one['location']['left'],
- 'height': max(pre_one['location']['height'], rear_one['location']['height'])}
- new_all_digital_list.append({'char': new_char, 'location': new_location})
- i = i + 1 + 1
- else:
- new_all_digital_list.append(pre_one)
- i = i + 1
- else:
- new_all_digital_list.append(pre_one) # 遇到字符y轴相差过大就结束
- i = i + 1
- content_list = list()
- for index, box in enumerate(choice_bbox_list['regions']): # rcnn识别的框匹配题号
- box = box['bounding_box']
- box_coordiante = (box['xmin'], box['ymin'], box['xmax'], box['ymax'])
- horizontal = box['xmax'] - box['xmin'] >= box['ymax'] - box['ymin']
- vertical = box['xmax'] - box['xmin'] < box['ymax'] - box['ymin']
- choice_number = {'number': 999, 'location': box_coordiante}
- content_list.insert(index, choice_number)
- for digital in new_all_digital_list:
- digital_coordiante = (digital['location']['left'], digital['location']['top'],
- digital['location']['left'] + digital['location']['width'],
- digital['location']['top'] + digital['location']['height'])
- if utils.decide_coordinate_contains(digital_coordiante, box_coordiante):
- if horizontal:
- box['xmin'] = digital['location']['left'] + digital['location']['width'] + 1 # 从数字处截取
- if vertical:
- box['ymin'] = digital['location']['top'] + digital['location']['height'] + 1
- box_coordiante = (box['xmin'], box['ymin'], box['xmax'], box['ymax'])
- content_list[index]['number'] = int(digital['char'])
- content_list[index]['location'] = box_coordiante
- break
- a_z = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
- for box in content_list:
- box_coordiante = (box['location'][0], box['location'][1], box['location'][2], box['location'][3])
- mtx = []
- for cv_box in cv_box_list:
- if utils.decide_coordinate_contains(cv_box, box_coordiante): # 若fasterrcnn未识别到选项框,单独的ABCD也舍去
- mtx.append(cv_box)
- matrix = np.asarray(sorted(mtx))
- dif = matrix[1:, 0] - matrix[:-1, 2] # 后一个char的left与前一个char的right的差
- dif[dif < 0] = 0
- dif_length = np.mean(dif) # 小于平均间隔的合并
- block_list = utils.box_by_x_intervel(matrix, dif_length)
- # block_list = utils.box_by_x_intervel(matrix, 5)
- choice_count = len(block_list)
- choice_option = ','.join(list(a_z[:choice_count]))
- combine = np.asarray(sorted(block_list))
- min_temp = np.min(combine, axis=0)
- max_temp = np.max(combine, axis=0)
- abcd_coordinate = {'xmin': int(min_temp[0] + left), 'ymin': int(min_temp[1] + top),
- 'xmax': int(max_temp[2] + left), 'ymax': int(max_temp[3] + top)}
- single_height = np.mean(combine[:, 3] - combine[:, 1])
- single_width = np.mean(combine[:, 2] - combine[:, 0])
- box['location'] = abcd_coordinate
- box['single_height'] = int(single_height)
- box['single_width'] = int(single_width)
- box['default_points'] = 5
- # box['choice_option'] = choice_option
- box['choice_option'] = 'A,B,C,D'
- if max_temp[2] - min_temp[0] >= max_temp[3] - min_temp[1]: # x > y 横向
- box['row'] = 1
- box['column'] = choice_count
- else:
- box['row'] = choice_count
- box['column'] = 1
- return content_list
- def choice_line(left, top, image, choice_bbox_list, xml_path):
- a_z = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
- t1 = time.time()
- word_result_list0 = get_ocr_text_and_coordinate(image, ocr_accuracy='accurate', language_type='ENG')
- # word_result_list0 = [{'chars': [{'char': '1', 'location': {'width': 25, 'top': 11, 'left': 63, 'height': 53}}, {'char': 'A', 'location': {'width': 23, 'top': 11, 'left': 142, 'height': 53}}, {'char': '.', 'location': {'width': 35, 'top': 11, 'left': 162, 'height': 53}}, {'char': '[', 'location': {'width': 17, 'top': 11, 'left': 197, 'height': 53}}, {'char': 'B', 'location': {'width': 28, 'top': 11, 'left': 213, 'height': 53}}, {'char': ']', 'location': {'width': 18, 'top': 11, 'left': 241, 'height': 53}}, {'char': 'C', 'location': {'width': 50, 'top': 11, 'left': 290, 'height': 53}}, {'char': 'D', 'location': {'width': 59, 'top': 11, 'left': 333, 'height': 53}}, {'char': ']', 'location': {'width': 34, 'top': 11, 'left': 389, 'height': 53}}, {'char': '5', 'location': {'width': 18, 'top': 11, 'left': 519, 'height': 53}}, {'char': 'A', 'location': {'width': 24, 'top': 11, 'left': 586, 'height': 53}}, {'char': 'T', 'location': {'width': 33, 'top': 11, 'left': 603, 'height': 53}}, {'char': '[', 'location': {'width': 17, 'top': 11, 'left': 650, 'height': 53}}, {'char': 'D', 'location': {'width': 25, 'top': 11, 'left': 812, 'height': 53}}, {'char': ']', 'location': {'width': 34, 'top': 11, 'left': 842, 'height': 53}}, {'char': '9', 'location': {'width': 28, 'top': 11, 'left': 963, 'height': 53}}, {'char': '[', 'location': {'width': 24, 'top': 11, 'left': 1022, 'height': 53}}, {'char': 'A', 'location': {'width': 26, 'top': 11, 'left': 1042, 'height': 53}}, {'char': '[', 'location': {'width': 25, 'top': 11, 'left': 1091, 'height': 53}}, {'char': 'B', 'location': {'width': 18, 'top': 11, 'left': 1121, 'height': 53}}, {'char': ']', 'location': {'width': 18, 'top': 11, 'left': 1139, 'height': 53}}, {'char': '[', 'location': {'width': 24, 'top': 11, 'left': 1171, 'height': 53}}, {'char': 'C', 'location': {'width': 25, 'top': 11, 'left': 1188, 'height': 53}}, {'char': 'H', 'location': {'width': 33, 'top': 11, 'left': 1206, 'height': 53}}, {'char': ']', 'location': {'width': 26, 'top': 11, 'left': 1288, 'height': 53}}], 'location': {'width': 1256, 'top': 9, 'left': 58, 'height': 57}, 'words': ' 1 A.[B] CD]5 AT[ D]9 [A [B] [CH]'}, {'chars': [{'char': '2', 'location': {'width': 25, 'top': 96, 'left': 61, 'height': 51}}, {'char': 'A', 'location': {'width': 24, 'top': 96, 'left': 139, 'height': 51}}, {'char': '[', 'location': {'width': 16, 'top': 96, 'left': 202, 'height': 51}}, {'char': 'B', 'location': {'width': 24, 'top': 96, 'left': 215, 'height': 51}}, {'char': '3', 'location': {'width': 33, 'top': 96, 'left': 233, 'height': 51}}, {'char': 'C', 'location': {'width': 25, 'top': 96, 'left': 292, 'height': 51}}, {'char': '[', 'location': {'width': 17, 'top': 96, 'left': 348, 'height': 51}}, {'char': 'D', 'location': {'width': 26, 'top': 96, 'left': 365, 'height': 51}}, {'char': ']', 'location': {'width': 35, 'top': 96, 'left': 390, 'height': 51}}, {'char': '6', 'location': {'width': 27, 'top': 96, 'left': 519, 'height': 51}}, {'char': 'A', 'location': {'width': 23, 'top': 96, 'left': 594, 'height': 51}}, {'char': ']', 'location': {'width': 35, 'top': 96, 'left': 614, 'height': 51}}, {'char': '[', 'location': {'width': 17, 'top': 96, 'left': 649, 'height': 51}}, {'char': 'B', 'location': {'width': 25, 'top': 96, 'left': 662, 'height': 51}}, {'char': 'I', 'location': {'width': 33, 'top': 96, 'left': 680, 'height': 51}}, {'char': '[', 'location': {'width': 16, 'top': 96, 'left': 727, 'height': 51}}, {'char': '[', 'location': {'width': 23, 'top': 96, 'left': 801, 'height': 51}}, {'char': 'D', 'location': {'width': 25, 'top': 96, 'left': 821, 'height': 51}}, {'char': ']', 'location': {'width': 17, 'top': 96, 'left': 847, 'height': 51}}, {'char': '1', 'location': {'width': 24, 'top': 96, 'left': 956, 'height': 51}}, {'char': '0', 'location': {'width': 33, 'top': 96, 'left': 973, 'height': 51}}, {'char': 'L', 'location': {'width': 20, 'top': 96, 'left': 1025, 'height': 51}}, {'char': 'A', 'location': {'width': 28, 'top': 96, 'left': 1038, 'height': 51}}, {'char': '[', 'location': {'width': 23, 'top': 96, 'left': 1103, 'height': 51}}, {'char': 'B', 'location': {'width': 26, 'top': 96, 'left': 1122, 'height': 51}}, {'char': ']', 'location': {'width': 10, 'top': 96, 'left': 1148, 'height': 51}}, {'char': 'I', 'location': {'width': 20, 'top': 96, 'left': 1180, 'height': 51}}, {'char': 'C', 'location': {'width': 24, 'top': 96, 'left': 1193, 'height': 51}}, {'char': 'I', 'location': {'width': 28, 'top': 96, 'left': 1211, 'height': 51}}, {'char': '[', 'location': {'width': 23, 'top': 96, 'left': 1248, 'height': 51}}, {'char': 'D', 'location': {'width': 27, 'top': 96, 'left': 1268, 'height': 51}}, {'char': ']', 'location': {'width': 17, 'top': 96, 'left': 1295, 'height': 51}}], 'location': {'width': 1255, 'top': 94, 'left': 57, 'height': 56}, 'words': ' 2 A[B3 C[D]6 A][BI[ [D]10 LA [B] ICI [D]'}, {'chars': [{'char': '3', 'location': {'width': 25, 'top': 178, 'left': 60, 'height': 53}}, {'char': '[', 'location': {'width': 25, 'top': 178, 'left': 122, 'height': 53}}, {'char': 'A', 'location': {'width': 18, 'top': 178, 'left': 143, 'height': 53}}, {'char': ']', 'location': {'width': 18, 'top': 178, 'left': 160, 'height': 53}}, {'char': '[', 'location': {'width': 17, 'top': 178, 'left': 197, 'height': 53}}, {'char': 'B', 'location': {'width': 27, 'top': 178, 'left': 214, 'height': 53}}, {'char': ']', 'location': {'width': 10, 'top': 178, 'left': 241, 'height': 53}}, {'char': 'C', 'location': {'width': 48, 'top': 178, 'left': 293, 'height': 53}}, {'char': 'D', 'location': {'width': 118, 'top': 178, 'left': 334, 'height': 53}}, {'char': '7', 'location': {'width': 97, 'top': 178, 'left': 445, 'height': 53}}, {'char': 'I', 'location': {'width': 21, 'top': 178, 'left': 572, 'height': 53}}, {'char': 'A', 'location': {'width': 30, 'top': 178, 'left': 585, 'height': 53}}, {'char': ']', 'location': {'width': 27, 'top': 178, 'left': 620, 'height': 53}}, {'char': '[', 'location': {'width': 18, 'top': 178, 'left': 647, 'height': 53}}, {'char': 'B', 'location': {'width': 25, 'top': 178, 'left': 662, 'height': 53}}, {'char': 'I', 'location': {'width': 39, 'top': 178, 'left': 680, 'height': 53}}, {'char': 'E', 'location': {'width': 34, 'top': 178, 'left': 711, 'height': 53}}, {'char': 'C', 'location': {'width': 25, 'top': 178, 'left': 738, 'height': 53}}, {'char': 'H', 'location': {'width': 29, 'top': 178, 'left': 756, 'height': 53}}, {'char': 'E', 'location': {'width': 21, 'top': 178, 'left': 795, 'height': 53}}, {'char': 'D', 'location': {'width': 29, 'top': 178, 'left': 809, 'height': 53}}, {'char': ']', 'location': {'width': 18, 'top': 178, 'left': 845, 'height': 53}}, {'char': '1', 'location': {'width': 20, 'top': 178, 'left': 958, 'height': 53}}, {'char': '1', 'location': {'width': 31, 'top': 178, 'left': 971, 'height': 53}}, {'char': '[', 'location': {'width': 25, 'top': 178, 'left': 1020, 'height': 53}}, {'char': 'A', 'location': {'width': 27, 'top': 178, 'left': 1041, 'height': 53}}, {'char': ']', 'location': {'width': 10, 'top': 178, 'left': 1068, 'height': 53}}, {'char': '[', 'location': {'width': 25, 'top': 178, 'left': 1101, 'height': 53}}, {'char': 'B', 'location': {'width': 18, 'top': 178, 'left': 1122, 'height': 53}}, {'char': ']', 'location': {'width': 18, 'top': 178, 'left': 1141, 'height': 53}}, {'char': '[', 'location': {'width': 24, 'top': 178, 'left': 1174, 'height': 53}}, {'char': 'C', 'location': {'width': 26, 'top': 178, 'left': 1191, 'height': 53}}, {'char': 'H', 'location': {'width': 35, 'top': 178, 'left': 1209, 'height': 53}}, {'char': 'L', 'location': {'width': 30, 'top': 178, 'left': 1236, 'height': 53}}, {'char': 'D', 'location': {'width': 29, 'top': 178, 'left': 1259, 'height': 53}}, {'char': ']', 'location': {'width': 18, 'top': 178, 'left': 1294, 'height': 53}}], 'location': {'width': 1257, 'top': 175, 'left': 55, 'height': 59}, 'words': ' 3 [A][B] CD7 IA][BIECH ED]11 [A] [B] [CHLD]'}, {'chars': [{'char': '4', 'location': {'width': 50, 'top': 262, 'left': 61, 'height': 53}}, {'char': 'A', 'location': {'width': 83, 'top': 262, 'left': 104, 'height': 53}}, {'char': 'B', 'location': {'width': 83, 'top': 262, 'left': 179, 'height': 53}}, {'char': 'C', 'location': {'width': 82, 'top': 262, 'left': 255, 'height': 53}}, {'char': 'D', 'location': {'width': 56, 'top': 262, 'left': 330, 'height': 53}}, {'char': ']', 'location': {'width': 36, 'top': 262, 'left': 391, 'height': 53}}, {'char': '8', 'location': {'width': 26, 'top': 262, 'left': 516, 'height': 53}}, {'char': 'A', 'location': {'width': 25, 'top': 262, 'left': 592, 'height': 53}}, {'char': '.', 'location': {'width': 35, 'top': 262, 'left': 693, 'height': 53}}, {'char': ']', 'location': {'width': 35, 'top': 262, 'left': 843, 'height': 53}}, {'char': '1', 'location': {'width': 20, 'top': 262, 'left': 955, 'height': 53}}, {'char': '2', 'location': {'width': 30, 'top': 262, 'left': 968, 'height': 53}}, {'char': 'L', 'location': {'width': 20, 'top': 262, 'left': 1018, 'height': 53}}, {'char': 'A', 'location': {'width': 29, 'top': 262, 'left': 1031, 'height': 53}}, {'char': '[', 'location': {'width': 18, 'top': 262, 'left': 1101, 'height': 53}}, {'char': 'I', 'location': {'width': 25, 'top': 262, 'left': 1239, 'height': 53}}, {'char': 'D', 'location': {'width': 34, 'top': 262, 'left': 1257, 'height': 53}}, {'char': 'T', 'location': {'width': 30, 'top': 262, 'left': 1284, 'height': 53}}], 'location': {'width': 1258, 'top': 259, 'left': 56, 'height': 58}, 'words': ' 4ABCD]8 A.]12 LA[ IDT'}]
- t2 = time.time()
- print('choice ocr time cost: ', t2 - t1)
- # print(word_result_list0)
- # try:
- # intervel_x = get_interval(word_result_list0)
- # except Exception:
- # intervel_x = 15
- intervel_x = 3
- img = preprocess(image, intervel_x, 3)
- cv_box_list0 = box_coordinates(img)
- content_list = get_choice_box_coordinate(left, top, word_result_list0, image, cv_box_list0, choice_bbox_list)
- tree = ET.parse(xml_path) # xml tree
- for index_num, choice_box in enumerate(content_list):
- abcd = choice_box['location']
- number = str(choice_box['number'])
- tree = utils.create_xml(number, tree, abcd['xmin'], abcd['ymin'], abcd['xmax'], abcd['ymax'])
- tree.write(xml_path)
- return content_list
- def choice_m_row_col0(left, top, image, choice_bbox_list, xml_path, img, choice_box):
- a_z = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
- t1 = time.time()
- tree = ET.parse(xml_path) # xml tree
- choice_m_dict_list = []
- for index0, box in enumerate(choice_bbox_list['regions']): # rcnn识别的框匹配题号
- box = box['bounding_box']
- box['ymin'] = box['ymin'] - 3 # lf_7_2 1change
- box['xmax'] = box['xmax'] + 3
- box['ymax'] = box['ymax'] + 3
- m_left, m_top = box['xmin']+left, box['ymin']+top,
- box_coordiante = (m_left, m_top, box['xmax']+left, box['ymax']+top)
- tree = utils.create_xml('choice_m', tree,
- box_coordiante[0], box_coordiante[1],
- box_coordiante[2], box_coordiante[3])
- single_choice_m = utils.crop_region(image, box)
- row_col_dict = get_choice_m_row_and_col(m_left, m_top, single_choice_m) # 所有的小框, 行列等
- if len(row_col_dict) > 0:
- # single choice_m ocr result
- res_dict = get_ocr_text_and_coordinate(single_choice_m, ocr_accuracy='accurate', language_type='CHN_ENG')
- choice_m_cal_num = [0]
- for index, ele in enumerate(res_dict):
- words = ele['words']
- location = ele['location']
- width = int(location['width'])
- height = int(location['height'])
- if width > height:
- abcd_str = 'ABD'
- cal_num = max([words.count(char) for char in abcd_str])
- choice_m_cal_num.append(cal_num)
- max_num = max(choice_m_cal_num)
- if max_num < 2:
- direction = 180
- choice_option = a_z[:row_col_dict['cols']]
- else:
- direction = 90
- choice_option = a_z[:row_col_dict['rows']]
- row_col_dict.update({'direction': direction, 'option': choice_option})
- choice_m_dict_list.append(row_col_dict)
- title_number_by_choice_m_list = get_title_number_by_choice_m.get_title_number(row_col_dict, choice_bbox_list, img, choice_box) # choice_m相对坐标 list
- for element in title_number_by_choice_m_list:
- if row_col_dict['bounding_box'] == element['bounding_box']:
- row_col_dict.update({'title_number': element['title_number']})
- for index_num, choice_box in enumerate(choice_m_dict_list):
- if len(choice_box['bounding_box']) > 0:
- abcd = choice_box['bounding_box']
- name = '{}_{}*{}_{}'.format('choice_m', choice_box['rows'], choice_box['cols'], choice_box['direction'])
- tree = utils.create_xml(name, tree,
- abcd['xmin'], abcd['ymin'],
- abcd['xmax'], abcd['ymax'])
- tree.write(xml_path)
- return choice_m_dict_list
- def choice_bbox_vague(choice_m_list0, x_y_interval_ave, singe_box_width_height_ave, direction, image_size):
- img_width = image_size[0]
- img_height = image_size[1]
- choice_list_temp1 = []
- choice_list_temp2 = []
- index_list1 = []
- index_list2 = []
- for index, ele in enumerate(choice_m_list0):
- if int(img_width) < int(img_height):
- choice_list_temp1.append(ele)
- index_list1.append(index)
- elif int(img_width) >= int(img_height) and ele[0] < img_width // 2:
- choice_list_temp1.append(ele)
- index_list1.append(index)
- elif int(img_width) >= int(img_height) and ele[0] >= img_width // 2:
- choice_list_temp2.append(ele)
- index_list2.append(index)
- if index_list2 == []:
- choice_list = [[choice_list_temp1, index_list1]]
- else:
- choice_list = [[choice_list_temp1, index_list1]]
- choice_list.append([choice_list_temp2, index_list2])
- choice_bbox_all = []
- for choice_m_list1 in choice_list:
- choice_m_list = sorted(choice_m_list1[0], key=lambda k: k[0])
- xmin0 = [ele[0] for ele in choice_m_list]
- ymin0 = [ele[1] for ele in choice_m_list]
- xmax0 = [ele[2] for ele in choice_m_list]
- ymax0 = [ele[3] for ele in choice_m_list]
- if direction == 180: # vertical
- x_diff = x_y_interval_ave[0]
- s_width = singe_box_width_height_ave[0]
- choice_bbox = (np.hstack((np.array([min(xmin0) - x_diff - 3 * s_width, min(ymin0)]), np.array([max(xmax0), max(ymax0)])))).tolist()
- choice_box = []
- for element in choice_bbox:
- if element < 0:
- choice_box.append(0)
- else:
- choice_box.append(element)
- choice_bbox_with_index_list = (choice_box, choice_m_list1[1])
- choice_bbox_all.append(choice_bbox_with_index_list)
- elif direction == 90:
- y_diff = x_y_interval_ave[1]
- s_height = singe_box_width_height_ave[1]
- choice_bbox = (np.hstack((np.array([min(xmin0), min(ymin0) - y_diff - 3 * s_height]), np.array([max(xmax0), max(ymax0)])))).tolist()
- choice_box = []
- for element in choice_bbox:
- if element < 0:
- choice_box.append(0)
- else:
- choice_box.append(element)
- choice_bbox_with_index_list = (choice_box, choice_m_list1[1])
- choice_bbox_all.append(choice_bbox_with_index_list)
- return choice_bbox_all
- def get_direction(words_res_dict, choice_m_mum=1):
- choice_m_cal_num = [0]
- for index, ele in enumerate(words_res_dict):
- words = ele['words']
- location = ele['location']
- width = int(location['width'])
- height = int(location['height'])
- if width > height:
- abcd_str = 'ABD'
- cal_num = max([words.count(char) for char in abcd_str])
- choice_m_cal_num.append(cal_num)
- # max_num = max(choice_m_cal_num)
- counts = np.bincount(np.array(choice_m_cal_num))
- mode = int(counts.argmax())
- # if max_num/choice_m_mum < 2:
- if mode < 2:
- direction = 180
- else:
- direction = 90
- return direction
- def choice_m_adjust(image, choice_m_bbox_list):
- a_z = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
- for index0, choice_m in enumerate(choice_m_bbox_list): # rcnn识别的框匹配题号
- box = choice_m['bounding_box']
- box['ymin'] = choice_m['ymin'] - 3 # lf_7_2 1change
- box['xmax'] = choice_m['xmax'] + 3
- box['ymax'] = choice_m['ymax'] + 3
- m_left, m_top = box['xmin'], box['ymin'],
- # box_coordiante = (m_left, m_top, box['xmax'], box['ymax'])
- single_choice_m = utils.crop_region(image, box)
- row_col_dict = get_choice_m_row_and_col(m_left, m_top, single_choice_m) # 所有的小框, 行列等
- if len(row_col_dict) > 0:
- if choice_m['direction'] == 90:
- choice_option = a_z[:row_col_dict['rows']].replace('', ',')[1:-1]
- else:
- choice_option = a_z[:row_col_dict['cols']].replace('', ',')[1:-1]
- choice_m.update(row_col_dict) # 更新 bounding_box
- choice_m.update({'choice_option': choice_option})
- return choice_m_bbox_list
- def choice_m_row_col(image, choice_m_bbox_list, direction, subject, xml_path):
- a_z = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
- choice_m_dict_list = []
- # 或在长宽比接近的choice_m中选取
- if not direction:
- random_one = random.randint(0, len(choice_m_bbox_list)-1)
- choice_m_for_dircetion = utils.crop_region(image, choice_m_bbox_list[random_one]['bounding_box'])
- res_dict = get_ocr_text_and_coordinate(choice_m_for_dircetion, ocr_accuracy='accurate', language_type='ENG')
- direction = get_direction(res_dict)
- for index0, box in enumerate(choice_m_bbox_list): # rcnn识别的框匹配题号
- box = box['bounding_box']
- box['ymin'] = box['ymin'] - 3 # lf_7_2 1change
- box['xmax'] = box['xmax'] + 3
- box['ymax'] = box['ymax'] + 3
- m_left, m_top = box['xmin'], box['ymin'],
- # box_coordiante = (m_left, m_top, box['xmax'], box['ymax'])
- single_choice_m = utils.crop_region(image, box)
- try:
- row_col_dict = get_choice_m_row_and_col(m_left, m_top, single_choice_m) # 所有的小框,行列
- if len(row_col_dict) > 0:
- if direction == 90:
- choice_option = a_z[:row_col_dict['rows']].replace('', ',')[1:-1]
- if 'number' in box.keys():
- title_number = box['number']
- else:
- title_number = [-1] * row_col_dict['cols']
- default_points = [-1] * row_col_dict['cols']
- else:
- choice_option = a_z[:row_col_dict['cols']].replace('', ',')[1:-1]
- if 'number' in box.keys():
- title_number = box['number']
- else:
- title_number = [-1] * row_col_dict['rows']
- default_points = [-1] * row_col_dict['rows']
- row_col_dict.update({'direction': direction, 'option': choice_option,
- 'number': title_number, 'default_points': default_points})
- choice_m_dict_list.append(row_col_dict)
- # else:
- # del choice_m_bbox_list[index0]
- except Exception:
- pass
- try:
- choice_m_box_dict = get_title_number_by_choice_m.analysis_s_box(choice_m_dict_list)
- choice_m_list = [[ele['bounding_box']['xmin'], ele['bounding_box']['ymin'],
- ele['bounding_box']['xmax'], ele['bounding_box']['ymax']] for ele in choice_m_box_dict]
- x_y_interval_all = []
- s_box_w_h = []
- rows_list = [ele['rows'] for ele in choice_m_box_dict if ele['rows'] == 1]
- if len(rows_list) == len(choice_m_box_dict):
- w_list = [ele['single_width'] for ele in choice_m_box_dict]
- h_list = [ele['single_height'] for ele in choice_m_box_dict]
- w_arr = np.array(w_list)
- h_arr = np.array(h_list)
- s_box_w_h.append((int(np.mean(w_arr)), int(np.mean(h_arr))))
- x_y_interval = s_box_w_h
- x_y_interval_all.append(x_y_interval)
- else:
- for index, s_box in enumerate(choice_m_box_dict):
- all_small_coordinate_dict = s_box['all_small_coordinate']
- all_small_coordinate_list = [[ele['xmin'], ele['ymin'], ele['xmax'], ele['ymax']] for ele in
- all_small_coordinate_dict]
- col = s_box['cols']
- rows = s_box['rows']
- if rows == 1:
- continue
- else:
- x_y_interval = utils.get_x_diff_and_y_diff1(all_small_coordinate_list, col)
- x_y_interval_all.append(x_y_interval)
- all_small_coordinate_list = sorted(all_small_coordinate_list, key=lambda k: k[1])
- s_box_array = np.array(all_small_coordinate_list)
- s_box_wid_hei = (
- int(np.mean(s_box_array[:, 2])) - int(np.mean(s_box_array[:, 0])),
- int(np.mean(s_box_array[:, 3])) - int(np.mean(s_box_array[:, 1])))
- s_box_w_h.append(s_box_wid_hei)
- whether_nan = [ele for index, ele in enumerate(x_y_interval_all) if 'nan' in ele]
- if len(whether_nan) == 0:
- x_y_interval_all0 = x_y_interval_all
- else:
- if len(x_y_interval_all) == 1:
- x_y_interval_all0 = s_box_w_h
- else:
- x_y_interval_all1 = []
- for element in x_y_interval_all:
- if element not in whether_nan:
- x_y_interval_all1.append(element)
- x_y_interval_all2 = [x_y_interval_all1[0] for i in range(len(whether_nan))]
- x_y_interval_all0 = x_y_interval_all1 + x_y_interval_all2
- x_y_interval_arr = np.array(x_y_interval_all0)
- if len(x_y_interval_arr) == 1 and len(rows_list) != 0:
- x_y_interval_all_arr = np.array(x_y_interval_all)
- x_ = int(np.mean(x_y_interval_all_arr[:, 0][0][0]))
- y_ = int(np.mean(x_y_interval_all_arr[:, 0][0][1]))
- x_y_interval_ave = (x_, y_)
- singe_box_width_height_ave = s_box_w_h[0]
- image_height, image_width, _ = image.shape
- image_size = (image_width, image_height)
- elif len(x_y_interval_arr) == 1 and len(rows_list) == 0:
- x_y_interval_all_arr = np.array(x_y_interval_all)
- x_ = int(np.mean(x_y_interval_all_arr[0][0]))
- y_ = int(np.mean(x_y_interval_all_arr[0][1]))
- x_y_interval_ave = (x_, y_)
- singe_box_width_height_ave = s_box_w_h[0]
- image_height, image_width, _ = image.shape
- image_size = (image_width, image_height)
- else:
- x_y_interval_ave = (int(np.mean(x_y_interval_arr[:, 0])), int(np.mean(x_y_interval_arr[:, 1])))
- s_box_w_h_arr = np.array(s_box_w_h)
- singe_box_width_height_ave = (int(np.mean(s_box_w_h_arr[:, 0])), int(np.mean(s_box_w_h_arr[:, 1])))
- image_height, image_width, _ = image.shape
- image_size = (image_width, image_height)
- choice_bbox = choice_bbox_vague(choice_m_list, x_y_interval_ave, singe_box_width_height_ave, direction,
- image_size)
- choice_m_dict_list_all_tmp = []
- for index, choice_box_ele in enumerate(choice_bbox):
- choice_bbox_ele = []
- if direction == 180:
- choice_bbox_ele0 = choice_box_ele[0]
- choice_bbox_ele = [choice_bbox_ele0[0], choice_bbox_ele0[1] - 2,
- choice_bbox_ele0[2], choice_bbox_ele0[3] + 5]
- elif direction == 90:
- choice_bbox_ele = choice_box_ele[0]
- choice_region = utils.crop_region_direct(image, choice_bbox_ele)
- choice_path = xml_path[: xml_path.rfind('/')]
- cv2.imwrite(os.path.join(choice_path, 'choice_region_' + str(index) + '.jpg'), choice_region)
- choice_m_box_dict_new = [choice_m_box_dict[i] for i in choice_box_ele[1]]
- choice_m_dict_list_part = get_title_number_by_choice_m.get_title_number(choice_bbox_ele, choice_region,
- image, choice_m_box_dict_new,
- x_y_interval_ave, subject, direction)
- choice_m_dict_list_all_tmp.append(choice_m_dict_list_part)
- if len(choice_m_dict_list_all_tmp) == 1:
- choice_m_dict_list_all = choice_m_dict_list_all_tmp[0]
- else:
- choice_m_dict_list_all = []
- for i in choice_m_dict_list_all_tmp:
- choice_m_dict_list_all = choice_m_dict_list_all + i
- except Exception as e:
- print(e)
- traceback.print_exc()
- choice_m_dict_list_all = choice_m_dict_list
- return choice_m_dict_list_all
- def choice_line_with_number(left, top, image, choice_bbox_list, xml_path):
- t1 = time.time()
- word_result_list0 = get_ocr_text_and_coordinate(image, ocr_accuracy='accurate', language_type='ENG')
- t2 = time.time()
- print('choice ocr time cost: ', t2 - t1)
- # print(word_result_list0)
- intervel_x = 3
- img = preprocess(image, intervel_x, 3)
- cv_box_list = box_coordinates(img)
- content_list = get_choice_line_box_coordinate(left, top, word_result_list0, cv_box_list, choice_bbox_list)
- return content_list
- def get_choice_line_box_coordinate(left, top, word_result_list, cv_box_list, choice_bbox_list):
- all_digital_list = []
- digital_model = re.compile(r'\d')
- for i, chars_dict in enumerate(word_result_list):
- chars_list = chars_dict['chars']
- for ele in chars_list:
- if digital_model.search(ele['char']):
- all_digital_list.append(ele)
- new_all_digital_list = []
- i = 1
- while i <= len(all_digital_list):
- pre_one = all_digital_list[i - 1]
- if i == len(all_digital_list):
- new_all_digital_list.append(pre_one)
- break
- rear_one = all_digital_list[i]
- condition1 = abs(pre_one['location']['top'] - rear_one['location']['top']) < pre_one['location'][
- 'height'] # 两字高度差小于一字高度
- condition2 = pre_one['location']['left'] + 2 * pre_one['location']['width'] > rear_one['location'][
- 'left'] # 某字宽度的2倍大于两字间间隔
- if condition1:
- if condition2:
- new_char = pre_one['char'] + rear_one['char']
- new_location = {'left': pre_one['location']['left'],
- 'top': min(pre_one['location']['top'], rear_one['location']['top']),
- 'width': rear_one['location']['left'] + rear_one['location']['width'] -
- pre_one['location']['left'],
- 'height': max(pre_one['location']['height'], rear_one['location']['height'])}
- new_all_digital_list.append({'char': new_char, 'location': new_location})
- i = i + 1 + 1
- else:
- new_all_digital_list.append(pre_one)
- i = i + 1
- else:
- new_all_digital_list.append(pre_one) # 遇到字符y轴相差过大就结束
- i = i + 1
- content_list = list()
- new_all_digital_list = sorted(new_all_digital_list, key=lambda k: k.get('location').get('top'))
- for index, box in enumerate(
- sorted(choice_bbox_list['regions'], key=lambda k: k.get('bounding_box').get('ymin'))): # rcnn识别的框匹配题号
- box = box['bounding_box']
- box_coordinate = (box['xmin'] + left, box['ymin'] + top, box['xmax'] + left, box['ymax'] + top)
- choice_number = {'number': 999, 'location': box_coordinate}
- content_list.insert(index, choice_number)
- for digital in new_all_digital_list:
- digital_coordinate = (digital['location']['left'] + left, digital['location']['top'] + top,
- digital['location']['left'] + digital['location']['width'] + left,
- digital['location']['top'] + digital['location']['height'] + top)
- if utils.decide_coordinate_contains(digital_coordinate, box_coordinate):
- content_list[index]['number'] = digital['char']
- new_all_digital_list.remove(digital)
- break
- for box in content_list: # 计算间距
- box_coordiante = (box['location'][0], box['location'][1], box['location'][2], box['location'][3])
- mtx = []
- for cv_box in cv_box_list:
- if utils.decide_coordinate_contains(cv_box, box_coordiante): # 若fasterrcnn未识别到选项框,单独的ABCD也舍去
- mtx.append(cv_box)
- matrix = np.asarray(sorted(mtx))
- dif = matrix[1:, 0] - matrix[:-1, 2] # 后一个char的left与起一个char的right的差
- dif[dif < 0] = 0
- dif_length = np.mean(dif) # 小于平均间隔的合并
- block_list = utils.box_by_x_intervel(matrix, dif_length)
- # block_list = utils.box_by_x_intervel(matrix, 5)
- box['abcd'] = block_list
- matrix_distance = np.asarray(sorted(block_list))
- dif = matrix_distance[1:, 0] - matrix_distance[:-1, 2] # 后一个char的left与起一个char的right的差
- distance = np.mean(dif)
- return content_list
|