exam_number_box.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. # @Author : lightXu
  2. # @File : exam_number_box.py
  3. # @Time : 2018/11/22 0022 下午 15:59
  4. import cv2
  5. import numpy as np
  6. import xml.etree.cElementTree as ET
  7. from segment.sheet_resolve.tools import utils
  8. from segment.sheet_resolve.tools.brain_api import get_ocr_text_and_coordinate, get_ocr_text_and_coordinate_direction
  9. import re
  10. def preprocess(img, xe, ye):
  11. scale = 0
  12. dilate = 1
  13. blur = 5
  14. # 预处理图像
  15. # img = cv2.imread(picture)
  16. # rescale the image
  17. if scale != 0:
  18. img = cv2.resize(img, None, fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC)
  19. # Convert to gray
  20. img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  21. # # Apply dilation and erosion to remove some noise
  22. # if dilate != 0:
  23. # kernel = np.ones((dilate, dilate), np.uint8)
  24. # img = cv2.dilate(img, kernel, iterations=1)
  25. # img = cv2.erode(img, kernel, iterations=1)
  26. # Apply blur to smooth out the edges
  27. # if blur != 0:
  28. # img = cv2.GaussianBlur(img, (blur, blur), 0)
  29. # Apply threshold to get image with only b&w (binarization)
  30. img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
  31. kernel = np.ones((ye, xe), np.uint8) # y轴膨胀, x轴膨胀
  32. dst = cv2.dilate(img, kernel, iterations=1)
  33. # cv2.imshow('dilate', dst)
  34. # if cv2.waitKey(0) == 27:
  35. # cv2.destroyAllWindows()
  36. return dst
  37. def contours(image):
  38. _, cnts, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  39. bboxes = []
  40. for cnt_id, cnt in enumerate(reversed(cnts)):
  41. x, y, w, h = cv2.boundingRect(cnt)
  42. bboxes.append((x, y, x + w, y + h))
  43. return bboxes
  44. def box_coordinates(img):
  45. img_arr = np.asarray(img)
  46. def axix_break_point(img, tolerance_number, axis):
  47. sum_x_axis = img.sum(axis=axis)
  48. sum_x_axis[sum_x_axis > 255 * tolerance_number] = 1 # 白色有字
  49. sum_x_axis[sum_x_axis != 1] = 0 # 黑色无字
  50. sum_x_axis_list = list(sum_x_axis)
  51. sum_x_axis_list.append(0) # 最后几行到结束有字时,使索引值增加最后一位
  52. split_x_index = []
  53. num = 1
  54. for index, ele in enumerate(sum_x_axis_list):
  55. num = num % 2
  56. if ele == num:
  57. # print(i)
  58. num = num + 1
  59. split_x_index.append(index)
  60. # print('length: ', len(split_x_index), split_x_index)
  61. return split_x_index
  62. y_break_points_list = axix_break_point(img_arr, 1, axis=1) # y轴分组
  63. img_arr_upper = img_arr[:y_break_points_list[1], :]
  64. # cv2.imshow('img_arr_upper', img_arr_upper)
  65. # if cv2.waitKey(0) == 27:
  66. # cv2.destroyAllWindows()
  67. x_break_points_list = axix_break_point(img_arr_upper, 1, axis=0)
  68. if len(x_break_points_list) <= 4:
  69. hand_writing = True
  70. else:
  71. hand_writing = False
  72. img_arr_for_x = img_arr
  73. ocr_region = img_arr_upper
  74. if hand_writing: # 存在手写考号区域
  75. ocr_region = img_arr[y_break_points_list[2]:y_break_points_list[3], :]
  76. y_break_points_list = y_break_points_list[2:]
  77. img_arr_for_x = img_arr[y_break_points_list[1]:, :]
  78. x_break_points_list = axix_break_point(img_arr_for_x, 1, axis=0)
  79. all_coordinates = []
  80. row_number = 0
  81. for i in range(0, len(y_break_points_list), 2): # y轴分组
  82. ymin = y_break_points_list[i]
  83. ymax = y_break_points_list[i + 1]
  84. matrix = np.array([0, 0, 0, 0])
  85. if ymax-ymin > 3: # 过滤噪音
  86. for j in range(0, len(x_break_points_list), 2):
  87. xmin = x_break_points_list[j]
  88. xmax = x_break_points_list[j + 1]
  89. if xmax - xmin > 3:
  90. matrix = np.vstack([matrix, np.array([xmin, ymin, xmax, ymax])])
  91. matrix = matrix[1:, :]
  92. dif = matrix[1:, 0] - matrix[:-1, 2] # 后一个char的left与起一个char的right的差
  93. dif[dif < 0] = 0
  94. dif_length = np.mean(dif) # 小于平均间隔的合并
  95. block_list = utils.box_by_x_intervel(matrix, dif_length)
  96. row = {'row': '{}'.format(row_number), 'coordinates': block_list}
  97. all_coordinates.append(row)
  98. row_number += 1
  99. # 识别文字和朝向
  100. try:
  101. word_result_list, _ = get_ocr_text_and_coordinate_direction(ocr_region)
  102. except Exception:
  103. word_result_list, _ = get_ocr_text_and_coordinate_direction(img_arr_for_x)
  104. direction = 180
  105. if len(word_result_list) > 0:
  106. all_char_list = []
  107. digital_model = re.compile(r'\d')
  108. for i, chars_dict in enumerate(word_result_list):
  109. chars_list = chars_dict['chars']
  110. for ele in chars_list:
  111. if digital_model.search(ele['char']):
  112. all_char_list.append(int(ele['char']))
  113. if sum(all_char_list) < 45//2:
  114. direction = 180
  115. else:
  116. direction = 90
  117. return all_coordinates, direction
  118. def exam_number(left, top, image, xml_path):
  119. img = preprocess(image, 3, 3)
  120. box_list, _ = box_coordinates(img)
  121. exam_bbox_list = []
  122. tree = ET.parse(xml_path) # xml tree
  123. for index_num, exam_bbox in enumerate(box_list):
  124. row_number = exam_bbox['row']
  125. coordinates = exam_bbox['coordinates']
  126. ii = 0
  127. for i, coordinate in enumerate(coordinates):
  128. area = (coordinate[2] - coordinate[0]) * (coordinate[3] - coordinate[1])
  129. if area > 400:
  130. number = '{:02d}_{}'.format(ii, row_number)
  131. tree = utils.create_xml(number, tree,
  132. coordinate[0]+left, coordinate[1]+top, coordinate[2]+left, coordinate[3]+top)
  133. region = [coordinate[0]+left, coordinate[1]+top, coordinate[2]+left, coordinate[3]+top]
  134. exam_bbox_list.append({'number': number, 'region': region})
  135. ii = ii + 1
  136. # print(exam_items_bbox)
  137. tree.write(xml_path)
  138. return exam_bbox_list
  139. def exam_number_column(left, top, image, xml_path):
  140. img = preprocess(image, 3, 3)
  141. box_list, _ = box_coordinates(img)
  142. column_number = len(box_list[0]['coordinates'])
  143. tree = ET.parse(xml_path) # xml tree
  144. column_list = []
  145. for i in range(0, column_number):
  146. matrix = np.array([0, 0, 0, 0])
  147. for coord in box_list:
  148. col = coord['coordinates']
  149. matrix = np.vstack([matrix, np.array(col[i])])
  150. combine = matrix[1:]
  151. min_temp = np.min(combine, axis=0)
  152. max_temp = np.max(combine, axis=0)
  153. column_coordinate = {'xmin': min_temp[0]+left, 'ymin': min_temp[1]+top,
  154. 'xmax': max_temp[2]+left, 'ymax': max_temp[3]+top}
  155. single_height = np.mean(combine[:, 3]-combine[:, 1])
  156. single_width = np.mean(combine[:, 2]-combine[:, 0])
  157. column_dict = {'number': i, 'location': column_coordinate,
  158. 'single_height': int(single_height),
  159. 'single_width': int(single_width),
  160. "choice_option": "0,1,2,3,4,5,6,7,8,9",
  161. 'row': 10, 'column': 1}
  162. column_list.append(column_dict)
  163. tree = utils.create_xml(str(i), tree,
  164. column_coordinate['xmin'], column_coordinate['ymin'],
  165. column_coordinate['xmax'], column_coordinate['ymax'])
  166. return column_list
  167. def exam_number_whole(left, top, image, xml_path):
  168. img = preprocess(image, 3, 3)
  169. box_list, direction = box_coordinates(img)
  170. coor = [coord['coordinates'] for coord in box_list]
  171. column_number = len(box_list[0]['coordinates'])
  172. row_number = len(box_list)
  173. tensor = np.asarray(coor).reshape(column_number*row_number, 4)
  174. min_temp = np.min(tensor, axis=0)
  175. max_temp = np.max(tensor, axis=0)
  176. column_coordinate = {'xmin': int(min_temp[0] + left), 'ymin': int(min_temp[1] + top),
  177. 'xmax': int(max_temp[2] + left), 'ymax': int(max_temp[3] + top)}
  178. single_height = np.mean(tensor[:, 3] - tensor[:, 1])
  179. single_width = np.mean(tensor[:, 2] - tensor[:, 0])
  180. column_dict = {'location': column_coordinate,
  181. 'single_height': int(single_height),
  182. 'single_width': int(single_width),
  183. "choice_option": "0,1,2,3,4,5,6,7,8,9",
  184. 'row': row_number, 'column': column_number,
  185. 'direction': direction}
  186. tree = ET.parse(xml_path) # xml tree
  187. tree = utils.create_xml('exam_number', tree,
  188. column_coordinate['xmin'], column_coordinate['ymin'],
  189. column_coordinate['xmax'], column_coordinate['ymax'])
  190. tree.write(xml_path)
  191. return column_dict