extract_textpoint_slow.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. # Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Contains various CTC decoders."""
  15. from __future__ import absolute_import
  16. from __future__ import division
  17. from __future__ import print_function
  18. import cv2
  19. import math
  20. import numpy as np
  21. from itertools import groupby
  22. from skimage.morphology._skeletonize import thin
  23. def get_dict(character_dict_path):
  24. character_str = ""
  25. with open(character_dict_path, "rb") as fin:
  26. lines = fin.readlines()
  27. for line in lines:
  28. line = line.decode("utf-8").strip("\n").strip("\r\n")
  29. character_str += line
  30. dict_character = list(character_str)
  31. return dict_character
  32. def point_pair2poly(point_pair_list):
  33. """
  34. Transfer vertical point_pairs into poly point in clockwise.
  35. """
  36. pair_length_list = []
  37. for point_pair in point_pair_list:
  38. pair_length = np.linalg.norm(point_pair[0] - point_pair[1])
  39. pair_length_list.append(pair_length)
  40. pair_length_list = np.array(pair_length_list)
  41. pair_info = (
  42. pair_length_list.max(),
  43. pair_length_list.min(),
  44. pair_length_list.mean(),
  45. )
  46. point_num = len(point_pair_list) * 2
  47. point_list = [0] * point_num
  48. for idx, point_pair in enumerate(point_pair_list):
  49. point_list[idx] = point_pair[0]
  50. point_list[point_num - 1 - idx] = point_pair[1]
  51. return np.array(point_list).reshape(-1, 2), pair_info
  52. def shrink_quad_along_width(quad, begin_width_ratio=0.0, end_width_ratio=1.0):
  53. """
  54. Generate shrink_quad_along_width.
  55. """
  56. ratio_pair = np.array([[begin_width_ratio], [end_width_ratio]], dtype=np.float32)
  57. p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair
  58. p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair
  59. return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]])
  60. def expand_poly_along_width(poly, shrink_ratio_of_width=0.3):
  61. """
  62. expand poly along width.
  63. """
  64. point_num = poly.shape[0]
  65. left_quad = np.array([poly[0], poly[1], poly[-2], poly[-1]], dtype=np.float32)
  66. left_ratio = (
  67. -shrink_ratio_of_width
  68. * np.linalg.norm(left_quad[0] - left_quad[3])
  69. / (np.linalg.norm(left_quad[0] - left_quad[1]) + 1e-6)
  70. )
  71. left_quad_expand = shrink_quad_along_width(left_quad, left_ratio, 1.0)
  72. right_quad = np.array(
  73. [
  74. poly[point_num // 2 - 2],
  75. poly[point_num // 2 - 1],
  76. poly[point_num // 2],
  77. poly[point_num // 2 + 1],
  78. ],
  79. dtype=np.float32,
  80. )
  81. right_ratio = 1.0 + shrink_ratio_of_width * np.linalg.norm(
  82. right_quad[0] - right_quad[3]
  83. ) / (np.linalg.norm(right_quad[0] - right_quad[1]) + 1e-6)
  84. right_quad_expand = shrink_quad_along_width(right_quad, 0.0, right_ratio)
  85. poly[0] = left_quad_expand[0]
  86. poly[-1] = left_quad_expand[-1]
  87. poly[point_num // 2 - 1] = right_quad_expand[1]
  88. poly[point_num // 2] = right_quad_expand[2]
  89. return poly
  90. def softmax(logits):
  91. """
  92. logits: N x d
  93. """
  94. max_value = np.max(logits, axis=1, keepdims=True)
  95. exp = np.exp(logits - max_value)
  96. exp_sum = np.sum(exp, axis=1, keepdims=True)
  97. dist = exp / exp_sum
  98. return dist
  99. def get_keep_pos_idxs(labels, remove_blank=None):
  100. """
  101. Remove duplicate and get pos idxs of keep items.
  102. The value of keep_blank should be [None, 95].
  103. """
  104. duplicate_len_list = []
  105. keep_pos_idx_list = []
  106. keep_char_idx_list = []
  107. for k, v_ in groupby(labels):
  108. current_len = len(list(v_))
  109. if k != remove_blank:
  110. current_idx = int(sum(duplicate_len_list) + current_len // 2)
  111. keep_pos_idx_list.append(current_idx)
  112. keep_char_idx_list.append(k)
  113. duplicate_len_list.append(current_len)
  114. return keep_char_idx_list, keep_pos_idx_list
  115. def remove_blank(labels, blank=0):
  116. new_labels = [x for x in labels if x != blank]
  117. return new_labels
  118. def insert_blank(labels, blank=0):
  119. new_labels = [blank]
  120. for l in labels:
  121. new_labels += [l, blank]
  122. return new_labels
  123. def ctc_greedy_decoder(probs_seq, blank=95, keep_blank_in_idxs=True):
  124. """
  125. CTC greedy (best path) decoder.
  126. """
  127. raw_str = np.argmax(np.array(probs_seq), axis=1)
  128. remove_blank_in_pos = None if keep_blank_in_idxs else blank
  129. dedup_str, keep_idx_list = get_keep_pos_idxs(
  130. raw_str, remove_blank=remove_blank_in_pos
  131. )
  132. dst_str = remove_blank(dedup_str, blank=blank)
  133. return dst_str, keep_idx_list
  134. def instance_ctc_greedy_decoder(gather_info, logits_map, keep_blank_in_idxs=True):
  135. """
  136. gather_info: [[x, y], [x, y] ...]
  137. logits_map: H x W X (n_chars + 1)
  138. """
  139. _, _, C = logits_map.shape
  140. ys, xs = zip(*gather_info)
  141. logits_seq = logits_map[list(ys), list(xs)] # n x 96
  142. probs_seq = softmax(logits_seq)
  143. dst_str, keep_idx_list = ctc_greedy_decoder(
  144. probs_seq, blank=C - 1, keep_blank_in_idxs=keep_blank_in_idxs
  145. )
  146. keep_gather_list = [gather_info[idx] for idx in keep_idx_list]
  147. return dst_str, keep_gather_list
  148. def ctc_decoder_for_image(gather_info_list, logits_map, keep_blank_in_idxs=True):
  149. """
  150. CTC decoder using multiple processes.
  151. """
  152. decoder_results = []
  153. for gather_info in gather_info_list:
  154. res = instance_ctc_greedy_decoder(
  155. gather_info, logits_map, keep_blank_in_idxs=keep_blank_in_idxs
  156. )
  157. decoder_results.append(res)
  158. return decoder_results
  159. def sort_with_direction(pos_list, f_direction):
  160. """
  161. f_direction: h x w x 2
  162. pos_list: [[y, x], [y, x], [y, x] ...]
  163. """
  164. def sort_part_with_direction(pos_list, point_direction):
  165. pos_list = np.array(pos_list).reshape(-1, 2)
  166. point_direction = np.array(point_direction).reshape(-1, 2)
  167. average_direction = np.mean(point_direction, axis=0, keepdims=True)
  168. pos_proj_leng = np.sum(pos_list * average_direction, axis=1)
  169. sorted_list = pos_list[np.argsort(pos_proj_leng)].tolist()
  170. sorted_direction = point_direction[np.argsort(pos_proj_leng)].tolist()
  171. return sorted_list, sorted_direction
  172. pos_list = np.array(pos_list).reshape(-1, 2)
  173. point_direction = f_direction[pos_list[:, 0], pos_list[:, 1]] # x, y
  174. point_direction = point_direction[:, ::-1] # x, y -> y, x
  175. sorted_point, sorted_direction = sort_part_with_direction(pos_list, point_direction)
  176. point_num = len(sorted_point)
  177. if point_num >= 16:
  178. middle_num = point_num // 2
  179. first_part_point = sorted_point[:middle_num]
  180. first_point_direction = sorted_direction[:middle_num]
  181. sorted_fist_part_point, sorted_fist_part_direction = sort_part_with_direction(
  182. first_part_point, first_point_direction
  183. )
  184. last_part_point = sorted_point[middle_num:]
  185. last_point_direction = sorted_direction[middle_num:]
  186. sorted_last_part_point, sorted_last_part_direction = sort_part_with_direction(
  187. last_part_point, last_point_direction
  188. )
  189. sorted_point = sorted_fist_part_point + sorted_last_part_point
  190. sorted_direction = sorted_fist_part_direction + sorted_last_part_direction
  191. return sorted_point, np.array(sorted_direction)
  192. def add_id(pos_list, image_id=0):
  193. """
  194. Add id for gather feature, for inference.
  195. """
  196. new_list = []
  197. for item in pos_list:
  198. new_list.append((image_id, item[0], item[1]))
  199. return new_list
  200. def sort_and_expand_with_direction(pos_list, f_direction):
  201. """
  202. f_direction: h x w x 2
  203. pos_list: [[y, x], [y, x], [y, x] ...]
  204. """
  205. h, w, _ = f_direction.shape
  206. sorted_list, point_direction = sort_with_direction(pos_list, f_direction)
  207. # expand along
  208. point_num = len(sorted_list)
  209. sub_direction_len = max(point_num // 3, 2)
  210. left_direction = point_direction[:sub_direction_len, :]
  211. right_dirction = point_direction[point_num - sub_direction_len :, :]
  212. left_average_direction = -np.mean(left_direction, axis=0, keepdims=True)
  213. left_average_len = np.linalg.norm(left_average_direction)
  214. left_start = np.array(sorted_list[0])
  215. left_step = left_average_direction / (left_average_len + 1e-6)
  216. right_average_direction = np.mean(right_dirction, axis=0, keepdims=True)
  217. right_average_len = np.linalg.norm(right_average_direction)
  218. right_step = right_average_direction / (right_average_len + 1e-6)
  219. right_start = np.array(sorted_list[-1])
  220. append_num = max(int((left_average_len + right_average_len) / 2.0 * 0.15), 1)
  221. left_list = []
  222. right_list = []
  223. for i in range(append_num):
  224. ly, lx = (
  225. np.round(left_start + left_step * (i + 1))
  226. .flatten()
  227. .astype("int32")
  228. .tolist()
  229. )
  230. if ly < h and lx < w and (ly, lx) not in left_list:
  231. left_list.append((ly, lx))
  232. ry, rx = (
  233. np.round(right_start + right_step * (i + 1))
  234. .flatten()
  235. .astype("int32")
  236. .tolist()
  237. )
  238. if ry < h and rx < w and (ry, rx) not in right_list:
  239. right_list.append((ry, rx))
  240. all_list = left_list[::-1] + sorted_list + right_list
  241. return all_list
  242. def sort_and_expand_with_direction_v2(pos_list, f_direction, binary_tcl_map):
  243. """
  244. f_direction: h x w x 2
  245. pos_list: [[y, x], [y, x], [y, x] ...]
  246. binary_tcl_map: h x w
  247. """
  248. h, w, _ = f_direction.shape
  249. sorted_list, point_direction = sort_with_direction(pos_list, f_direction)
  250. # expand along
  251. point_num = len(sorted_list)
  252. sub_direction_len = max(point_num // 3, 2)
  253. left_direction = point_direction[:sub_direction_len, :]
  254. right_dirction = point_direction[point_num - sub_direction_len :, :]
  255. left_average_direction = -np.mean(left_direction, axis=0, keepdims=True)
  256. left_average_len = np.linalg.norm(left_average_direction)
  257. left_start = np.array(sorted_list[0])
  258. left_step = left_average_direction / (left_average_len + 1e-6)
  259. right_average_direction = np.mean(right_dirction, axis=0, keepdims=True)
  260. right_average_len = np.linalg.norm(right_average_direction)
  261. right_step = right_average_direction / (right_average_len + 1e-6)
  262. right_start = np.array(sorted_list[-1])
  263. append_num = max(int((left_average_len + right_average_len) / 2.0 * 0.15), 1)
  264. max_append_num = 2 * append_num
  265. left_list = []
  266. right_list = []
  267. for i in range(max_append_num):
  268. ly, lx = (
  269. np.round(left_start + left_step * (i + 1))
  270. .flatten()
  271. .astype("int32")
  272. .tolist()
  273. )
  274. if ly < h and lx < w and (ly, lx) not in left_list:
  275. if binary_tcl_map[ly, lx] > 0.5:
  276. left_list.append((ly, lx))
  277. else:
  278. break
  279. for i in range(max_append_num):
  280. ry, rx = (
  281. np.round(right_start + right_step * (i + 1))
  282. .flatten()
  283. .astype("int32")
  284. .tolist()
  285. )
  286. if ry < h and rx < w and (ry, rx) not in right_list:
  287. if binary_tcl_map[ry, rx] > 0.5:
  288. right_list.append((ry, rx))
  289. else:
  290. break
  291. all_list = left_list[::-1] + sorted_list + right_list
  292. return all_list
  293. def generate_pivot_list_curved(
  294. p_score,
  295. p_char_maps,
  296. f_direction,
  297. score_thresh=0.5,
  298. is_expand=True,
  299. is_backbone=False,
  300. image_id=0,
  301. ):
  302. """
  303. return center point and end point of TCL instance; filter with the char maps;
  304. """
  305. p_score = p_score[0]
  306. f_direction = f_direction.transpose(1, 2, 0)
  307. p_tcl_map = (p_score > score_thresh) * 1.0
  308. skeleton_map = thin(p_tcl_map)
  309. instance_count, instance_label_map = cv2.connectedComponents(
  310. skeleton_map.astype(np.uint8), connectivity=8
  311. )
  312. # get TCL Instance
  313. all_pos_yxs = []
  314. center_pos_yxs = []
  315. end_points_yxs = []
  316. instance_center_pos_yxs = []
  317. pred_strs = []
  318. if instance_count > 0:
  319. for instance_id in range(1, instance_count):
  320. pos_list = []
  321. ys, xs = np.where(instance_label_map == instance_id)
  322. pos_list = list(zip(ys, xs))
  323. ### FIX-ME, eliminate outlier
  324. if len(pos_list) < 3:
  325. continue
  326. if is_expand:
  327. pos_list_sorted = sort_and_expand_with_direction_v2(
  328. pos_list, f_direction, p_tcl_map
  329. )
  330. else:
  331. pos_list_sorted, _ = sort_with_direction(pos_list, f_direction)
  332. all_pos_yxs.append(pos_list_sorted)
  333. # use decoder to filter background points.
  334. p_char_maps = p_char_maps.transpose([1, 2, 0])
  335. decode_res = ctc_decoder_for_image(
  336. all_pos_yxs, logits_map=p_char_maps, keep_blank_in_idxs=True
  337. )
  338. for decoded_str, keep_yxs_list in decode_res:
  339. if is_backbone:
  340. keep_yxs_list_with_id = add_id(keep_yxs_list, image_id=image_id)
  341. instance_center_pos_yxs.append(keep_yxs_list_with_id)
  342. pred_strs.append(decoded_str)
  343. else:
  344. end_points_yxs.extend((keep_yxs_list[0], keep_yxs_list[-1]))
  345. center_pos_yxs.extend(keep_yxs_list)
  346. if is_backbone:
  347. return pred_strs, instance_center_pos_yxs
  348. else:
  349. return center_pos_yxs, end_points_yxs
  350. def generate_pivot_list_horizontal(
  351. p_score, p_char_maps, f_direction, score_thresh=0.5, is_backbone=False, image_id=0
  352. ):
  353. """
  354. return center point and end point of TCL instance; filter with the char maps;
  355. """
  356. p_score = p_score[0]
  357. f_direction = f_direction.transpose(1, 2, 0)
  358. p_tcl_map_bi = (p_score > score_thresh) * 1.0
  359. instance_count, instance_label_map = cv2.connectedComponents(
  360. p_tcl_map_bi.astype(np.uint8), connectivity=8
  361. )
  362. # get TCL Instance
  363. all_pos_yxs = []
  364. center_pos_yxs = []
  365. end_points_yxs = []
  366. instance_center_pos_yxs = []
  367. if instance_count > 0:
  368. for instance_id in range(1, instance_count):
  369. pos_list = []
  370. ys, xs = np.where(instance_label_map == instance_id)
  371. pos_list = list(zip(ys, xs))
  372. ### FIX-ME, eliminate outlier
  373. if len(pos_list) < 5:
  374. continue
  375. # add rule here
  376. main_direction = extract_main_direction(pos_list, f_direction) # y x
  377. reference_directin = np.array([0, 1]).reshape([-1, 2]) # y x
  378. is_h_angle = abs(np.sum(main_direction * reference_directin)) < math.cos(
  379. math.pi / 180 * 70
  380. )
  381. point_yxs = np.array(pos_list)
  382. max_y, max_x = np.max(point_yxs, axis=0)
  383. min_y, min_x = np.min(point_yxs, axis=0)
  384. is_h_len = (max_y - min_y) < 1.5 * (max_x - min_x)
  385. pos_list_final = []
  386. if is_h_len:
  387. xs = np.unique(xs)
  388. for x in xs:
  389. ys = instance_label_map[:, x].copy().reshape((-1,))
  390. y = int(np.where(ys == instance_id)[0].mean())
  391. pos_list_final.append((y, x))
  392. else:
  393. ys = np.unique(ys)
  394. for y in ys:
  395. xs = instance_label_map[y, :].copy().reshape((-1,))
  396. x = int(np.where(xs == instance_id)[0].mean())
  397. pos_list_final.append((y, x))
  398. pos_list_sorted, _ = sort_with_direction(pos_list_final, f_direction)
  399. all_pos_yxs.append(pos_list_sorted)
  400. # use decoder to filter background points.
  401. p_char_maps = p_char_maps.transpose([1, 2, 0])
  402. decode_res = ctc_decoder_for_image(
  403. all_pos_yxs, logits_map=p_char_maps, keep_blank_in_idxs=True
  404. )
  405. for decoded_str, keep_yxs_list in decode_res:
  406. if is_backbone:
  407. keep_yxs_list_with_id = add_id(keep_yxs_list, image_id=image_id)
  408. instance_center_pos_yxs.append(keep_yxs_list_with_id)
  409. else:
  410. end_points_yxs.extend((keep_yxs_list[0], keep_yxs_list[-1]))
  411. center_pos_yxs.extend(keep_yxs_list)
  412. if is_backbone:
  413. return instance_center_pos_yxs
  414. else:
  415. return center_pos_yxs, end_points_yxs
  416. def generate_pivot_list_slow(
  417. p_score,
  418. p_char_maps,
  419. f_direction,
  420. score_thresh=0.5,
  421. is_backbone=False,
  422. is_curved=True,
  423. image_id=0,
  424. ):
  425. """
  426. Warp all the function together.
  427. """
  428. if is_curved:
  429. return generate_pivot_list_curved(
  430. p_score,
  431. p_char_maps,
  432. f_direction,
  433. score_thresh=score_thresh,
  434. is_expand=True,
  435. is_backbone=is_backbone,
  436. image_id=image_id,
  437. )
  438. else:
  439. return generate_pivot_list_horizontal(
  440. p_score,
  441. p_char_maps,
  442. f_direction,
  443. score_thresh=score_thresh,
  444. is_backbone=is_backbone,
  445. image_id=image_id,
  446. )
  447. # for refine module
  448. def extract_main_direction(pos_list, f_direction):
  449. """
  450. f_direction: h x w x 2
  451. pos_list: [[y, x], [y, x], [y, x] ...]
  452. """
  453. pos_list = np.array(pos_list)
  454. point_direction = f_direction[pos_list[:, 0], pos_list[:, 1]]
  455. point_direction = point_direction[:, ::-1] # x, y -> y, x
  456. average_direction = np.mean(point_direction, axis=0, keepdims=True)
  457. average_direction = average_direction / (np.linalg.norm(average_direction) + 1e-6)
  458. return average_direction
  459. def sort_by_direction_with_image_id_deprecated(pos_list, f_direction):
  460. """
  461. f_direction: h x w x 2
  462. pos_list: [[id, y, x], [id, y, x], [id, y, x] ...]
  463. """
  464. pos_list_full = np.array(pos_list).reshape(-1, 3)
  465. pos_list = pos_list_full[:, 1:]
  466. point_direction = f_direction[pos_list[:, 0], pos_list[:, 1]] # x, y
  467. point_direction = point_direction[:, ::-1] # x, y -> y, x
  468. average_direction = np.mean(point_direction, axis=0, keepdims=True)
  469. pos_proj_leng = np.sum(pos_list * average_direction, axis=1)
  470. sorted_list = pos_list_full[np.argsort(pos_proj_leng)].tolist()
  471. return sorted_list
  472. def sort_by_direction_with_image_id(pos_list, f_direction):
  473. """
  474. f_direction: h x w x 2
  475. pos_list: [[y, x], [y, x], [y, x] ...]
  476. """
  477. def sort_part_with_direction(pos_list_full, point_direction):
  478. pos_list_full = np.array(pos_list_full).reshape(-1, 3)
  479. pos_list = pos_list_full[:, 1:]
  480. point_direction = np.array(point_direction).reshape(-1, 2)
  481. average_direction = np.mean(point_direction, axis=0, keepdims=True)
  482. pos_proj_leng = np.sum(pos_list * average_direction, axis=1)
  483. sorted_list = pos_list_full[np.argsort(pos_proj_leng)].tolist()
  484. sorted_direction = point_direction[np.argsort(pos_proj_leng)].tolist()
  485. return sorted_list, sorted_direction
  486. pos_list = np.array(pos_list).reshape(-1, 3)
  487. point_direction = f_direction[pos_list[:, 1], pos_list[:, 2]] # x, y
  488. point_direction = point_direction[:, ::-1] # x, y -> y, x
  489. sorted_point, sorted_direction = sort_part_with_direction(pos_list, point_direction)
  490. point_num = len(sorted_point)
  491. if point_num >= 16:
  492. middle_num = point_num // 2
  493. first_part_point = sorted_point[:middle_num]
  494. first_point_direction = sorted_direction[:middle_num]
  495. sorted_fist_part_point, sorted_fist_part_direction = sort_part_with_direction(
  496. first_part_point, first_point_direction
  497. )
  498. last_part_point = sorted_point[middle_num:]
  499. last_point_direction = sorted_direction[middle_num:]
  500. sorted_last_part_point, sorted_last_part_direction = sort_part_with_direction(
  501. last_part_point, last_point_direction
  502. )
  503. sorted_point = sorted_fist_part_point + sorted_last_part_point
  504. sorted_direction = sorted_fist_part_direction + sorted_last_part_direction
  505. return sorted_point
  506. def generate_pivot_list_tt_inference(
  507. p_score,
  508. p_char_maps,
  509. f_direction,
  510. score_thresh=0.5,
  511. is_backbone=False,
  512. is_curved=True,
  513. image_id=0,
  514. ):
  515. """
  516. return center point and end point of TCL instance; filter with the char maps;
  517. """
  518. p_score = p_score[0]
  519. f_direction = f_direction.transpose(1, 2, 0)
  520. p_tcl_map = (p_score > score_thresh) * 1.0
  521. skeleton_map = thin(p_tcl_map)
  522. instance_count, instance_label_map = cv2.connectedComponents(
  523. skeleton_map.astype(np.uint8), connectivity=8
  524. )
  525. # get TCL Instance
  526. all_pos_yxs = []
  527. if instance_count > 0:
  528. for instance_id in range(1, instance_count):
  529. pos_list = []
  530. ys, xs = np.where(instance_label_map == instance_id)
  531. pos_list = list(zip(ys, xs))
  532. ### FIX-ME, eliminate outlier
  533. if len(pos_list) < 3:
  534. continue
  535. pos_list_sorted = sort_and_expand_with_direction_v2(
  536. pos_list, f_direction, p_tcl_map
  537. )
  538. pos_list_sorted_with_id = add_id(pos_list_sorted, image_id=image_id)
  539. all_pos_yxs.append(pos_list_sorted_with_id)
  540. return all_pos_yxs