|
@@ -10,12 +10,13 @@ from paddleocr import PaddleOCR
|
|
|
from core.line_parser import LineParser
|
|
|
|
|
|
|
|
|
-class Directoin(Enum):
|
|
|
+class Direction(Enum):
|
|
|
TOP = 0
|
|
|
RIGHT = 1
|
|
|
BOTTOM = 2
|
|
|
LEFT = 3
|
|
|
|
|
|
+# 父类
|
|
|
class OcrAnchor(object):
|
|
|
# 输入识别anchor的名字, 如身份证号
|
|
|
def __init__(self, name: str, d: List[Directoin]):
|
|
@@ -48,12 +49,13 @@ class OcrAnchor(object):
|
|
|
return 1 if anchor[1] < c[1] else 3
|
|
|
|
|
|
self.direction_funcs = {
|
|
|
- Directoin.TOP: t_func,
|
|
|
- Directoin.BOTTOM: b_func,
|
|
|
- Directoin.LEFT: l_func,
|
|
|
- Directoin.RIGHT: r_func,
|
|
|
+ Direction.TOP: t_func,
|
|
|
+ Direction.BOTTOM: b_func,
|
|
|
+ Direction.LEFT: l_func,
|
|
|
+ Direction.RIGHT: r_func,
|
|
|
}
|
|
|
|
|
|
+ # 获取中心区域坐标 -> (x, y)
|
|
|
def get_rec_area(self, res) -> Tuple[float, float]:
|
|
|
"""获得整张身份证的识别区域, 返回识别区域的中心点"""
|
|
|
boxes = []
|
|
@@ -68,11 +70,15 @@ class OcrAnchor(object):
|
|
|
# w, h = (r - l, b - t)
|
|
|
return (l + r) / 2, (t + b) / 2
|
|
|
|
|
|
+ # 判断是否是 锚点
|
|
|
def is_anchor(self, txt, box) -> bool:
|
|
|
pass
|
|
|
|
|
|
+ # 找 锚点 -> 锚点坐标
|
|
|
def find_anchor(self, res) -> Tuple[bool, float, float]:
|
|
|
- """寻找身份证号的识别区域以及中心点,根据身份证的w > h判断是否水平"""
|
|
|
+ """
|
|
|
+ 寻找身份证号的识别区域以及中心点
|
|
|
+ """
|
|
|
for row in res:
|
|
|
for r in row:
|
|
|
txt = r.txt.replace('-', '').replace(' ', '')
|
|
@@ -83,6 +89,8 @@ class OcrAnchor(object):
|
|
|
return True, (l + r) / 2, (t + b) / 2
|
|
|
return False, 0., 0.
|
|
|
|
|
|
+ # 定位 锚点 -> 角度
|
|
|
+ # -> 锚点(x, y) pic(x, y) is_horizontal
|
|
|
def locate_anchor(self, res, is_horizontal) -> int:
|
|
|
found, id_cx, id_cy = self.find_anchor(res)
|
|
|
|
|
@@ -102,6 +110,7 @@ class OcrAnchor(object):
|
|
|
raise Exception('angle is not compatiable')
|
|
|
return pre
|
|
|
|
|
|
+
|
|
|
# if is_horizontal:
|
|
|
# # 如果是水平的,身份证号的位置在相对识别区域的下方,方向则为0度,否则是180度
|
|
|
# return 0 if id_cy > cy else 2
|
|
@@ -110,6 +119,7 @@ class OcrAnchor(object):
|
|
|
# return 1 if id_cx < cx else 3
|
|
|
|
|
|
|
|
|
+# 子类1 人像面
|
|
|
class FrontSideAnchor(OcrAnchor):
|
|
|
def __init__(self, name: str, d: List[Directoin]):
|
|
|
super(FrontSideAnchor, self).__init__(name, d)
|
|
@@ -124,6 +134,7 @@ class FrontSideAnchor(OcrAnchor):
|
|
|
return super(FrontSideAnchor, self).locate_anchor(res, is_horizontal)
|
|
|
|
|
|
|
|
|
+# 子类2 国徽面
|
|
|
class BackSideAnchor(OcrAnchor):
|
|
|
def __init__(self, name: str, d: List[Directoin]):
|
|
|
super(BackSideAnchor, self).__init__(name, d)
|
|
@@ -139,6 +150,10 @@ class BackSideAnchor(OcrAnchor):
|
|
|
return super(BackSideAnchor, self).locate_anchor(res, is_horizontal)
|
|
|
|
|
|
|
|
|
+# 调用以上 🔧工具
|
|
|
+# <- ocr_生数据
|
|
|
+# == ocr_熟数据(行处理后)
|
|
|
+# -> 角度0/1/2/3
|
|
|
def detect_angle(result, ocr_anchor: OcrAnchor):
|
|
|
lp = LineParser(result)
|
|
|
res = lp.parse()
|
|
@@ -150,13 +165,20 @@ def detect_angle(result, ocr_anchor: OcrAnchor):
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
-# 角度检测器
|
|
|
class AngleDetector(object):
|
|
|
+ """
|
|
|
+ 角度检测器
|
|
|
+ """
|
|
|
ocr: PaddleOCR
|
|
|
|
|
|
+ # 角度检测器
|
|
|
+ # <- img(cv2格式) img_type
|
|
|
+ # == result <- img(cv2)
|
|
|
+ # -> angle result(ocr生)
|
|
|
def detect_angle(self, img, image_type):
|
|
|
image_type = int(image_type)
|
|
|
- ocr_anchor = BackSideAnchor('有效期', [Directoin.BOTTOM]) if image_type != 0 else FrontSideAnchor('身份证号', Directoin.BOTTOM)
|
|
|
+ ocr_anchor = BackSideAnchor('有效期', [Directoin.BOTTOM]) if image_type != 0 else FrontSideAnchor('身份证号', [Directoin.BOTTOM])
|
|
|
+
|
|
|
result = self.ocr.ocr(img, cls=True)
|
|
|
|
|
|
try:
|
|
@@ -171,38 +193,3 @@ class AngleDetector(object):
|
|
|
angle = detect_angle(result, ocr_anchor)
|
|
|
# 旋转90度之后要重新计算角度
|
|
|
return (angle - 1 + 4) % 4, result
|
|
|
-
|
|
|
- def _detect_back(self, image):
|
|
|
- mask = np.zeros(image.shape, dtype=np.uint8)
|
|
|
- gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
|
|
|
- blur = cv2.GaussianBlur(gray, (3, 3), 0)
|
|
|
- adaptive = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 15, 4)
|
|
|
-
|
|
|
- cnts = cv2.findContours(adaptive, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
|
|
- cnts = cnts[0] if len(cnts) == 2 else cnts[1]
|
|
|
-
|
|
|
- for c in cnts:
|
|
|
- area = cv2.contourArea(c)
|
|
|
- if area < 45000 and area > 20:
|
|
|
- cv2.drawContours(mask, [c], -1, (255, 255, 255), -1)
|
|
|
-
|
|
|
- mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
|
|
|
- h, w = mask.shape
|
|
|
-
|
|
|
- # Horizontal
|
|
|
- if w > h:
|
|
|
- left = mask[0:h, 0:0 + w // 2]
|
|
|
- right = mask[0:h, w // 2:]
|
|
|
- left_pixels = cv2.countNonZero(left)
|
|
|
- right_pixels = cv2.countNonZero(right)
|
|
|
- print(f'left: {left_pixels}, right: {right_pixels}')
|
|
|
- angle = 0 if left_pixels >= right_pixels else 2
|
|
|
- # Vertical
|
|
|
- else:
|
|
|
- top = mask[0:h // 2, 0:w]
|
|
|
- bottom = mask[h // 2:, 0:w]
|
|
|
- top_pixels = cv2.countNonZero(top)
|
|
|
- bottom_pixels = cv2.countNonZero(bottom)
|
|
|
- print(f'top: {top_pixels}, bottom: {bottom_pixels}')
|
|
|
- angle = 1 if bottom_pixels <= top_pixels else 3
|
|
|
- return angle, None
|