choice_line_box.py 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. # @Author : lightXu
  2. # @File : choice_line_box.py
  3. # @Time : 2018/11/22 0022 下午 16:01
  4. import time
  5. import re
  6. import cv2
  7. import os
  8. import random
  9. import traceback
  10. import numpy as np
  11. import xml.etree.cElementTree as ET
  12. from segment.sheet_resolve.tools.brain_api import get_ocr_text_and_coordinate
  13. from segment.sheet_resolve.tools import tf_settings, utils
  14. from segment.sheet_resolve.analysis.choice.choice_m_row_column import get_choice_m_row_and_col
  15. from . import get_title_number_by_choice_m
  16. def get_interval(word_result_list):
  17. all_char_str = ''
  18. location = []
  19. for i, chars_dict in enumerate(word_result_list):
  20. chars_list = chars_dict['chars']
  21. for ele in chars_list:
  22. all_char_str = all_char_str + ele['char']
  23. location.append(ele['location'])
  24. pattern1 = re.compile(r"\]\[")
  25. pattern2 = re.compile(r"\[[ABCD]")
  26. def intervel(pattern):
  27. group_list = []
  28. for i in pattern.finditer(all_char_str):
  29. # print(i.group() + str(i.span()))
  30. group_list.append(list(i.span()))
  31. # print(group_list)
  32. sum_intervel = 0
  33. size = 0
  34. for group in group_list:
  35. left_x, right_x = location[group[0]]['left'] \
  36. + location[group[0]]['width'], location[group[1] - 1]['left']
  37. if abs(location[group[0]]['top'] - location[group[1]]['top']) < location[group[0]]['height']:
  38. if right_x - left_x > 0:
  39. sum_intervel = sum_intervel + right_x - left_x
  40. size += 1
  41. # print(sum_intervel // size)
  42. return sum_intervel // size
  43. intervel_width1 = intervel(pattern1)
  44. intervel_width2 = intervel(pattern2)
  45. return (intervel_width1 + intervel_width2) * 2 // 3
  46. def preprocess(image0, xe, ye):
  47. scale = 0
  48. dilate = 1
  49. blur = 5
  50. # 预处理图像
  51. img = image0
  52. # rescale the image
  53. if scale != 0:
  54. img = cv2.resize(img, None, fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC)
  55. # Convert to gray
  56. img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  57. # # Apply dilation and erosion to remove some noise
  58. # if dilate != 0:
  59. # kernel = np.ones((dilate, dilate), np.uint8)
  60. # img = cv2.dilate(img, kernel, iterations=1)
  61. # img = cv2.erode(img, kernel, iterations=1)
  62. # Apply blur to smooth out the edges
  63. # if blur != 0:
  64. # img = cv2.GaussianBlur(img, (blur, blur), 0)
  65. # Apply threshold to get image with only b&w (binarization)
  66. img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
  67. # cv2.namedWindow('image', cv2.WINDOW_NORMAL)
  68. # cv2.imshow('image', img)
  69. # if cv2.waitKey(0) == 27:
  70. # cv2.destroyAllWindows()
  71. # cv2.imwrite('otsu.jpg', img)
  72. kernel = np.ones((ye, xe), np.uint8) # y轴膨胀, x轴膨胀
  73. dst = cv2.dilate(img, kernel, iterations=1)
  74. # cv2.imshow('dilate', dst)
  75. # if cv2.waitKey(0) == 27:
  76. # cv2.destroyAllWindows()
  77. return dst
  78. def contours(image):
  79. _, cnts, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  80. bboxes = []
  81. for cnt_id, cnt in enumerate(reversed(cnts)):
  82. x, y, w, h = cv2.boundingRect(cnt)
  83. bboxes.append((x, y, x + w, y + h))
  84. return bboxes
  85. def box_coordinates(img):
  86. img_arr = np.asarray(img)
  87. def axis_break_point(img, tolerance_number, axis):
  88. sum_x_axis = img.sum(axis=axis)
  89. sum_x_axis[sum_x_axis > 255 * tolerance_number] = 1 # 白色有字
  90. sum_x_axis[sum_x_axis != 1] = 0 # 黑色无字
  91. sum_x_axis_list = list(sum_x_axis)
  92. sum_x_axis_list.append(0) # 最后几行到结束有字时,使索引值增加最后一位
  93. split_x_index = []
  94. num = 1
  95. for index, ele in enumerate(sum_x_axis_list):
  96. num = num % 2
  97. if ele == num:
  98. # print(i)
  99. num = num + 1
  100. split_x_index.append(index)
  101. # print('length: ', len(split_x_index), split_x_index)
  102. return split_x_index
  103. y_break_points_list = axis_break_point(img_arr, 1, axis=1)
  104. x_break_points_list = axis_break_point(img_arr, 1, axis=0)
  105. all_coordinates = []
  106. for i in range(0, len(y_break_points_list), 2): # y轴分组
  107. ymin = y_break_points_list[i]
  108. ymax = y_break_points_list[i + 1]
  109. for j in range(0, len(x_break_points_list), 2):
  110. xmin = x_break_points_list[j]
  111. xmax = x_break_points_list[j + 1]
  112. all_coordinates.append([xmin, ymin, xmax, ymax])
  113. return all_coordinates
  114. def get_choice_box_coordinate(left, top, word_result_list, choice_img, cv_box_list, choice_bbox_list):
  115. shape = choice_img.shape
  116. y, x = shape[0], shape[1]
  117. # cv2.imshow('ocr_region', ocr_region)
  118. # if cv2.waitKey(0) == 27:
  119. # cv2.destroyAllWindows()
  120. all_digital_list = []
  121. digital_model = re.compile(r'\d')
  122. for i, chars_dict in enumerate(word_result_list):
  123. chars_list = chars_dict['chars']
  124. for ele in chars_list:
  125. if digital_model.search(ele['char']):
  126. all_digital_list.append(ele)
  127. new_all_digital_list = []
  128. i = 1
  129. while i <= len(all_digital_list):
  130. pre_one = all_digital_list[i - 1]
  131. if i == len(all_digital_list):
  132. new_all_digital_list.append(pre_one)
  133. break
  134. rear_one = all_digital_list[i]
  135. condition1 = abs(pre_one['location']['top'] - rear_one['location']['top']) < pre_one['location'][
  136. 'height'] # 两字高度差小于一字高度
  137. condition2 = pre_one['location']['left'] + 2 * pre_one['location']['width'] > rear_one['location'][
  138. 'left'] # 某字宽度的2倍大于两字间间隔
  139. if condition1:
  140. if condition2:
  141. new_char = pre_one['char'] + rear_one['char']
  142. new_location = {'left': pre_one['location']['left'],
  143. 'top': min(pre_one['location']['top'], rear_one['location']['top']),
  144. 'width': rear_one['location']['left'] + rear_one['location']['width'] -
  145. pre_one['location']['left'],
  146. 'height': max(pre_one['location']['height'], rear_one['location']['height'])}
  147. new_all_digital_list.append({'char': new_char, 'location': new_location})
  148. i = i + 1 + 1
  149. else:
  150. new_all_digital_list.append(pre_one)
  151. i = i + 1
  152. else:
  153. new_all_digital_list.append(pre_one) # 遇到字符y轴相差过大就结束
  154. i = i + 1
  155. content_list = list()
  156. for index, box in enumerate(choice_bbox_list['regions']): # rcnn识别的框匹配题号
  157. box = box['bounding_box']
  158. box_coordiante = (box['xmin'], box['ymin'], box['xmax'], box['ymax'])
  159. horizontal = box['xmax'] - box['xmin'] >= box['ymax'] - box['ymin']
  160. vertical = box['xmax'] - box['xmin'] < box['ymax'] - box['ymin']
  161. choice_number = {'number': 999, 'location': box_coordiante}
  162. content_list.insert(index, choice_number)
  163. for digital in new_all_digital_list:
  164. digital_coordiante = (digital['location']['left'], digital['location']['top'],
  165. digital['location']['left'] + digital['location']['width'],
  166. digital['location']['top'] + digital['location']['height'])
  167. if utils.decide_coordinate_contains(digital_coordiante, box_coordiante):
  168. if horizontal:
  169. box['xmin'] = digital['location']['left'] + digital['location']['width'] + 1 # 从数字处截取
  170. if vertical:
  171. box['ymin'] = digital['location']['top'] + digital['location']['height'] + 1
  172. box_coordiante = (box['xmin'], box['ymin'], box['xmax'], box['ymax'])
  173. content_list[index]['number'] = int(digital['char'])
  174. content_list[index]['location'] = box_coordiante
  175. break
  176. a_z = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  177. for box in content_list:
  178. box_coordiante = (box['location'][0], box['location'][1], box['location'][2], box['location'][3])
  179. mtx = []
  180. for cv_box in cv_box_list:
  181. if utils.decide_coordinate_contains(cv_box, box_coordiante): # 若fasterrcnn未识别到选项框,单独的ABCD也舍去
  182. mtx.append(cv_box)
  183. matrix = np.asarray(sorted(mtx))
  184. dif = matrix[1:, 0] - matrix[:-1, 2] # 后一个char的left与前一个char的right的差
  185. dif[dif < 0] = 0
  186. dif_length = np.mean(dif) # 小于平均间隔的合并
  187. block_list = utils.box_by_x_intervel(matrix, dif_length)
  188. # block_list = utils.box_by_x_intervel(matrix, 5)
  189. choice_count = len(block_list)
  190. choice_option = ','.join(list(a_z[:choice_count]))
  191. combine = np.asarray(sorted(block_list))
  192. min_temp = np.min(combine, axis=0)
  193. max_temp = np.max(combine, axis=0)
  194. abcd_coordinate = {'xmin': int(min_temp[0] + left), 'ymin': int(min_temp[1] + top),
  195. 'xmax': int(max_temp[2] + left), 'ymax': int(max_temp[3] + top)}
  196. single_height = np.mean(combine[:, 3] - combine[:, 1])
  197. single_width = np.mean(combine[:, 2] - combine[:, 0])
  198. box['location'] = abcd_coordinate
  199. box['single_height'] = int(single_height)
  200. box['single_width'] = int(single_width)
  201. box['default_points'] = 5
  202. # box['choice_option'] = choice_option
  203. box['choice_option'] = 'A,B,C,D'
  204. if max_temp[2] - min_temp[0] >= max_temp[3] - min_temp[1]: # x > y 横向
  205. box['row'] = 1
  206. box['column'] = choice_count
  207. else:
  208. box['row'] = choice_count
  209. box['column'] = 1
  210. return content_list
  211. def choice_line(left, top, image, choice_bbox_list, xml_path):
  212. a_z = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  213. t1 = time.time()
  214. word_result_list0 = get_ocr_text_and_coordinate(image, ocr_accuracy='accurate', language_type='ENG')
  215. # 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'}]
  216. t2 = time.time()
  217. print('choice ocr time cost: ', t2 - t1)
  218. # print(word_result_list0)
  219. # try:
  220. # intervel_x = get_interval(word_result_list0)
  221. # except Exception:
  222. # intervel_x = 15
  223. intervel_x = 3
  224. img = preprocess(image, intervel_x, 3)
  225. cv_box_list0 = box_coordinates(img)
  226. content_list = get_choice_box_coordinate(left, top, word_result_list0, image, cv_box_list0, choice_bbox_list)
  227. tree = ET.parse(xml_path) # xml tree
  228. for index_num, choice_box in enumerate(content_list):
  229. abcd = choice_box['location']
  230. number = str(choice_box['number'])
  231. tree = utils.create_xml(number, tree, abcd['xmin'], abcd['ymin'], abcd['xmax'], abcd['ymax'])
  232. tree.write(xml_path)
  233. return content_list
  234. def choice_m_row_col0(left, top, image, choice_bbox_list, xml_path, img, choice_box):
  235. a_z = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  236. t1 = time.time()
  237. tree = ET.parse(xml_path) # xml tree
  238. choice_m_dict_list = []
  239. for index0, box in enumerate(choice_bbox_list['regions']): # rcnn识别的框匹配题号
  240. box = box['bounding_box']
  241. box['ymin'] = box['ymin'] - 3 # lf_7_2 1change
  242. box['xmax'] = box['xmax'] + 3
  243. box['ymax'] = box['ymax'] + 3
  244. m_left, m_top = box['xmin']+left, box['ymin']+top,
  245. box_coordiante = (m_left, m_top, box['xmax']+left, box['ymax']+top)
  246. tree = utils.create_xml('choice_m', tree,
  247. box_coordiante[0], box_coordiante[1],
  248. box_coordiante[2], box_coordiante[3])
  249. single_choice_m = utils.crop_region(image, box)
  250. row_col_dict = get_choice_m_row_and_col(m_left, m_top, single_choice_m) # 所有的小框, 行列等
  251. if len(row_col_dict) > 0:
  252. # single choice_m ocr result
  253. res_dict = get_ocr_text_and_coordinate(single_choice_m, ocr_accuracy='accurate', language_type='CHN_ENG')
  254. choice_m_cal_num = [0]
  255. for index, ele in enumerate(res_dict):
  256. words = ele['words']
  257. location = ele['location']
  258. width = int(location['width'])
  259. height = int(location['height'])
  260. if width > height:
  261. abcd_str = 'ABD'
  262. cal_num = max([words.count(char) for char in abcd_str])
  263. choice_m_cal_num.append(cal_num)
  264. max_num = max(choice_m_cal_num)
  265. if max_num < 2:
  266. direction = 180
  267. choice_option = a_z[:row_col_dict['cols']]
  268. else:
  269. direction = 90
  270. choice_option = a_z[:row_col_dict['rows']]
  271. row_col_dict.update({'direction': direction, 'option': choice_option})
  272. choice_m_dict_list.append(row_col_dict)
  273. 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
  274. for element in title_number_by_choice_m_list:
  275. if row_col_dict['bounding_box'] == element['bounding_box']:
  276. row_col_dict.update({'title_number': element['title_number']})
  277. for index_num, choice_box in enumerate(choice_m_dict_list):
  278. if len(choice_box['bounding_box']) > 0:
  279. abcd = choice_box['bounding_box']
  280. name = '{}_{}*{}_{}'.format('choice_m', choice_box['rows'], choice_box['cols'], choice_box['direction'])
  281. tree = utils.create_xml(name, tree,
  282. abcd['xmin'], abcd['ymin'],
  283. abcd['xmax'], abcd['ymax'])
  284. tree.write(xml_path)
  285. return choice_m_dict_list
  286. def choice_bbox_vague(choice_m_list0, x_y_interval_ave, singe_box_width_height_ave, direction, image_size):
  287. img_width = image_size[0]
  288. img_height = image_size[1]
  289. choice_list_temp1 = []
  290. choice_list_temp2 = []
  291. index_list1 = []
  292. index_list2 = []
  293. for index, ele in enumerate(choice_m_list0):
  294. if int(img_width) < int(img_height):
  295. choice_list_temp1.append(ele)
  296. index_list1.append(index)
  297. elif int(img_width) >= int(img_height) and ele[0] < img_width // 2:
  298. choice_list_temp1.append(ele)
  299. index_list1.append(index)
  300. elif int(img_width) >= int(img_height) and ele[0] >= img_width // 2:
  301. choice_list_temp2.append(ele)
  302. index_list2.append(index)
  303. if index_list2 == []:
  304. choice_list = [[choice_list_temp1, index_list1]]
  305. else:
  306. choice_list = [[choice_list_temp1, index_list1]]
  307. choice_list.append([choice_list_temp2, index_list2])
  308. choice_bbox_all = []
  309. for choice_m_list1 in choice_list:
  310. choice_m_list = sorted(choice_m_list1[0], key=lambda k: k[0])
  311. xmin0 = [ele[0] for ele in choice_m_list]
  312. ymin0 = [ele[1] for ele in choice_m_list]
  313. xmax0 = [ele[2] for ele in choice_m_list]
  314. ymax0 = [ele[3] for ele in choice_m_list]
  315. if direction == 180: # vertical
  316. x_diff = x_y_interval_ave[0]
  317. s_width = singe_box_width_height_ave[0]
  318. choice_bbox = (np.hstack((np.array([min(xmin0) - x_diff - 3 * s_width, min(ymin0)]), np.array([max(xmax0), max(ymax0)])))).tolist()
  319. choice_box = []
  320. for element in choice_bbox:
  321. if element < 0:
  322. choice_box.append(0)
  323. else:
  324. choice_box.append(element)
  325. choice_bbox_with_index_list = (choice_box, choice_m_list1[1])
  326. choice_bbox_all.append(choice_bbox_with_index_list)
  327. elif direction == 90:
  328. y_diff = x_y_interval_ave[1]
  329. s_height = singe_box_width_height_ave[1]
  330. choice_bbox = (np.hstack((np.array([min(xmin0), min(ymin0) - y_diff - 3 * s_height]), np.array([max(xmax0), max(ymax0)])))).tolist()
  331. choice_box = []
  332. for element in choice_bbox:
  333. if element < 0:
  334. choice_box.append(0)
  335. else:
  336. choice_box.append(element)
  337. choice_bbox_with_index_list = (choice_box, choice_m_list1[1])
  338. choice_bbox_all.append(choice_bbox_with_index_list)
  339. return choice_bbox_all
  340. def get_direction(words_res_dict, choice_m_mum=1):
  341. choice_m_cal_num = [0]
  342. for index, ele in enumerate(words_res_dict):
  343. words = ele['words']
  344. location = ele['location']
  345. width = int(location['width'])
  346. height = int(location['height'])
  347. if width > height:
  348. abcd_str = 'ABD'
  349. cal_num = max([words.count(char) for char in abcd_str])
  350. choice_m_cal_num.append(cal_num)
  351. # max_num = max(choice_m_cal_num)
  352. counts = np.bincount(np.array(choice_m_cal_num))
  353. mode = int(counts.argmax())
  354. # if max_num/choice_m_mum < 2:
  355. if mode < 2:
  356. direction = 180
  357. else:
  358. direction = 90
  359. return direction
  360. def choice_m_adjust(image, choice_m_bbox_list):
  361. a_z = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  362. for index0, choice_m in enumerate(choice_m_bbox_list): # rcnn识别的框匹配题号
  363. box = choice_m['bounding_box']
  364. box['ymin'] = choice_m['ymin'] - 3 # lf_7_2 1change
  365. box['xmax'] = choice_m['xmax'] + 3
  366. box['ymax'] = choice_m['ymax'] + 3
  367. m_left, m_top = box['xmin'], box['ymin'],
  368. # box_coordiante = (m_left, m_top, box['xmax'], box['ymax'])
  369. single_choice_m = utils.crop_region(image, box)
  370. row_col_dict = get_choice_m_row_and_col(m_left, m_top, single_choice_m) # 所有的小框, 行列等
  371. if len(row_col_dict) > 0:
  372. if choice_m['direction'] == 90:
  373. choice_option = a_z[:row_col_dict['rows']].replace('', ',')[1:-1]
  374. else:
  375. choice_option = a_z[:row_col_dict['cols']].replace('', ',')[1:-1]
  376. choice_m.update(row_col_dict) # 更新 bounding_box
  377. choice_m.update({'choice_option': choice_option})
  378. return choice_m_bbox_list
  379. def choice_m_row_col(image, choice_m_bbox_list, direction, subject, xml_path):
  380. a_z = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  381. choice_m_dict_list = []
  382. # 或在长宽比接近的choice_m中选取
  383. if not direction:
  384. random_one = random.randint(0, len(choice_m_bbox_list)-1)
  385. choice_m_for_dircetion = utils.crop_region(image, choice_m_bbox_list[random_one]['bounding_box'])
  386. res_dict = get_ocr_text_and_coordinate(choice_m_for_dircetion, ocr_accuracy='accurate', language_type='ENG')
  387. direction = get_direction(res_dict)
  388. for index0, box in enumerate(choice_m_bbox_list): # rcnn识别的框匹配题号
  389. box = box['bounding_box']
  390. box['ymin'] = box['ymin'] - 3 # lf_7_2 1change
  391. box['xmax'] = box['xmax'] + 3
  392. box['ymax'] = box['ymax'] + 3
  393. m_left, m_top = box['xmin'], box['ymin'],
  394. # box_coordiante = (m_left, m_top, box['xmax'], box['ymax'])
  395. single_choice_m = utils.crop_region(image, box)
  396. try:
  397. row_col_dict = get_choice_m_row_and_col(m_left, m_top, single_choice_m) # 所有的小框,行列
  398. if len(row_col_dict) > 0:
  399. if direction == 90:
  400. choice_option = a_z[:row_col_dict['rows']].replace('', ',')[1:-1]
  401. if 'number' in box.keys():
  402. title_number = box['number']
  403. else:
  404. title_number = [-1] * row_col_dict['cols']
  405. default_points = [-1] * row_col_dict['cols']
  406. else:
  407. choice_option = a_z[:row_col_dict['cols']].replace('', ',')[1:-1]
  408. if 'number' in box.keys():
  409. title_number = box['number']
  410. else:
  411. title_number = [-1] * row_col_dict['rows']
  412. default_points = [-1] * row_col_dict['rows']
  413. row_col_dict.update({'direction': direction, 'option': choice_option,
  414. 'number': title_number, 'default_points': default_points})
  415. choice_m_dict_list.append(row_col_dict)
  416. # else:
  417. # del choice_m_bbox_list[index0]
  418. except Exception:
  419. pass
  420. try:
  421. choice_m_box_dict = get_title_number_by_choice_m.analysis_s_box(choice_m_dict_list)
  422. choice_m_list = [[ele['bounding_box']['xmin'], ele['bounding_box']['ymin'],
  423. ele['bounding_box']['xmax'], ele['bounding_box']['ymax']] for ele in choice_m_box_dict]
  424. x_y_interval_all = []
  425. s_box_w_h = []
  426. rows_list = [ele['rows'] for ele in choice_m_box_dict if ele['rows'] == 1]
  427. if len(rows_list) == len(choice_m_box_dict):
  428. w_list = [ele['single_width'] for ele in choice_m_box_dict]
  429. h_list = [ele['single_height'] for ele in choice_m_box_dict]
  430. w_arr = np.array(w_list)
  431. h_arr = np.array(h_list)
  432. s_box_w_h.append((int(np.mean(w_arr)), int(np.mean(h_arr))))
  433. x_y_interval = s_box_w_h
  434. x_y_interval_all.append(x_y_interval)
  435. else:
  436. for index, s_box in enumerate(choice_m_box_dict):
  437. all_small_coordinate_dict = s_box['all_small_coordinate']
  438. all_small_coordinate_list = [[ele['xmin'], ele['ymin'], ele['xmax'], ele['ymax']] for ele in
  439. all_small_coordinate_dict]
  440. col = s_box['cols']
  441. rows = s_box['rows']
  442. if rows == 1:
  443. continue
  444. else:
  445. x_y_interval = utils.get_x_diff_and_y_diff1(all_small_coordinate_list, col)
  446. x_y_interval_all.append(x_y_interval)
  447. all_small_coordinate_list = sorted(all_small_coordinate_list, key=lambda k: k[1])
  448. s_box_array = np.array(all_small_coordinate_list)
  449. s_box_wid_hei = (
  450. int(np.mean(s_box_array[:, 2])) - int(np.mean(s_box_array[:, 0])),
  451. int(np.mean(s_box_array[:, 3])) - int(np.mean(s_box_array[:, 1])))
  452. s_box_w_h.append(s_box_wid_hei)
  453. whether_nan = [ele for index, ele in enumerate(x_y_interval_all) if 'nan' in ele]
  454. if len(whether_nan) == 0:
  455. x_y_interval_all0 = x_y_interval_all
  456. else:
  457. if len(x_y_interval_all) == 1:
  458. x_y_interval_all0 = s_box_w_h
  459. else:
  460. x_y_interval_all1 = []
  461. for element in x_y_interval_all:
  462. if element not in whether_nan:
  463. x_y_interval_all1.append(element)
  464. x_y_interval_all2 = [x_y_interval_all1[0] for i in range(len(whether_nan))]
  465. x_y_interval_all0 = x_y_interval_all1 + x_y_interval_all2
  466. x_y_interval_arr = np.array(x_y_interval_all0)
  467. if len(x_y_interval_arr) == 1 and len(rows_list) != 0:
  468. x_y_interval_all_arr = np.array(x_y_interval_all)
  469. x_ = int(np.mean(x_y_interval_all_arr[:, 0][0][0]))
  470. y_ = int(np.mean(x_y_interval_all_arr[:, 0][0][1]))
  471. x_y_interval_ave = (x_, y_)
  472. singe_box_width_height_ave = s_box_w_h[0]
  473. image_height, image_width, _ = image.shape
  474. image_size = (image_width, image_height)
  475. elif len(x_y_interval_arr) == 1 and len(rows_list) == 0:
  476. x_y_interval_all_arr = np.array(x_y_interval_all)
  477. x_ = int(np.mean(x_y_interval_all_arr[0][0]))
  478. y_ = int(np.mean(x_y_interval_all_arr[0][1]))
  479. x_y_interval_ave = (x_, y_)
  480. singe_box_width_height_ave = s_box_w_h[0]
  481. image_height, image_width, _ = image.shape
  482. image_size = (image_width, image_height)
  483. else:
  484. x_y_interval_ave = (int(np.mean(x_y_interval_arr[:, 0])), int(np.mean(x_y_interval_arr[:, 1])))
  485. s_box_w_h_arr = np.array(s_box_w_h)
  486. singe_box_width_height_ave = (int(np.mean(s_box_w_h_arr[:, 0])), int(np.mean(s_box_w_h_arr[:, 1])))
  487. image_height, image_width, _ = image.shape
  488. image_size = (image_width, image_height)
  489. choice_bbox = choice_bbox_vague(choice_m_list, x_y_interval_ave, singe_box_width_height_ave, direction,
  490. image_size)
  491. choice_m_dict_list_all_tmp = []
  492. for index, choice_box_ele in enumerate(choice_bbox):
  493. choice_bbox_ele = []
  494. if direction == 180:
  495. choice_bbox_ele0 = choice_box_ele[0]
  496. choice_bbox_ele = [choice_bbox_ele0[0], choice_bbox_ele0[1] - 2,
  497. choice_bbox_ele0[2], choice_bbox_ele0[3] + 5]
  498. elif direction == 90:
  499. choice_bbox_ele = choice_box_ele[0]
  500. choice_region = utils.crop_region_direct(image, choice_bbox_ele)
  501. choice_path = xml_path[: xml_path.rfind('/')]
  502. cv2.imwrite(os.path.join(choice_path, 'choice_region_' + str(index) + '.jpg'), choice_region)
  503. choice_m_box_dict_new = [choice_m_box_dict[i] for i in choice_box_ele[1]]
  504. choice_m_dict_list_part = get_title_number_by_choice_m.get_title_number(choice_bbox_ele, choice_region,
  505. image, choice_m_box_dict_new,
  506. x_y_interval_ave, subject, direction)
  507. choice_m_dict_list_all_tmp.append(choice_m_dict_list_part)
  508. if len(choice_m_dict_list_all_tmp) == 1:
  509. choice_m_dict_list_all = choice_m_dict_list_all_tmp[0]
  510. else:
  511. choice_m_dict_list_all = []
  512. for i in choice_m_dict_list_all_tmp:
  513. choice_m_dict_list_all = choice_m_dict_list_all + i
  514. except Exception as e:
  515. print(e)
  516. traceback.print_exc()
  517. choice_m_dict_list_all = choice_m_dict_list
  518. return choice_m_dict_list_all
  519. def choice_line_with_number(left, top, image, choice_bbox_list, xml_path):
  520. t1 = time.time()
  521. word_result_list0 = get_ocr_text_and_coordinate(image, ocr_accuracy='accurate', language_type='ENG')
  522. t2 = time.time()
  523. print('choice ocr time cost: ', t2 - t1)
  524. # print(word_result_list0)
  525. intervel_x = 3
  526. img = preprocess(image, intervel_x, 3)
  527. cv_box_list = box_coordinates(img)
  528. content_list = get_choice_line_box_coordinate(left, top, word_result_list0, cv_box_list, choice_bbox_list)
  529. return content_list
  530. def get_choice_line_box_coordinate(left, top, word_result_list, cv_box_list, choice_bbox_list):
  531. all_digital_list = []
  532. digital_model = re.compile(r'\d')
  533. for i, chars_dict in enumerate(word_result_list):
  534. chars_list = chars_dict['chars']
  535. for ele in chars_list:
  536. if digital_model.search(ele['char']):
  537. all_digital_list.append(ele)
  538. new_all_digital_list = []
  539. i = 1
  540. while i <= len(all_digital_list):
  541. pre_one = all_digital_list[i - 1]
  542. if i == len(all_digital_list):
  543. new_all_digital_list.append(pre_one)
  544. break
  545. rear_one = all_digital_list[i]
  546. condition1 = abs(pre_one['location']['top'] - rear_one['location']['top']) < pre_one['location'][
  547. 'height'] # 两字高度差小于一字高度
  548. condition2 = pre_one['location']['left'] + 2 * pre_one['location']['width'] > rear_one['location'][
  549. 'left'] # 某字宽度的2倍大于两字间间隔
  550. if condition1:
  551. if condition2:
  552. new_char = pre_one['char'] + rear_one['char']
  553. new_location = {'left': pre_one['location']['left'],
  554. 'top': min(pre_one['location']['top'], rear_one['location']['top']),
  555. 'width': rear_one['location']['left'] + rear_one['location']['width'] -
  556. pre_one['location']['left'],
  557. 'height': max(pre_one['location']['height'], rear_one['location']['height'])}
  558. new_all_digital_list.append({'char': new_char, 'location': new_location})
  559. i = i + 1 + 1
  560. else:
  561. new_all_digital_list.append(pre_one)
  562. i = i + 1
  563. else:
  564. new_all_digital_list.append(pre_one) # 遇到字符y轴相差过大就结束
  565. i = i + 1
  566. content_list = list()
  567. new_all_digital_list = sorted(new_all_digital_list, key=lambda k: k.get('location').get('top'))
  568. for index, box in enumerate(
  569. sorted(choice_bbox_list['regions'], key=lambda k: k.get('bounding_box').get('ymin'))): # rcnn识别的框匹配题号
  570. box = box['bounding_box']
  571. box_coordinate = (box['xmin'] + left, box['ymin'] + top, box['xmax'] + left, box['ymax'] + top)
  572. choice_number = {'number': 999, 'location': box_coordinate}
  573. content_list.insert(index, choice_number)
  574. for digital in new_all_digital_list:
  575. digital_coordinate = (digital['location']['left'] + left, digital['location']['top'] + top,
  576. digital['location']['left'] + digital['location']['width'] + left,
  577. digital['location']['top'] + digital['location']['height'] + top)
  578. if utils.decide_coordinate_contains(digital_coordinate, box_coordinate):
  579. content_list[index]['number'] = digital['char']
  580. new_all_digital_list.remove(digital)
  581. break
  582. for box in content_list: # 计算间距
  583. box_coordiante = (box['location'][0], box['location'][1], box['location'][2], box['location'][3])
  584. mtx = []
  585. for cv_box in cv_box_list:
  586. if utils.decide_coordinate_contains(cv_box, box_coordiante): # 若fasterrcnn未识别到选项框,单独的ABCD也舍去
  587. mtx.append(cv_box)
  588. matrix = np.asarray(sorted(mtx))
  589. dif = matrix[1:, 0] - matrix[:-1, 2] # 后一个char的left与起一个char的right的差
  590. dif[dif < 0] = 0
  591. dif_length = np.mean(dif) # 小于平均间隔的合并
  592. block_list = utils.box_by_x_intervel(matrix, dif_length)
  593. # block_list = utils.box_by_x_intervel(matrix, 5)
  594. box['abcd'] = block_list
  595. matrix_distance = np.asarray(sorted(block_list))
  596. dif = matrix_distance[1:, 0] - matrix_distance[:-1, 2] # 后一个char的left与起一个char的right的差
  597. distance = np.mean(dif)
  598. return content_list