idc.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. from flask import Blueprint, views, render_template, request
  2. from utils.util import *
  3. import re
  4. from utils.conf import MAX_CONTENT_LENGTH
  5. idc = Blueprint("idc", __name__, url_prefix="/idc")
  6. __CN = "中国CHINA"
  7. __face_ptn = r"^姓名(?P<name>.+)性别(?P<gender>男|女)民族(?P<nation>.+)" \
  8. r"出生(?P<year>\d{4})年(?P<month>\d\d)月(?P<day>\d\d)日" \
  9. r"住址(?P<addr>.+)公民身份号码(?P<idn>\d{17}\d|x|X)$"
  10. __icon_ptn = r"^中华人民共和国居民身份证签发机关(?P<agent>.+)" \
  11. r"有效期限(?P<from_year>\d{4})\.(?P<from_month>\d{2})\.(?P<from_day>\d{2})" \
  12. r"[^\d]+(?P<to_year>\d{4})\.(?P<to_month>\d{2})\.(?P<to_day>\d{2})$"
  13. # 需要图片在PC上看着是:横长竖宽
  14. def get_face_info(data: "list[str]") -> "tuple[dict, str, bool]":
  15. res = {"name": "", "gender": "", "nation": "", "birth": {"year": "", "month": "", "day": ""}, "addr": "", "idn": ""}
  16. if len(data) < 5: # 最少 5 个识别结果
  17. return res, "请使用正确的身份证人像面照片", False
  18. deal = [item.replace(" ", "") for item in data if not str_include(__CN, item)]
  19. if not deal[0].startswith("姓名"): # 非正,逆序后尝试
  20. deal.reverse()
  21. if not deal[0].startswith("姓名"):
  22. return res, "请确保照片为:横长竖宽,正面朝上", False
  23. str_all = "".join(deal)
  24. print(str_all)
  25. if match := re.match(__face_ptn, str_all):
  26. res["name"] = match.group("name")
  27. res["gender"] = match.group("gender")
  28. res["nation"] = match.group("nation")
  29. res["birth"] = {
  30. "year": match.group("year"),
  31. "month": match.group("month"),
  32. "day": match.group("day")
  33. }
  34. res["addr"] = match.group("addr")
  35. res["idn"] = match.group("idn")
  36. return res, "", True
  37. return res, "识别失败,请重新选择", False
  38. def get_icon_info(data: "list[str]"):
  39. res = {"agent": "", "from": {"year": "", "month": "", "day": ""}, "to": {"year": "", "month": "", "day": ""}}
  40. if len(data) < 4: # 最少 4 个识别结果
  41. return res, "请使用正确的身份证国徽面照片", False
  42. deal = [item.replace(" ", "") for item in data if not str_include(__CN, item)]
  43. if not deal[0].startswith("中华"): # 非正,逆序后尝试
  44. deal.reverse()
  45. if not deal[0].startswith("中华"):
  46. return res, "请确保照片为:横长竖宽,正面朝上", False
  47. str_all = "".join(deal)
  48. print(str_all)
  49. if match := re.match(__icon_ptn, str_all):
  50. res["agent"] = match.group("agent")
  51. res["from"] = {
  52. "year": match.group("from_year"),
  53. "month": match.group("from_month"),
  54. "day": match.group("from_day"),
  55. }
  56. res["to"] = {
  57. "year": match.group("to_year"),
  58. "month": match.group("to_month"),
  59. "day": match.group("to_day"),
  60. }
  61. return res, "", True
  62. return res, "识别失败,请重新选择", False
  63. class IdcView(views.MethodView):
  64. @staticmethod
  65. def get():
  66. return render_template("idc_index.html")
  67. @staticmethod
  68. def post():
  69. pic = request.files.get("picture")
  70. if pic is None:
  71. return Response("empty body")
  72. ext = get_ext_name(pic.filename)
  73. if not is_image_ext(ext):
  74. return Response("文件类型错误")
  75. content = pic.read()
  76. if len(content) > MAX_CONTENT_LENGTH:
  77. return Response("文件过大,请重新选择")
  78. raw_path = f"static/images/{current_time()}_{rand_str()}.{ext}"
  79. with open(raw_path, "wb") as fp:
  80. fp.write(content)
  81. fp.close()
  82. which = request.form.get("which")
  83. if which is not None:
  84. which = which.lower()
  85. if which not in ["face", "icon"]:
  86. return Response(f"not recognized arg <which>: '{which}'")
  87. ocr_res, _ = recognize(content)
  88. words = [it[1][0] for it in ocr_res]
  89. if which == "face":
  90. info, msg, sta = get_face_info(words)
  91. if sta:
  92. return Response(data=info)
  93. return Response(msg, info)
  94. info, msg, sta = get_icon_info(words)
  95. if sta:
  96. return Response(data=info)
  97. return Response(msg, info)
  98. class IdcHtmlView(views.MethodView):
  99. @staticmethod
  100. def post():
  101. pic = request.files.get("picture")
  102. if pic is None:
  103. return Response("empty body")
  104. ext = get_ext_name(pic.filename)
  105. if not is_image_ext(ext):
  106. return Response("文件类型错误")
  107. content = pic.read()
  108. if len(content) > MAX_CONTENT_LENGTH:
  109. return Response("文件过大,请重新选择")
  110. cut, rnd = current_time(), rand_str()
  111. raw_path = f"static/images/{cut}_{rnd}.{ext}"
  112. rec_path = f"static/images/{cut}_{rnd}_rec.{ext}"
  113. with open(raw_path, "wb") as fp:
  114. fp.write(content)
  115. fp.close()
  116. which = request.form.get("which")
  117. if which is not None:
  118. which = which.lower()
  119. if which not in ["face", "icon"]:
  120. return Response(f"not recognized arg <which>: '{which}'")
  121. ocr_res, img_shape = recognize(content)
  122. words = [it[1][0] for it in ocr_res]
  123. draw_img(img_shape, [{"pos": it[0], "word": it[1][0], "rate": it[1][1]} for it in ocr_res], rec_path)
  124. if which == "face":
  125. info, msg, sta = get_face_info(words)
  126. else:
  127. info, msg, sta = get_icon_info(words)
  128. info["SUCCESS"] = str(sta).upper()
  129. info["MESSAGE"] = msg
  130. return render_template("k-v_result.html", raw=raw_path, rec=rec_path, data=info)
  131. idc.add_url_rule("/", view_func=IdcView.as_view("idc"))
  132. idc.add_url_rule("/html/", view_func=IdcHtmlView.as_view("idc-html"))