zeke-chin 2 роки тому
батько
коміт
dfe2f0b88a

+ 5 - 1
.gitignore

@@ -2,4 +2,8 @@
 .vscode/
 .DS_Store
 __pycache__
-*.pyc
+*.pyc
+images/test/
+convert_markdown.py
+markdown
+*.md

+ 0 - 2
convert_json.py

@@ -54,8 +54,6 @@ if __name__ == '__main__':
     #
     #     with (d / fn).open('w', encoding='utf-8') as f:
     #         json.dump(res, f, ensure_ascii=False, indent=4)
-
-    1
     root = Path(__file__).parent
     print(root)
     img_paths = chain(

+ 5 - 6
core/direction.py

@@ -7,7 +7,6 @@ from enum import Enum
 from typing import Tuple, List
 
 import cv2
-import numpy as np
 from paddleocr import PaddleOCR
 
 from core.line_parser import LineParser
@@ -80,10 +79,10 @@ class OcrAnchor(object):
         for row in res:
             for r in row:
                 if self.is_anchor(r.txt, r.box):
-                    l, t = np.min(r.box, 0)
-                    r, b = np.max(r.box, 0)
-                    return True, (l + r) / 2, (t + b) / 2
-                    # return True, r.center[0], r.center[1]
+                    # l, t = np.min(r.box, 0)
+                    # r, b = np.max(r.box, 0)
+                    # return True, (l + r) / 2, (t + b) / 2
+                    return True, r.center[0], r.center[1]
         return False, 0., 0.
 
     # get angle
@@ -126,7 +125,7 @@ class PeopleAnchor(OcrAnchor):
         super(PeopleAnchor, self).__init__(name, d)
 
     def is_anchor(self, txt, box):
-        txts = re.findall('常住', txt)
+        txts = re.findall('常住', txt) or re.findall('登记卡', txt)
         if len(txts) > 0:
             return True
         return False

+ 40 - 16
core/line_parser.py

@@ -1,6 +1,7 @@
 import numpy as np
 from dataclasses import dataclass
 
+
 # result 对象
 @dataclass
 class OcrResult(object):
@@ -30,6 +31,20 @@ class OcrResult(object):
         r, b = self.rb
         return [r - l, b - t]
 
+    @property
+    def area(self):
+        w, h = self.wh
+        return w * h
+
+    @property
+    def is_slope(self):
+        p0 = self.box[0]
+        p1 = self.box[1]
+        if p0[0] == p1[0]:
+            return False
+        slope = abs(1. * (p0[1] - p1[1]) / (p0[0] - p1[0]))
+        return 0.4 < slope < 2.5
+
     @property
     def center(self):
         l, t = self.lt
@@ -37,25 +52,32 @@ class OcrResult(object):
         return [(r + l) / 2, (b + t) / 2]
 
     def one_line(self, b, is_horizontal, eps: float = 20.0) -> bool:
-        if is_horizontal:
-            return abs(self.lt[1] - b.lt[1]) < eps
-        else:
-            return abs(self.rb[0] - b.rb[0]) < eps
+        y_idx = 0 + is_horizontal
+        x_idx = 1 - y_idx
+        if b.lt[x_idx] < self.lt[x_idx] < self.rb[x_idx] < b.rb[x_idx]: return False
+        if self.lt[x_idx] < b.lt[x_idx] < b.rb[x_idx] < self.rb[x_idx]: return False
+        eps = 0.45 * (self.wh[y_idx] + b.wh[y_idx])
+        dist = abs(self.center[y_idx] - b.center[y_idx])
+        return dist < eps
 
 
 # 行处理器
 class LineParser(object):
-    def __init__(self, ocr_raw_result):
-        # self.is_horizontal = ocr_raw_result.is_horizontal
+    def __init__(self, ocr_raw_result, filters=None):
+        if filters is None:
+            filters = [lambda x: x.is_slope]
         self.ocr_res = []
         for re in ocr_raw_result:
             o = OcrResult(np.array(re[0]), re[1][0], re[1][1])
+            if any([f(o) for f in filters]): continue
             self.ocr_res.append(o)
+        # for f in filters:
+        #     self.ocr_res = list(filter(f, self.ocr_res))
+        self.ocr_res = sorted(self.ocr_res, key=lambda x: x.area, reverse=True)
         self.eps = self.avg_height * 0.86
 
-    # 判断是否水平
     @property
-    def is_horizontal(self) -> bool:
+    def is_horizontal(self):
         res = self.ocr_res
         wh = np.stack([np.abs(np.array(r.lt) - np.array(r.rb)) for r in res])
         return np.sum(wh[:, 0] > wh[:, 1]) > np.sum(wh[:, 0] < wh[:, 1])
@@ -65,6 +87,7 @@ class LineParser(object):
         idx = self.is_horizontal + 0
         return np.mean(np.array([r.wh[idx] for r in self.ocr_res]))
 
+    # 整体置信度
     @property
     def confidence(self):
         return np.mean([r.conf for r in self.ocr_res])
@@ -86,13 +109,6 @@ class LineParser(object):
             # 拿出 OcrResult对象的 第i值 -暂存-
             res_i = self.ocr_res[i]
 
-            # any:-> True
-            #       -input: 可迭代对象   |   -output: bool
-            #       -如果iterable的任何元素为true,则返回true。如果iterable为空,则返回false。 -与🚪-
-            # map: -> [False, False, False, False, True, True, False, False]
-            #       -input: (函数, 可迭代对象)     |    -output: 可迭代对象
-            #       -把 res 喂给lambda --lambda返回True的值-->  输出 新的可迭代对象
-
             # 这次的 res_i 之前已经在结果集中,就继续下一个
             if any(map(lambda x: res_i in x, res)): continue
 
@@ -102,10 +118,18 @@ class LineParser(object):
 
             for j in range(i, length):
                 res_j = self.ocr_res[j]
+                # 这次的 res_i 之前已经在结果集中,就继续下一个
+                if any(map(lambda x: res_j in x, res)): continue
+
                 if res_i.one_line(res_j, self.is_horizontal, self.eps):
                     # LineParser 对象  不可以直接加入字典
 
                     res_row.add(res_j)
             res.append(res_row)
         idx = self.is_horizontal + 0
-        return sorted([list(r) for r in res], key=lambda x: x[0].lt[idx])
+        res = sorted([sorted(list(r), key=lambda x: x.lt[1 - idx]) for r in res], key=lambda x: x[0].lt[idx])
+        for row in res:
+            print('---')
+            print(''.join([r.txt for r in row]))
+        return res
+

+ 3 - 3
core/ocr.py

@@ -26,9 +26,9 @@ class IdCardOcr:
     def predict(self, image: np.ndarray, image_type: str):
         img_type = int(image_type)
 
-        img, angle, result = self._rotate_img(image, img_type)
+        image, angle, result = self._rotate_img(image, img_type)
         print(f'---------- detect angle: {angle} 图片角度 ----------')
-        if image_type == 0:
+        if img_type == 0:
             if angle != 0:
                 # 角度不为0需要重新识别,字面
                 _, _, result = self._ocr(image)
@@ -41,7 +41,7 @@ class IdCardOcr:
     def _rotate_img(self, image, image_type) -> (np.ndarray, int):
         angle, result = self.angle_detector.detect_angle(image, image_type)
         if angle == 1:
-            image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)
+            image = cv2.rotate(image, cv2.ROTATE_90_COUNTERCLOCKWISE)
         if angle == 2:
             image = cv2.rotate(image, cv2.ROTATE_180)
         if angle == 3:

+ 26 - 19
core/parser.py

@@ -33,17 +33,15 @@ class Parser(object):
         for key in self.keys:
             self.res[key] = RecItem()
 
-        for i in range(len(self.result)):
-            temp = [self.result[i][0].txt, self.result[i][0].conf]
-            for j in range(len(self.result[i])):
-                self.result[i][j].txt = self.result[i][j].txt \
-                    .replace("|", "").replace(":", "").replace(":", "").replace(",", "").replace(",", "") \
-                    .replace("【", "").replace("】", "").replace("「", "").replace("[", "").replace("]", "") \
-                    .replace(" ", "")
-            for k in range(1, len(self.result[i])):
-                temp[0] = temp[0] + self.result[i][k].txt
-                temp[1] = np.mean([temp[1], self.result[i][k].conf])
-            self.result[i].append(temp)
+        for item in self.result:
+            temp = [item[0].txt, item[0].conf]
+            for j in range(len(item)):
+                item[j].txt = item[j].txt.replace("|", "").replace(":", "").replace(":", "").replace(",", "").replace(",", "").replace("【", "").replace("】", "").replace("「", "").replace("[", "").replace("]", "").replace(" ", "")
+
+            for k in range(1, len(item)):
+                temp[0] = temp[0] + item[k].txt
+                temp[1] = np.mean([temp[1], item[k].conf])
+            item.append(temp)
 
     def parse(self):
         return self.res
@@ -83,7 +81,7 @@ class FrontRegBookParser(Parser):
                     or "街" in txt
 
             ):
-                address_txt = txt
+                address_txt = txt.split("民族")[0]
                 break
 
         if address_txt is not None:
@@ -100,9 +98,9 @@ class FrontRegBookParser(Parser):
         df.replace([None], [''])
 
         province = df.iloc[0, 0]
-        city = df.iloc[0, 1]
-        region = df.iloc[0, 2]
-        detail = df.iloc[0, 3]
+        city = df.iloc[0, 1] or ""
+        region = df.iloc[0, 2] or ""
+        detail = df.iloc[0, 3] or ""
         print(f'pronvince: {province}, city: {city}, region: {region}, detail: {detail}')
         self.res["address_province"] = RecItem(province, conf)
         self.res["address_city"] = RecItem(city, conf)
@@ -116,6 +114,10 @@ class FrontRegBookParser(Parser):
             self.res["address_region"] = RecItem(region, conf)
             self.res["address_detail"] = RecItem(detail, conf)
 
+        city_dic = {"宜城市":"宣城市"}
+        if city in city_dic:
+            city = city_dic[city]
+
         self.res['address'].text = province + city + region + detail
 
     # 存入
@@ -134,12 +136,17 @@ class PeopleRegBookParser(Parser):
         """
         name_val = ''
         conf = 0.
+        is_name = False
         for i in range(len(self.result)):
             res = self.result[i]
             txt = res[-1][0]
             conf = res[-1][1]
-            if "姓名" in txt:
-                name_val = txt.split("姓名")[-1].split("户主")[0]
+            for s in range(len(txt)):
+                if txt[s] == "名" and s < 2 and "姓名" in txt:
+                    is_name = True
+            if is_name:
+                name_val = txt.split("姓名")[-1].split("户主")[0].split("中主")[0]
+                break
 
         if len(name_val) < 5:
             self.res["name"] = RecItem(name_val, conf)
@@ -238,7 +245,7 @@ class PeopleRegBookParser(Parser):
             txt = res[-1][0]
             birth_place_conf = res[-1][1]
             if "出生地" in txt:
-                birth_place_txt = txt.split('民族')[0]
+                birth_place_txt = txt.split('民族')[0].split('民')[0]
                 break
 
         if birth_place_txt:
@@ -256,7 +263,7 @@ class PeopleRegBookParser(Parser):
             res = self.result[i]
             txt = res[-1][0]
             native_place_conf = res[-1][1]
-            if '贯' in txt:
+            if '贯' in txt and '出' in txt:
                 native_place_txt = txt.split('出生')[0]
                 break
 

+ 3 - 3
docker-compose.yml

@@ -1,8 +1,8 @@
 version: '2'
 services:
-  idcard:
-    hostname: schoolcert
-    container_name: schoolcert
+  regbook:
+    hostname: regbook
+    container_name: regbook
     restart: always
     image: registry.cn-hangzhou.aliyuncs.com/sxtest/regbook:cpu
     privileged: true

BIN
huko_det_infer/inference.pdiparams


BIN
huko_det_infer/inference.pdiparams.info


BIN
huko_det_infer/inference.pdmodel


BIN
images/all/0/06_0.jpg


BIN
images/all/0/06_180.jpg


BIN
images/all/0/06_270.jpg


BIN
images/all/0/06_90.jpg


+ 7 - 2
server.py

@@ -2,6 +2,8 @@ from fastapi import FastAPI, Request
 from fastapi.middleware.cors import CORSMiddleware
 from pydantic import BaseModel
 from paddleocr import PaddleOCR
+
+from core.direction import AngleDetector
 from sx_utils.sximage import *
 from sx_utils.sxtime import sxtimeit
 from sx_utils.sxweb import web_try
@@ -30,7 +32,7 @@ print(f'use gpu: {use_gpu}')
 # 初始化ocr模型和后处理模型
 ocr = PaddleOCR(use_angle_cls=True,
                 rec_model_dir="./ch_ppocr_server_v2.0_rec_infer/",
-                det_model_dir="./ch_ppocr_server_v2.0_det_infer/",
+                det_model_dir="./huko_det_infer/",
                 cls_model_dir="./huko_cls_infer/",
                 rec_algorithm='CRNN',
                 ocr_version='PP-OCRv2',
@@ -40,7 +42,10 @@ ocr = PaddleOCR(use_angle_cls=True,
 # ocr = PaddleOCR(use_angle_cls=True,
 #                 use_gpu=use_gpu)
 
-m = IdCardOcr(ocr)
+# 初始化 角度检测器 对象
+ad = AngleDetector(ocr)
+
+m = IdCardOcr(ocr, ad)
 
 
 @app.get("/ping")

+ 50 - 0
testing/orient_0_test.py

@@ -0,0 +1,50 @@
+import unittest
+import base64
+from pathlib import Path
+
+import requests
+from testing.utils import *
+
+
+class TestIdCardAddress(unittest.TestCase):
+    def _helper(self, image_path, orient, image_type='0'):
+        root = Path(__file__).parent
+        image_path = str(root / image_path)
+        r = send_request(image_path, image_type)
+        self.assertEqual(orient, r['result']['orientation'], f'{image_path} orientation case error')
+
+    def test_03_0(self):
+        image_path = '../images/all/0/03.png'
+        self._helper(image_path, 0)
+
+    def test_05_0(self):
+        image_path = '../images/all/0/05_0.jpg'
+        self._helper(image_path, 0)
+
+    def test_05_90(self):
+        image_path = '../images/all/0/05_90.jpg'
+        self._helper(image_path, 1)
+
+    def test_05_180(self):
+        image_path = '../images/all/0/05_180.jpg'
+        self._helper(image_path, 2)
+
+    def test_05_270(self):
+        image_path = '../images/all/0/05_270.jpg'
+        self._helper(image_path, 3)
+
+    def test_06_0(self):
+        image_path = '../images/all/0/06_0.jpg'
+        self._helper(image_path, 0)
+
+    def test_06_90(self):
+        image_path = '../images/all/0/06_90.jpg'
+        self._helper(image_path, 1)
+
+    def test_06_180(self):
+        image_path = '../images/all/0/06_180.jpg'
+        self._helper(image_path, 2)
+
+    def test_06_270(self):
+        image_path = '../images/all/0/06_270.jpg'
+        self._helper(image_path, 3)

+ 14 - 0
testing/orient_1_test.py

@@ -0,0 +1,14 @@
+import unittest
+import base64
+from pathlib import Path
+
+import requests
+from testing.utils import *
+
+
+class TestIdCardAddress(unittest.TestCase):
+    def _helper(self, image_path, orient, image_type='0'):
+        root = Path(__file__).parent
+        image_path = str(root / image_path)
+        r = send_request(image_path, image_type)
+        self.assertEqual(orient, r['result']['orientation'], f'{image_path} orientation case error')

+ 12 - 0
testing/utils.py

@@ -0,0 +1,12 @@
+import base64
+import requests
+
+url = 'http://0.0.0.0:8080'
+
+
+def send_request(image_path, image_type):
+    with open(image_path, 'rb') as f:
+        img_str: str = base64.encodebytes(f.read()).decode('utf-8')
+        r = requests.post(f'{url}/ocr_system/regbook', json={'image': img_str, 'image_type': image_type})
+        print(r.json())
+        return r.json()