util.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. import cv2
  2. import matplotlib.pylab as plt
  3. import numpy as np
  4. def read_single_img(img_path):
  5. try:
  6. im = cv2.imdecode(np.fromfile(img_path, dtype=np.uint8), -1)
  7. except FileNotFoundError as e:
  8. raise e
  9. return im
  10. def pre_process(image, blank_top=20, blank_bottom=-20, blur_size=5, sigma=5, debug=0):
  11. # 返回二值逆图
  12. blank_size = 20
  13. if image.ndim == 3:
  14. gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
  15. elif image.ndim == 2:
  16. gray = image
  17. else:
  18. raise Exception('The dimension of the image should be 2 or 3!')
  19. # 裁边
  20. gray[0:blank_top, :] = 255
  21. gray[blank_bottom:, :] = 255
  22. gray[:, 0:blank_size] = 255
  23. gray[:, -blank_size:] = 255
  24. pre = 255 - gray
  25. pre = cv2.GaussianBlur(pre, (blur_size, blur_size), sigma)
  26. binary = cv2.threshold(pre, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
  27. if debug == 1:
  28. # 显示灰度图和二值图
  29. plt.figure(figsize=(15, 10))
  30. plt.subplot(211)
  31. plt.title('gray')
  32. plt.imshow(gray, cmap='gray')
  33. plt.subplot(212)
  34. plt.title('binary')
  35. plt.imshow(255 - binary, cmap='gray')
  36. plt.show()
  37. return binary
  38. def pre_process_for_anchors(image, blank_top=20, blank_bottom=-20, h_ratio=(0.1, 0.9),
  39. blur_size=3, sigma=5, blank_size=20, debug=0):
  40. # 去掉中间内容,返回上下定位点的二值逆图
  41. # h_ration=0 则不裁去中间部分
  42. # assert(image.ndim == 2 or image.ndim == 3)
  43. if image.ndim == 3:
  44. gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
  45. elif image.ndim == 2:
  46. gray = image.copy()
  47. else:
  48. raise Exception('The dimension of the image should be 2 or 3!')
  49. # 裁边
  50. gray[0:blank_top, :] = 255
  51. gray[blank_bottom:, :] = 255
  52. gray[:, 0:blank_size] = 255
  53. gray[:, -blank_size:] = 255
  54. # 去掉中间内容
  55. if h_ratio != 0:
  56. h0 = int(image.shape[0] * h_ratio[0])
  57. h1 = int(image.shape[0] * h_ratio[1])
  58. gray[h0:h1, :] = 255
  59. pre = 255 - gray
  60. pre = cv2.GaussianBlur(pre, (blur_size, blur_size), sigma)
  61. binary = cv2.threshold(pre, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
  62. if debug == 1:
  63. # 显示灰度图和二值图
  64. plt.figure(figsize=(15, 10))
  65. plt.subplot(211)
  66. plt.title('gray')
  67. plt.imshow(gray, cmap='gray')
  68. plt.subplot(212)
  69. plt.title('binary')
  70. plt.imshow(255 - binary, cmap='gray')
  71. plt.show()
  72. return binary
  73. def extract_feature(binary, method=4, ker_size1=2, ker_size2=10, debug=0):
  74. # 对二值图进一步处理
  75. close_size = 3
  76. kernel_height = 5
  77. kernel_width = 1
  78. close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (close_size, close_size))
  79. kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_height, kernel_width))
  80. horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ker_size1, ker_size2))
  81. vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ker_size2, ker_size1))
  82. if method == 1:
  83. # ret = cv2.dilate(binary, kernel)
  84. ret = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, iterations=1)
  85. ret = cv2.morphologyEx(ret, cv2.MORPH_OPEN, vertical_kernel)
  86. elif method == 2:
  87. ret = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
  88. ret = cv2.morphologyEx(ret, cv2.MORPH_CLOSE, close_kernel)
  89. elif method == 3:
  90. ret = cv2.morphologyEx(binary, cv2.MORPH_OPEN, horizontal_kernel)
  91. ret = cv2.morphologyEx(ret, cv2.MORPH_OPEN, vertical_kernel)
  92. elif method == 4:
  93. ret = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, close_kernel)
  94. ret = cv2.morphologyEx(ret, cv2.MORPH_OPEN, horizontal_kernel)
  95. ret = cv2.morphologyEx(ret, cv2.MORPH_OPEN, vertical_kernel)
  96. else:
  97. ret = binary
  98. if debug == 1:
  99. # 显示特征提取其后的图像
  100. plt.figure(figsize=(15, 10))
  101. plt.subplot(211)
  102. plt.title('before feature extraction')
  103. plt.imshow(255 - binary, cmap='gray')
  104. # plt.show()
  105. # plt.figure(figsize=(15, 10))
  106. plt.subplot(212)
  107. plt.title('after feature extraction')
  108. plt.imshow(255 - ret, cmap='gray')
  109. plt.show()
  110. return ret
  111. def draw_contour(binary):
  112. (major, minor, _) = cv2.__version__.split(".") # check cv version
  113. boxes = []
  114. contours = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  115. contours = contours[1] if major == '3' else contours[0]
  116. for i in range(0, len(contours)):
  117. xmin, ymin, w, h = cv2.boundingRect(contours[i])
  118. xmax = xmin + w
  119. ymax = ymin + h
  120. centroid = [xmin + w // 2, ymin + h // 2]
  121. boxes.append([xmin, ymin, xmax, ymax, centroid, w*h])
  122. return boxes
  123. def draw_connected_component(binary):
  124. connectivity = 8
  125. boxes = []
  126. num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(binary, connectivity=connectivity)
  127. for l in range(1, num_labels):
  128. xmin = stats[l, cv2.CC_STAT_LEFT]
  129. ymin = stats[l, cv2.CC_STAT_TOP]
  130. xmax = stats[l, cv2.CC_STAT_WIDTH] + xmin
  131. ymax = stats[l, cv2.CC_STAT_HEIGHT] + ymin
  132. area = stats[l, cv2.CC_STAT_AREA]
  133. boxes.append([xmin, ymin, xmax, ymax, [int(centroids[l][0]), int(centroids[l][1])], area])
  134. return boxes
  135. def find_boxes(binary, method='connected', debug=0):
  136. # 寻找轮廓
  137. if method == 'contour':
  138. boxes = draw_contour(binary)
  139. elif method == 'connected':
  140. boxes = draw_connected_component(binary)
  141. else:
  142. raise Exception('Wrong method for finding boxes!')
  143. if debug == 1:
  144. # 显示boxes信息
  145. boxes.sort(key=lambda x: x[4][1])
  146. print('The features of the boxes after feature extractions:')
  147. for box in boxes:
  148. width = box[2] - box[0]
  149. height = box[3] - box[1]
  150. w_to_h = width / height
  151. area = box[-1]
  152. centroid = box[-2]
  153. area_ratio = area / (width * height)
  154. print('width:{}, height:{}, centroid:{}, w_to_h:{}, area:{}, area ratio:{}'.
  155. format(width, height, centroid, w_to_h, area, area_ratio))
  156. return boxes
  157. def find_marker_by_shape(boxes,
  158. shape_para={'height': (80, 10), 'w2h': (3, 0.6), 'area': (6000, 500), 'area_ratio': 0.5},
  159. debug=0):
  160. # 通过形状参数寻找定位点
  161. area_ratio_threshold = 0.96
  162. max_height, min_height = shape_para['height']
  163. max_w2h, min_w2h = shape_para['w2h']
  164. max_area, min_area = shape_para['area']
  165. min_area_ratio = shape_para['area_ratio']
  166. markers = []
  167. for box in boxes:
  168. w = box[2] - box[0]
  169. h = box[3] - box[1]
  170. if box[-1] >= area_ratio_threshold*w*h and min_area <= box[-1] <= max_area:
  171. markers.append(box)
  172. elif min_height <= h <= max_height and min_w2h <= w/h <= max_w2h \
  173. and min_area <= box[-1] <= max_area and box[-1] >= min_area_ratio*w*h:
  174. markers.append(box)
  175. if debug == 1:
  176. # 显示通过形状参数找到的定位点信息
  177. markers.sort(reverse=True, key=lambda x: x[-1])
  178. print('The features of the markers picking up by shapes:')
  179. for box in markers:
  180. width = box[2] - box[0]
  181. height = box[3] - box[1]
  182. w_to_h = width / height
  183. area = box[-1]
  184. centroid = box[-2]
  185. area_ratio = area / (width * height)
  186. print('width:{}, height:{}, centroid:{}, w_to_h:{}, area:{}, area ratio:{}'.
  187. format(width, height, centroid, w_to_h, area, area_ratio))
  188. elif debug == 2:
  189. # 显示所有定位点信息
  190. print('The features of the markers without picking up by shapes:')
  191. for box in boxes:
  192. markers.append(box)
  193. for box in markers:
  194. width = box[2] - box[0]
  195. height = box[3] - box[1]
  196. w_to_h = width / height
  197. area = box[-1]
  198. centroid = box[-2]
  199. area_ratio = area / (width * height)
  200. print('width:{}, height:{}, centroid:{}, w_to_h:{}, area:{}, area ratio:{}'.
  201. format(width, height, centroid, w_to_h, area, area_ratio))
  202. return markers
  203. def find_box_list_by_position(box, box_list, method='h', shift_threshold=30, slope_threshold=0.2, area_threshold=0.28):
  204. # 根据相近原则将box加入box_list中
  205. if len(box_list) > 0:
  206. if method == 'h': # 水平分布
  207. index_flag, distance = -1, shift_threshold
  208. for index, bl in enumerate(box_list):
  209. d = abs(box[4][1] - bl[-1][4][1])
  210. if d < distance:
  211. distance = d
  212. index_flag = index
  213. if index_flag >= 0:
  214. box_list[index_flag].append(box)
  215. else:
  216. box_list.append([box])
  217. elif method == 'v': # 垂直分布
  218. index_flag, distance = -1, shift_threshold
  219. for index, bl in enumerate(box_list):
  220. d = abs(box[4][0] - bl[-1][4][0])
  221. if d < distance and d < abs(box[4][1] - bl[-1][4][1]) * slope_threshold:
  222. distance = d
  223. index_flag = index
  224. if index_flag >= 0:
  225. box_list[index_flag].append(box)
  226. else:
  227. box_list.append([box])
  228. elif method == 's': # 面积相近分布
  229. index_flag, area_diff = -1, area_threshold
  230. for index, bl in enumerate(box_list):
  231. d = abs((box[-1] - bl[-1][-1]) / bl[-1][-1])
  232. if d < area_diff:
  233. area_diff = d
  234. index_flag = index
  235. if index_flag >= 0:
  236. box_list[index_flag].append(box)
  237. else:
  238. box_list.append([box])
  239. else:
  240. box_list.append([box])
  241. return box_list
  242. def collect_markers_by_position(boxes, method='h', shift_threshold=30, slope_threshold=0.2, area_threshold=0.28, debug=0):
  243. # 按照相近位置排列定位点
  244. box_list = []
  245. if method == 'h': # 按水平位置相近排列
  246. boxes.sort(key=lambda x: x[4][0])
  247. for b in boxes:
  248. box_list = find_box_list_by_position(b, box_list, method=method, shift_threshold=shift_threshold,
  249. slope_threshold=slope_threshold)
  250. box_list.sort(key=lambda x: x[0][4][1])
  251. elif method == 'v': # 按垂直位置相近排列
  252. boxes.sort(key=lambda x: x[4][1])
  253. for b in boxes:
  254. box_list = find_box_list_by_position(b, box_list, method=method, shift_threshold=shift_threshold,
  255. slope_threshold=slope_threshold)
  256. box_list.sort(key=lambda x: x[0][4][0])
  257. elif method == 's': # 按面积大小相近排列
  258. boxes.sort(reverse=True, key=lambda x: x[-1])
  259. for b in boxes:
  260. box_list = find_box_list_by_position(b, box_list, method=method, shift_threshold=shift_threshold,
  261. slope_threshold=slope_threshold, area_threshold=area_threshold)
  262. box_list.sort(reverse=True, key=lambda x: x[0][-1])
  263. # if method == 'h': # 按水平位置相近排列
  264. # boxes.sort(key=lambda x: x[4][1])
  265. # for b in boxes:
  266. # index_flag, distance = -1, shift_threshold
  267. # for index, single_list in enumerate(box_list):
  268. # if abs(b[4][1] - single_list[-1][4][1]) < distance:
  269. # distance = abs(b[4][1] - single_list[-1][4][1])
  270. # index_flag = index
  271. # if index_flag >= 0:
  272. # box_list[index_flag].append(b)
  273. # else:
  274. # box_list.append([b])
  275. #
  276. # elif method == 'v': # 按垂直位置相近排列
  277. # boxes.sort(key=lambda x: x[4][0])
  278. # for b in boxes:
  279. # index_flag, distance = -1, shift_threshold
  280. # for index, single_list in enumerate(box_list):
  281. # if abs(b[4][0] - single_list[-1][4][0]) < distance:
  282. # distance = abs(b[4][0] - single_list[-1][4][0])
  283. # index_flag = index
  284. # if index_flag >= 0:
  285. # box_list[index_flag].append(b)
  286. # else:
  287. # box_list.append([b])
  288. if debug == 1:
  289. print('box list slope')
  290. if method == 'h':
  291. for box in box_list:
  292. if len(box) >= 2:
  293. for i in range(len(box)-1):
  294. slope = (box[i+1][4][1] - box[i][4][1])/(box[i+1][4][0] - box[i][4][0])
  295. print(slope)
  296. elif method == 'v':
  297. for box in box_list:
  298. if len(box) >= 2:
  299. for i in range(len(box) - 1):
  300. slope = (box[i + 1][4][0] - box[i][4][0]) / (box[i + 1][4][1] - box[i][4][1])
  301. print(slope)
  302. return box_list
  303. def collect_markers_by_area(boxes, area_threshold=2):
  304. # 去除面积相差过大的定位点
  305. boxes.sort(reverse=True, key=lambda x: x[-1])
  306. # print(boxes[0])
  307. min_area = boxes[0][-1] / area_threshold
  308. boxes = [b for b in boxes if b[-1] > min_area]
  309. return boxes
  310. def check_with_anchor(problem_markers, top_anchors, page_width, column_num):
  311. # 根据top_anchors位置去除异常markers
  312. min_shift = 100
  313. column_pos = []
  314. if len(top_anchors) == column_num + 1:
  315. column_pos.append(top_anchors[0][4][0])
  316. if top_anchors[1][4][0] - top_anchors[0][4][0] < top_anchors[-1][4][0] - top_anchors[-2][4][0]:
  317. for i in range(2, column_num+1):
  318. column_pos.append(top_anchors[i][4][0] - page_width)
  319. else:
  320. for i in range(1, column_num):
  321. column_pos.append(top_anchors[i][4][0])
  322. for index, markers in enumerate(problem_markers):
  323. remove_list = []
  324. for i in range(len(markers)//2):
  325. if abs(markers[2*i][4][0]-column_pos[index]) > min_shift or \
  326. abs(markers[2*i+1][4][0]-column_pos[index]-page_width) > min_shift:
  327. remove_list.extend([2*i, 2*i+1])
  328. problem_markers[index] = [problem_markers[index][i] for i in range(len(problem_markers[index]))
  329. if i not in remove_list]
  330. return problem_markers
  331. def remove_abnormal_marker(markers, page_width, debug=0):
  332. # 从markers中剔除异常点
  333. error = 10
  334. max_std = 3
  335. min_std = 0.1
  336. min_area_ratio = 0.9
  337. min_distance = 60
  338. min_slope = 0.2
  339. distance_list = []
  340. remove_list = []
  341. for i in range(len(markers)//2-1):
  342. min_flag = i
  343. distance_flag = abs(markers[2*i+1][4][0] - markers[2*i][4][0] - page_width) + abs(markers[2*i+1][4][1] -
  344. markers[2*i][4][1])
  345. for j in range(i+1, len(markers)//2):
  346. if abs(markers[2*i+1][4][0] - markers[2*j+1][4][0]) + abs(markers[2*i+1][4][1] - markers[2*j+1][4][1]) \
  347. < error:
  348. if distance_flag < abs(markers[2*j+1][4][0] - markers[2*j][4][0] - page_width) + \
  349. abs(markers[2*j+1][4][1] - markers[2*j][4][1]):
  350. remove_list.extend([2*j, 2*j+1])
  351. else:
  352. distance_flag = abs(markers[2*j+1][4][0] - markers[2*j][4][0] - page_width) + \
  353. abs(markers[2*j+1][4][1] - markers[2*j][4][1])
  354. remove_list.extend([2*min_flag, 2*min_flag+1])
  355. min_flag = j
  356. markers = [markers[i] for i in range(len(markers)) if i not in remove_list]
  357. remove_list = []
  358. if len(markers) >= 6:
  359. left_slope_list = np.asarray([abs((markers[2 * i][4][0] - markers[2 * i + 2][4][0])
  360. / (markers[2 * i][4][1] - markers[2 * i + 2][4][1]))
  361. for i in range(len(markers)//2-1)])
  362. right_slope_list = np.asarray([abs((markers[2 * i + 1][4][0] - markers[2 * i + 3][4][0]) /
  363. (markers[2 * i + 1][4][1] - markers[2 * i + 3][4][1]))
  364. for i in range(len(markers) // 2 - 1)])
  365. left_slope_list = left_slope_list > min_slope
  366. right_slope_list = right_slope_list > min_slope
  367. for i in range(len(left_slope_list)):
  368. if left_slope_list[i]:
  369. if i == len(left_slope_list) - 1:
  370. if not left_slope_list[i-1]:
  371. remove_list.extend([2*(i+1), 2*(i+1)+1])
  372. elif left_slope_list[i+1]:
  373. remove_list.extend([2*(i+1), 2*(i+1)+1])
  374. elif not left_slope_list[i+1]:
  375. remove_list.extend([2*i, 2*i+1])
  376. for i in range(len(right_slope_list)):
  377. if right_slope_list[i]:
  378. if i == len(right_slope_list) - 1:
  379. if not right_slope_list[i-1]:
  380. remove_list.extend([2*(i+1), 2*(i+1)+1])
  381. elif right_slope_list[i+1]:
  382. remove_list.extend([2*(i+1), 2*(i+1)+1])
  383. elif not right_slope_list[i+1]:
  384. remove_list.extend([2*i, 2*i+1])
  385. markers = [markers[i] for i in range(len(markers)) if i not in set(remove_list)]
  386. remove_list = []
  387. if len(markers) >= 2:
  388. left_x_list = np.asarray([markers[2*i][4][0] for i in range(len(markers)//2)])
  389. left_y_list = np.asarray([markers[2*i][4][1] for i in range(len(markers)//2)])
  390. right_x_list = np.asarray([markers[2*i+1][4][0] for i in range(len(markers)//2)])
  391. right_y_list = np.asarray([markers[2*i+1][4][1] for i in range(len(markers)//2)])
  392. distance_list = right_x_list - left_x_list
  393. shift_list = right_y_list - left_y_list
  394. left_x_mean = left_x_list.mean()
  395. distance_mean = distance_list.mean()
  396. shift_mean = shift_list.mean()
  397. left_x_std = left_x_list.std()
  398. distance_std = distance_list.std()
  399. shift_std = shift_list.std()
  400. if len(markers) >= 4:
  401. for i in range(len(markers)//2):
  402. if left_x_std > min_std and abs(left_x_list[i] - left_x_mean) / left_x_std > max_std:
  403. remove_list.extend([2*i, 2*i+1])
  404. elif shift_std > min_std and abs(shift_list[i] - shift_mean) / shift_std > max_std:
  405. remove_list.extend([2 * i, 2 * i + 1])
  406. elif distance_std > min_std and abs(distance_list[i] - distance_mean) / distance_std > max_std:
  407. remove_list.extend([2 * i, 2 * i + 1])
  408. elif len(markers) == 2:
  409. # area_ratio_list = np.asarray([m[-1]/((m[2]-m[0])*(m[3]-m[1])) for m in markers])
  410. if abs(distance_list-page_width) + abs(shift_list) > min_distance:
  411. remove_list.extend([0, 1])
  412. markers = [markers[i] for i in range(len(markers)) if i not in remove_list]
  413. if len(markers) >= 2:
  414. new_page_width = markers[1][4][0] - markers[0][4][0]
  415. else:
  416. new_page_width = page_width
  417. if debug == 1:
  418. print(len(markers))
  419. if len(markers) >= 4:
  420. print('left', left_x_mean, left_x_std)
  421. print(left_x_list)
  422. for i, x in enumerate(left_x_list):
  423. delta = abs(x - left_x_mean) / left_x_std
  424. print(delta, left_y_list[i])
  425. print('shift', shift_mean, shift_std)
  426. print(shift_list)
  427. for i, shift in enumerate(shift_list):
  428. delta = abs(shift - shift_mean) / shift_std
  429. print(delta, left_y_list[i])
  430. print('distance', distance_mean, distance_std)
  431. print(distance_list)
  432. for i, distance in enumerate(distance_list):
  433. delta = abs(distance - distance_mean) / distance_std
  434. print(delta, left_y_list[i])
  435. print('total')
  436. for i in range(len(left_x_list)):
  437. delta = abs(left_x_list[i] - left_x_mean) / left_x_std + abs(shift_list[i] - shift_mean) / shift_std \
  438. + abs(distance_list[i] - distance_mean) / distance_std
  439. d = abs(left_x_list[i] - left_x_mean) + abs(shift_list[i] - shift_mean) + \
  440. abs(distance_list[i] - distance_mean)
  441. print(delta, d, left_y_list[i])
  442. if debug == 2:
  443. if len(markers) >= 2:
  444. print('page width', page_width, 'new page width:', new_page_width,
  445. 'distant difference:', abs(new_page_width - page_width) + abs(markers[1][4][1]-markers[0][4][1]))
  446. return markers, new_page_width
  447. def draw_box(image, boxes, color=(0, 255, 0), debug=0):
  448. # 生成定位点标注框图
  449. for box in boxes:
  450. if len(box) > 0:
  451. cv2.rectangle(image, (box[0], box[1]), (box[2], box[3]), color, 5)
  452. if debug == 1:
  453. # 显示定位点信息
  454. for box in boxes:
  455. if len(box) == 4:
  456. width = box[2] - box[0]
  457. height = box[3] - box[1]
  458. w_to_h = width / height
  459. area = width * height
  460. # centroid = (box[0]+box[1])/2, (box[1]+box[3])/2
  461. print('width:{}, height:{}, w_to_h:{}, area:{}, top_left:{}, bottom_right:{},'.
  462. format(width, height, w_to_h, area, box[0:2], box[2:4]))
  463. elif len(box) > 4:
  464. width = box[2] - box[0]
  465. height = box[3] - box[1]
  466. w_to_h = width / height
  467. position_ratio = box[4][0] / image.shape[1]
  468. area = box[-1]
  469. centroid = box[4]
  470. area_ratio = area / (width * height)
  471. print('width:{}, height:{}, centroid:{}, position ratio:{}, w_to_h:{}, area:{}, area ratio:{}'.
  472. format(width, height, centroid, position_ratio, w_to_h, area, area_ratio))
  473. def find_pair(marker, boxes, page_width, threshold=100):
  474. # 若page_width为正,在boxes中找到marker的右配对, 若page_width为负, 在在boxes中找到marker的左配对
  475. distance = threshold
  476. pair_index = -1
  477. for i in range(len(boxes)):
  478. if abs(marker[4][1] - boxes[i][4][1]) + abs(boxes[i][4][0] - marker[4][0] - page_width) <= threshold:
  479. if abs(marker[4][1] - boxes[i][4][1]) + abs(boxes[i][4][0] - marker[4][0] - page_width) < distance:
  480. distance = abs(marker[4][1] - boxes[i][4][1]) + abs(boxes[i][4][0] - marker[4][0] - page_width)
  481. pair_index = i
  482. if pair_index >= 0:
  483. return boxes[pair_index], pair_index, distance
  484. else:
  485. return [], pair_index, distance
  486. def find_pair_list(marker_list, all_list, page_width, horizontal_threshold=100, debug=0):
  487. # all_list 中找到与marker_list最接近的配对list
  488. max_count = 0
  489. index_flag = -1
  490. min_distance = horizontal_threshold
  491. for index, l in enumerate(all_list):
  492. count = 0
  493. distance = 0
  494. for m in marker_list:
  495. if find_pair(m, l, page_width, horizontal_threshold)[1] >= 0:
  496. count += 1
  497. distance += find_pair(m, l, page_width, horizontal_threshold)[2]
  498. if count > max_count:
  499. max_count = count
  500. index_flag = index
  501. min_distance = distance / count
  502. elif count == max_count and count > 0:
  503. distance /= count
  504. if distance < min_distance:
  505. min_distance = distance
  506. index_flag = index
  507. if debug == 1:
  508. if index_flag >= 0:
  509. print('page width:', abs(marker_list[0][4][0] - all_list[index_flag][0][4][0]), 'anchor width:', page_width)
  510. return all_list[index_flag], index_flag, min_distance
  511. def find_column(anchors, width, column_num=2, debug=0):
  512. # 确定栏数,单栏宽度及第一栏和最后一栏的定位
  513. double_page_width_ratio = 0.42 # 默认双栏宽度比例
  514. three_page_width_ratio = 0.29 # 默认三栏宽度比例
  515. double_page_separation = 250 # 默认双栏栏间间距
  516. three_page_separation = 100 # 默认三栏栏间间距
  517. horizontal_threshold = 80 # 单栏宽度比例阈值
  518. top_anchors, bottom_anchors = anchors[:2]
  519. page_width = width
  520. if len(top_anchors) >= 2:
  521. for i in range(len(top_anchors)-1):
  522. page_width_0 = top_anchors[i+1][4][0] - top_anchors[i][4][0]
  523. page_width_1 = (top_anchors[i + 1][4][0] - top_anchors[i][4][0]) // 2
  524. page_width_2 = (top_anchors[i + 1][4][0] - top_anchors[i][4][0]) // 3
  525. if abs(page_width_0 - width * double_page_width_ratio) < horizontal_threshold:
  526. column_num = 2
  527. if page_width_0 < page_width:
  528. page_width = page_width_0
  529. elif abs(page_width_0 - width * three_page_width_ratio) < horizontal_threshold:
  530. column_num = 3
  531. if page_width_0 < page_width:
  532. page_width = page_width_0
  533. elif abs(page_width_1 - width * double_page_width_ratio) < horizontal_threshold:
  534. column_num = 2
  535. if page_width_1 < page_width:
  536. page_width = page_width_1
  537. elif abs(page_width_1 - width * three_page_width_ratio) < horizontal_threshold:
  538. column_num = 3
  539. if page_width_1 < page_width:
  540. page_width = page_width_1
  541. elif abs(page_width_2 - width * double_page_width_ratio) < horizontal_threshold:
  542. column_num = 2
  543. if page_width_2 < page_width:
  544. page_width = page_width_2
  545. elif abs(page_width_2 - width * three_page_width_ratio) < horizontal_threshold:
  546. column_num = 3
  547. if page_width_2 < page_width:
  548. page_width = page_width_2
  549. if page_width == width:
  550. if column_num == 2:
  551. page_width = int(width * double_page_width_ratio) # 如果没有找到合适的大定位点,使用默认的双栏宽度
  552. elif column_num == 3:
  553. page_width = int(width * three_page_width_ratio) # 如果没有找到合适的大定位点,使用默认的三栏宽度
  554. # 寻找第一栏和最后一栏的定位
  555. column_pos = []
  556. if len(top_anchors) >= 1:
  557. for i in range(4):
  558. if top_anchors[0][4][0] - (i + 1) * page_width < 0:
  559. column_pos.append(top_anchors[0][4][0] - i * page_width)
  560. break
  561. for i in range(4):
  562. if top_anchors[-1][4][0] + (i + 1) * page_width > width:
  563. column_pos.append(top_anchors[-1][4][0] + (i - 1) * page_width)
  564. break
  565. elif len(bottom_anchors) == 2:
  566. column_pos = [bottom_anchors[0][4][0], bottom_anchors[-1][4][0] - page_width]
  567. elif column_num == 2:
  568. column_pos = [(width - double_page_separation) // 2 - page_width, (width + double_page_separation) // 2]
  569. elif column_num == 3:
  570. column_pos = [width // 2 - three_page_separation - page_width * 3 // 2,
  571. width // 2 + three_page_separation + page_width // 2]
  572. if debug == 1:
  573. print('top anchors')
  574. for t in top_anchors:
  575. print(t[4])
  576. print('bottom anchors')
  577. for b in bottom_anchors:
  578. print(b[4])
  579. print('page width:', page_width, 'column number:', column_num, 'column position:', column_pos)
  580. return page_width, column_num, column_pos