new.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. from pathlib import Path
  2. from typing import List, Optional
  3. import cv2
  4. import requests
  5. from mdutils.mdutils import MdUtils
  6. from dataclasses import dataclass
  7. import json
  8. import time
  9. import base64
  10. from itertools import chain
  11. from tqdm import tqdm
  12. import numpy as np
  13. from ocr_config import OCR_CONFIGS
  14. class Image:
  15. def __init__(self, path: Path, rotate):
  16. self._path = path
  17. self.rotate = rotate
  18. self._ocr_result = None
  19. self.category = True
  20. try:
  21. self.gt_result = self.get_json()
  22. except Exception as e:
  23. print(self.json_path)
  24. raise e
  25. def __repr__(self):
  26. return f'path: {self.path}, rotate: {self.rotate}, gt_result: {self.gt_result}, cate: {self.category}'
  27. @property
  28. def path(self):
  29. return self._path
  30. @path.setter
  31. def path(self, path):
  32. self._path = path
  33. @property
  34. def fn(self):
  35. return self._path.stem
  36. @property
  37. def ocr_result(self):
  38. return self._ocr_result
  39. @ocr_result.setter
  40. def ocr_result(self, value):
  41. self._ocr_result = value
  42. def get_gt_result(self, key):
  43. if key == 'orientation':
  44. return self.rotate + 1 if self.rotate is not None else 0
  45. elif key in self.gt_result:
  46. return self.gt_result[key]
  47. else:
  48. return None
  49. @property
  50. def json_path(self):
  51. return self.path.parent / f'{self.path.stem}.json'
  52. def save_image(self, img, rotate):
  53. dst = self.path.parent.parent / (".ro_dst")
  54. if not dst.exists(): dst.mkdir()
  55. self.path = dst / f'{self.path.stem}-{rotate + 1}.jpg'
  56. # print('save image', self.path)
  57. img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
  58. cv2.imwrite(str(self.path), img)
  59. def get_base64(self, rotate=None):
  60. # print(self.path)
  61. img = cv2.imread(str(self.path))
  62. img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
  63. if rotate is not None:
  64. img = cv2.rotate(img, rotate)
  65. self.save_image(img, rotate)
  66. _, img = cv2.imencode('.jpg', img)
  67. img_str = base64.b64encode(img).decode('utf-8')
  68. return img_str
  69. def get_json(self):
  70. with open(self.json_path, 'r') as f:
  71. return json.load(f)
  72. def send_request(image: Image, ocr_name, ocr_address, image_type=None):
  73. base64_str = image.get_base64(image.rotate)
  74. config = OCR_CONFIGS[ocr_name][ocr_address]
  75. headers = {
  76. 'Content-Type': 'application/json',
  77. 'Authorization': config.token
  78. }
  79. data = {
  80. 'image': base64_str,
  81. }
  82. if image_type is not None:
  83. data['image_type'] = image_type
  84. response = requests.post(config.url, headers=headers, json=data)
  85. return response.json()
  86. def parser_path(path: Path, rotate: bool):
  87. name = time.strftime("%m-%d_", time.localtime()) + path.name
  88. if rotate:
  89. name = f'{name}_R.md'
  90. return path.parent / name
  91. class Dataset(object):
  92. def __init__(self, images_path, image_type, ocr_name, ocr_address, rotate=False):
  93. self.image_type = image_type
  94. self.ocr_name = ocr_name
  95. self.ocr_address = ocr_address
  96. self.images_path = images_path
  97. self.image_list = []
  98. for p in chain(*[Path(self.images_path).rglob('*.jpg')]):
  99. if rotate:
  100. self.image_list.extend(Image(p, r) for r in [None, 0, 1, 2])
  101. else:
  102. self.image_list.append(Image(p, None))
  103. self.field = ['orientation', 'name', 'id', 'language', 'level', 'exam_time', 'score']
  104. # if self.image_type:
  105. # self.field = ['orientation', 'type', 'address', 'address_province', 'address_city', 'address_region',
  106. # 'address_detail']
  107. # else:
  108. # self.field = ['orientation', 'name', 'id', 'gender', 'birthplace', 'birthplace_province', 'birthplace_city',
  109. # 'birthplace_region', 'native_place', 'native_place_province', 'native_place_city',
  110. # 'native_place_region', 'blood_type', 'religion']
  111. self.correct = {k: 0 for k in self.field}
  112. self.error = {k: 0 for k in self.field}
  113. def __len__(self):
  114. return len(self.image_list)
  115. def _evaluate_one(self, image: Image):
  116. def _get_predict(r, key):
  117. if isinstance(r[key], dict):
  118. return r[key]['text']
  119. else:
  120. return r[key]
  121. if image.rotate is not None: image.gt_result['orientation'] = image.rotate + 1
  122. r = send_request(image, self.ocr_name, self.ocr_address, self.image_type)
  123. err_str = ''
  124. if r['status'] == '000':
  125. res = r['result']
  126. for key in self.field:
  127. # print('attr: ', key)
  128. if key in res:
  129. gt = image.get_gt_result(key)
  130. predict = _get_predict(res, key)
  131. # print(f'gt: {gt}, predict: {predict}')
  132. if predict == gt:
  133. self.correct[key] += 1
  134. else:
  135. image.category = False
  136. self.error[key] += 1
  137. err_str += f'正确:{gt}<br>返回:{predict}<br>'
  138. if image.category:
  139. image.ocr_result = image.gt_result
  140. else:
  141. image.ocr_result = err_str
  142. else:
  143. image.ocr_result = r['msg']
  144. image.category = False
  145. for key in self.field:
  146. self.error[key] += 1
  147. def __call__(self): # sourcery skip: yield-from
  148. # yield 返回一个生成器
  149. for image in self.image_list:
  150. yield image
  151. # 比较
  152. def evaluate(self):
  153. for image in tqdm(self.image_list):
  154. self._evaluate_one(image)
  155. # 计算总体准确度
  156. @property
  157. def accuracy(self):
  158. return sum(list(self.correct.values())) / sum(list(self.correct.values()) + list(self.error.values()))
  159. # 计算元素准确度
  160. @property
  161. def attrs_accuracy(self):
  162. return {k: self.correct[k] / (self.correct[k] + self.error[k]) for k in self.field}
  163. class MD(object):
  164. def __init__(self, file_path: Path):
  165. self.name = file_path.name
  166. self.f = MdUtils(file_name=str(file_path))
  167. self.field_table: List = ['字段', '正确率']
  168. self.true_table: List = ['图片', '识别结果']
  169. self.false_table: List = ['图片', '识别结果']
  170. self.write_header(f'{self.name}测试报告')
  171. def write_header(self, title, level=1):
  172. self.f.new_header(level=level, title=title)
  173. def write_total_accuracy(self, ds: Dataset):
  174. def get_format_total_accuracy(ds: Dataset):
  175. acc = ds.accuracy * 100
  176. return "{:.2f}%".format(acc)
  177. # 1. 拿到format之后的百分数
  178. res = get_format_total_accuracy(ds)
  179. # 2. 写入
  180. self.f.new_paragraph(res)
  181. def write_table_accuracy(self, ds: Dataset, columns=2, text_align='center'):
  182. def format_table_accuracy(ds: Dataset):
  183. table = ds.attrs_accuracy
  184. for k, v in table.items():
  185. acc = v * 100
  186. table[k] = "{:.2f}%".format(acc)
  187. return table
  188. def dict_2_list(dic: dict):
  189. l = []
  190. for k, v in dic.items():
  191. l.extend((k, v))
  192. return l
  193. table_dict = format_table_accuracy(ds)
  194. table_list = dict_2_list(table_dict)
  195. self.field_table.extend(table_list)
  196. rows = len(self.field_table) // columns
  197. self.f.new_table(columns=columns, rows=rows, text=self.field_table, text_align=text_align)
  198. def write_table_result(self, ds: Dataset, columns=2, text_align='center'):
  199. for image in ds.image_list:
  200. md_image = self.f.new_inline_image(text='', path=str(image.path))
  201. if image.category:
  202. self.true_table.extend([md_image, image.ocr_result])
  203. else:
  204. self.false_table.extend([md_image, image.ocr_result])
  205. true_rows = len(self.true_table) // columns
  206. false_rows = len(self.false_table) // columns
  207. self.write_header('True')
  208. self.f.new_table(columns=columns, rows=true_rows, text=self.true_table, text_align=text_align)
  209. self.write_header('False')
  210. self.f.new_table(columns=columns, rows=false_rows, text=self.false_table, text_align=text_align)
  211. # if __name__ == '__main__':
  212. # markdown = MD('英语等级证书')
  213. #
  214. # dataset = Dataset(Path(''), 'cet', 'local', False)
  215. # print(len(dataset))
  216. # for d in dataset():
  217. # print(d)
  218. #
  219. # dataset.evaluate()
  220. # print(dataset.accuracy)
  221. #
  222. # markdown.write_total_accuracy(dataset)
  223. # markdown.write_table_accuracy(dataset)
  224. # markdown.write_table_result(dataset)
  225. #
  226. # markdown.f.create_md_file()