anchor.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import re
  2. from dataclasses import dataclass
  3. from enum import Enum
  4. from typing import Tuple, List
  5. import cv2
  6. import numpy as np
  7. from paddleocr import PaddleOCR
  8. from core.line_parser import LineParser
  9. class Direction(Enum):
  10. TOP = 0
  11. RIGHT = 1
  12. BOTTOM = 2
  13. LEFT = 3
  14. class OcrAnchor(object):
  15. # 输入识别anchor的名字, 如身份证号
  16. def __init__(self, name: str, d: List[Direction]):
  17. self.name = name
  18. # anchor位置
  19. self.direction = d
  20. def t_func(anchor, c, is_horizontal):
  21. if is_horizontal:
  22. return 0 if anchor[1] < c[1] else 2
  23. else:
  24. return 1 if anchor[0] > c[0] else 3
  25. def l_func(anchor, c, is_horizontal):
  26. if is_horizontal:
  27. return 0 if anchor[0] < c[0] else 2
  28. else:
  29. return 1 if anchor[1] < c[1] else 3
  30. def b_func(anchor, c, is_horizontal):
  31. if is_horizontal:
  32. return 0 if anchor[1] > c[1] else 2
  33. else:
  34. return 1 if anchor[0] < c[0] else 3
  35. def r_func(anchor, c, is_horizontal):
  36. if is_horizontal:
  37. return 0 if anchor[0] > c[0] else 2
  38. else:
  39. return 1 if anchor[1] > c[1] else 3
  40. self.direction_funcs = {
  41. Direction.TOP: t_func,
  42. Direction.BOTTOM: b_func,
  43. Direction.LEFT: l_func,
  44. Direction.RIGHT: r_func,
  45. }
  46. # 获取中心区域坐标 -> (x, y)
  47. def get_rec_area(self, res) -> Tuple[float, float]:
  48. """获得整张身份证的识别区域, 返回识别区域的中心点"""
  49. boxes = []
  50. for row in res:
  51. for r in row:
  52. boxes.extend(r.box)
  53. boxes = np.stack(boxes)
  54. l, t = np.min(boxes, 0)
  55. r, b = np.max(boxes, 0)
  56. # 识别区域的box
  57. # big_box = [[l, t], [r, t], [r, b], [l, b]]
  58. # w, h = (r - l, b - t)
  59. return (l + r) / 2, (t + b) / 2
  60. # 判断是否是 锚点
  61. def is_anchor(self, txt, box, conf) -> bool:
  62. pass
  63. # 找 锚点 -> 锚点坐标
  64. def find_anchor(self, res) -> Tuple[bool, float, float]:
  65. """
  66. 寻找锚点 中心点坐标
  67. """
  68. for row in res:
  69. for r in row:
  70. txt = r.txt.replace('-', '').replace(' ', '')
  71. box = r.box
  72. conf = r.conf
  73. flag = self.is_anchor(txt, box, conf)
  74. if flag:
  75. l, t = np.min(box, 0)
  76. r, b = np.max(box, 0)
  77. return True, (l + r) / 2, (t + b) / 2
  78. # if flag and (len(re.findall('\d{10,20}', txt)) > 0 and conf > 0.95):
  79. # l, t = np.min(box, 0)
  80. # r, b = np.max(box, 0)
  81. # return True, (l + r) / 2, (t + b) / 2
  82. # elif flag:
  83. # l, t = np.min(box, 0)
  84. # r, b = np.max(box, 0)
  85. # if l:
  86. # return True, (l + r) / 2, (t + b) / 2
  87. # else:
  88. return False, 0., 0.
  89. # 定位 锚点 -> 角度
  90. # -> 锚点(x, y) pic(x, y) is_horizontal
  91. def locate_anchor(self, res, is_horizontal) -> int:
  92. found, id_cx, id_cy = self.find_anchor(res)
  93. # 如果识别不到身份证号
  94. if not found: raise Exception(f'识别不到anchor{self.name}')
  95. cx, cy = self.get_rec_area(res)
  96. pre = None
  97. for d in self.direction:
  98. f = self.direction_funcs.get(d, None)
  99. angle = f((id_cx, id_cy), (cx, cy), is_horizontal)
  100. if pre is None:
  101. pre = angle
  102. else:
  103. if angle != pre:
  104. raise Exception('angle is not compatiable')
  105. return pre
  106. class BankCardAnchor(OcrAnchor):
  107. def __init__(self, name: str, d: List[Direction]):
  108. super(BankCardAnchor, self).__init__(name, d)
  109. def is_anchor(self, txt, box, conf) -> bool:
  110. # # 这边我动了手脚,可能需要改一下长度,到时候测试再看
  111. # txts = re.findall('\d{5,20}', txt)
  112. # # print(txts)
  113. # if conf > 0.95 and len(txts) > 0:
  114. # # print("这是我识别出来的卡号:", txts)
  115. # return True
  116. # 这里逻辑有点长,理想情况下,置信度比较高的txt会在卡号附近,一般在卡号下方
  117. if len(re.findall('\d{16,20}', txt)) > 0 and conf > 0.95: # 完美找到卡号
  118. return True
  119. elif len(re.findall('\d{10,16}', txt)) > 0 and conf > 0.95: # 卡号只找到了一半多点
  120. return True
  121. elif len(re.findall('\d{6,10}', txt)) > 0 and conf > 0.95: # 卡号 只找到了一点
  122. return True
  123. elif len(re.findall('\d{4,6}', txt)) > 0 and conf > 0.95: # 卡号只找到了一丢丢
  124. return True
  125. elif conf > 0.95: # 可能卡号就是找到了一个数字,但是置信度很高,
  126. return True
  127. # elif conf >= 0.9:
  128. # return True
  129. return False
  130. def locate_anchor(self, res, is_horizontal) -> int:
  131. return super(BankCardAnchor, self).locate_anchor(res, is_horizontal)