项目初始化
This commit is contained in:
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
8
.idea/easyocr-api.iml
generated
Normal file
8
.idea/easyocr-api.iml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
41
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
41
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<Languages>
|
||||||
|
<language minSize="130" name="Python" />
|
||||||
|
</Languages>
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredPackages">
|
||||||
|
<value>
|
||||||
|
<list size="8">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="protobuf" />
|
||||||
|
<item index="1" class="java.lang.String" itemvalue="mkl-random" />
|
||||||
|
<item index="2" class="java.lang.String" itemvalue="numpy" />
|
||||||
|
<item index="3" class="java.lang.String" itemvalue="starlette" />
|
||||||
|
<item index="4" class="java.lang.String" itemvalue="charset-normalizer" />
|
||||||
|
<item index="5" class="java.lang.String" itemvalue="h11" />
|
||||||
|
<item index="6" class="java.lang.String" itemvalue="obs" />
|
||||||
|
<item index="7" class="java.lang.String" itemvalue="torch" />
|
||||||
|
</list>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredErrors">
|
||||||
|
<list>
|
||||||
|
<option value="W292" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredErrors">
|
||||||
|
<list>
|
||||||
|
<option value="N803" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
4
.idea/misc.xml
generated
Normal file
4
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (easyocr)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/easyocr-api.iml" filepath="$PROJECT_DIR$/.idea/easyocr-api.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
0
api/__init__.py
Normal file
0
api/__init__.py
Normal file
61
api/easy_orc_api.py
Normal file
61
api/easy_orc_api.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import os
|
||||||
|
import cv2
|
||||||
|
import easyocr
|
||||||
|
import numpy as np
|
||||||
|
from core.response import SuccessResponse
|
||||||
|
from fastapi import APIRouter, UploadFile, Form
|
||||||
|
from schemas.orc_result import ResultInfo, ResultMain
|
||||||
|
|
||||||
|
|
||||||
|
app = APIRouter()
|
||||||
|
|
||||||
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
model_dir = os.path.join(BASE_DIR, 'model')
|
||||||
|
|
||||||
|
reader = easyocr.Reader(
|
||||||
|
['ch_sim', 'en'],
|
||||||
|
model_storage_directory=model_dir,
|
||||||
|
download_enabled=False,
|
||||||
|
gpu=True)
|
||||||
|
|
||||||
|
|
||||||
|
def preprocess_img(image):
|
||||||
|
# 转为灰度图
|
||||||
|
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
||||||
|
# 二值化
|
||||||
|
_, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
|
||||||
|
# 去噪声
|
||||||
|
denoised = cv2.medianBlur(binary, 3)
|
||||||
|
return denoised
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/upload")
|
||||||
|
async def orc(files: list[UploadFile] = Form(...)):
|
||||||
|
result = []
|
||||||
|
for file in files:
|
||||||
|
main = ResultMain()
|
||||||
|
main.file_name = file.filename
|
||||||
|
# 读取上传的文件内容
|
||||||
|
image_data = await file.read()
|
||||||
|
|
||||||
|
# 2. 将字节数据转换为 numpy 数组
|
||||||
|
np_array = np.frombuffer(image_data, np.uint8)
|
||||||
|
|
||||||
|
# 3. 使用 cv2.imdecode 解码为 OpenCV 图像
|
||||||
|
image = cv2.imdecode(np_array, cv2.IMREAD_COLOR)
|
||||||
|
new_image = preprocess_img(image)
|
||||||
|
datas = reader.readtext(np.array(new_image))
|
||||||
|
infos = []
|
||||||
|
for data in datas:
|
||||||
|
info = ResultInfo()
|
||||||
|
bounding_boxs = data[0]
|
||||||
|
left_up = bounding_boxs[0]
|
||||||
|
right_down = bounding_boxs[2]
|
||||||
|
info.bounding_box_left_up = [int(left_up[0]), int(left_up[1])]
|
||||||
|
info.bounding_box_right_down = [int(right_down[0]), int(right_down[1])]
|
||||||
|
info.text = data[1]
|
||||||
|
info.confidence = round(data[2], 4)
|
||||||
|
infos.append(info)
|
||||||
|
main.infos = infos
|
||||||
|
result.append(main.model_dump())
|
||||||
|
return SuccessResponse(data=result)
|
55
api/paddle_ocr_api.py
Normal file
55
api/paddle_ocr_api.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
from core.response import SuccessResponse
|
||||||
|
from fastapi import APIRouter, UploadFile, Form
|
||||||
|
from schemas.orc_result import ResultInfo, ResultMain
|
||||||
|
|
||||||
|
from paddleocr import PaddleOCR
|
||||||
|
import numpy as np
|
||||||
|
import cv2
|
||||||
|
|
||||||
|
|
||||||
|
app = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
paddle_ocr = PaddleOCR(lang='ch')
|
||||||
|
|
||||||
|
|
||||||
|
def enhance_image(img):
|
||||||
|
"""增强亮部和暗部,提升数字清晰度"""
|
||||||
|
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
|
||||||
|
l, a, b = cv2.split(lab)
|
||||||
|
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
|
||||||
|
cl = clahe.apply(l)
|
||||||
|
enhanced = cv2.merge((cl, a, b))
|
||||||
|
return cv2.cvtColor(enhanced, cv2.COLOR_LAB2BGR)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/upload")
|
||||||
|
async def orc(files: list[UploadFile] = Form(...)):
|
||||||
|
result = []
|
||||||
|
for file in files:
|
||||||
|
main = ResultMain()
|
||||||
|
main.file_name = file.filename
|
||||||
|
# 读取上传的文件内容
|
||||||
|
image_data = await file.read()
|
||||||
|
# 从字节数据读取图像
|
||||||
|
np_array = np.frombuffer(image_data, np.uint8)
|
||||||
|
img = cv2.imdecode(np_array, cv2.IMREAD_COLOR)
|
||||||
|
# 图像增强
|
||||||
|
img = enhance_image(img)
|
||||||
|
# 4. 调用 OCR 模型进行识别
|
||||||
|
datas = paddle_ocr.ocr(img, cls=False)
|
||||||
|
infos = []
|
||||||
|
if datas:
|
||||||
|
for data in datas[0]:
|
||||||
|
info = ResultInfo()
|
||||||
|
bounding_boxs = data[0]
|
||||||
|
left_up = bounding_boxs[0]
|
||||||
|
right_down = bounding_boxs[2]
|
||||||
|
info.bounding_box_left_up = [int(left_up[0]), int(left_up[1])]
|
||||||
|
info.bounding_box_right_down = [int(right_down[0]), int(right_down[1])]
|
||||||
|
info.text = data[1][0]
|
||||||
|
info.confidence = round(data[1][1], 4)
|
||||||
|
infos.append(info)
|
||||||
|
main.infos = infos
|
||||||
|
result.append(main.model_dump())
|
||||||
|
return SuccessResponse(data=result)
|
24
application.py
Normal file
24
application.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
|
from api.easy_orc_api import app as easy_orc_api
|
||||||
|
|
||||||
|
from api.paddle_ocr_api import app as paddle_ocr_api
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
添加cros中间件,允许跨域请求
|
||||||
|
'''
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
app.include_router(paddle_ocr_api, prefix="/ocr", tags=["ocr识别"])
|
32
core/response.py
Normal file
32
core/response.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# 依赖安装:pip install orjson
|
||||||
|
from fastapi.responses import ORJSONResponse as Response
|
||||||
|
from fastapi import status as http_status
|
||||||
|
from core import status as http
|
||||||
|
|
||||||
|
|
||||||
|
class SuccessResponse(Response):
|
||||||
|
"""
|
||||||
|
成功响应
|
||||||
|
"""
|
||||||
|
def __init__(self, data=None, msg="success", code=http.HTTP_SUCCESS, status=http_status.HTTP_200_OK, **kwargs):
|
||||||
|
self.data = {
|
||||||
|
"code": code,
|
||||||
|
"message": msg,
|
||||||
|
"data": data
|
||||||
|
}
|
||||||
|
self.data.update(kwargs)
|
||||||
|
super().__init__(content=self.data, status_code=status)
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorResponse(Response):
|
||||||
|
"""
|
||||||
|
失败响应
|
||||||
|
"""
|
||||||
|
def __init__(self, msg=None, code=http.HTTP_ERROR, status=http_status.HTTP_200_OK, **kwargs):
|
||||||
|
self.data = {
|
||||||
|
"code": code,
|
||||||
|
"message": msg,
|
||||||
|
"data": []
|
||||||
|
}
|
||||||
|
self.data.update(kwargs)
|
||||||
|
super().__init__(content=self.data, status_code=status)
|
14
core/status.py
Normal file
14
core/status.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# @version : 1.0
|
||||||
|
# @Create Time : 2022/8/10 22:20
|
||||||
|
# @File : status.py
|
||||||
|
# @IDE : PyCharm
|
||||||
|
# @desc : 简要说明
|
||||||
|
|
||||||
|
|
||||||
|
HTTP_SUCCESS = 200
|
||||||
|
HTTP_ERROR = 400
|
||||||
|
HTTP_401_UNAUTHORIZED = 401
|
||||||
|
HTTP_403_FORBIDDEN = 403
|
||||||
|
HTTP_404_NOT_FOUND = 404
|
5
main.py
Normal file
5
main.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import uvicorn
|
||||||
|
from application import app
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
uvicorn.run("main:app", port=9800, reload=False, host='0.0.0.0')
|
BIN
model/zh_sim_g2.pth
Normal file
BIN
model/zh_sim_g2.pth
Normal file
Binary file not shown.
6
requirement.txt
Normal file
6
requirement.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
fastapi==0.112.2
|
||||||
|
uvicorn==0.32.1
|
||||||
|
easyocr==1.7.2
|
||||||
|
orjson==3.10.16
|
||||||
|
python-multipart==0.0.20
|
||||||
|
paddleocr==2.10.0
|
18
schemas/orc_result.py
Normal file
18
schemas/orc_result.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
from pydantic import BaseModel, ConfigDict, field_validator
|
||||||
|
|
||||||
|
|
||||||
|
class ResultInfo(BaseModel):
|
||||||
|
bounding_box_left_up: list[int] | None = []
|
||||||
|
bounding_box_right_down: list[int] | None = []
|
||||||
|
text: str | None = None
|
||||||
|
confidence: float | None = None
|
||||||
|
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
|
||||||
|
class ResultMain(BaseModel):
|
||||||
|
file_name: str | None = None
|
||||||
|
infos: list[ResultInfo] | None = []
|
||||||
|
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
17
utils/opencv_util.py
Normal file
17
utils/opencv_util.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# 增强对比度 + 灰度转换 + 自适应阈值
|
||||||
|
import cv2
|
||||||
|
|
||||||
|
img = cv2.imread("fb1df265-5085-4bc2-881b-7674b620c4ac.jpg")
|
||||||
|
|
||||||
|
# 转灰度图
|
||||||
|
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||||
|
|
||||||
|
# 提升对比度
|
||||||
|
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
|
||||||
|
enhanced = clahe.apply(gray)
|
||||||
|
|
||||||
|
# 二值化处理
|
||||||
|
binary = cv2.adaptiveThreshold(enhanced, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
|
||||||
|
cv2.THRESH_BINARY_INV, 11, 2)
|
||||||
|
|
||||||
|
cv2.imwrite("processed.png", binary)
|
39
utils/os_utils.py
Normal file
39
utils/os_utils.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import os
|
||||||
|
from fastapi import UploadFile
|
||||||
|
|
||||||
|
|
||||||
|
def file_path(*path):
|
||||||
|
"""
|
||||||
|
拼接返回文件路径
|
||||||
|
:param path:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return_path = os.path.join(*path)
|
||||||
|
return return_path
|
||||||
|
|
||||||
|
|
||||||
|
def create_folder(*path):
|
||||||
|
"""根据路径创建文件夹"""
|
||||||
|
folder_path = os.path.join(*path)
|
||||||
|
try:
|
||||||
|
os.makedirs(folder_path, exist_ok=True)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"创建文件夹时错误: {e}")
|
||||||
|
return folder_path
|
||||||
|
|
||||||
|
|
||||||
|
def save_images(*path, file: UploadFile):
|
||||||
|
"""
|
||||||
|
保存上传的图片
|
||||||
|
:param path: 路径
|
||||||
|
:param file: 文件
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
save_path = os.path.join(*path, file.filename)
|
||||||
|
|
||||||
|
os.makedirs(os.path.dirname(save_path), exist_ok=True)
|
||||||
|
with open(save_path, "wb") as f:
|
||||||
|
for line in file.file:
|
||||||
|
f.write(line)
|
||||||
|
return save_path
|
||||||
|
|
11
utils/pillow_util.py
Normal file
11
utils/pillow_util.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from PIL import Image, ImageEnhance
|
||||||
|
|
||||||
|
# 加载图片
|
||||||
|
img = Image.open("fb1df265-5085-4bc2-881b-7674b620c4ac.jpg")
|
||||||
|
|
||||||
|
# 增强对比度(1.5 倍为示例,你可以调节参数)
|
||||||
|
enhancer = ImageEnhance.Contrast(img)
|
||||||
|
img_enhanced = enhancer.enhance(4.0)
|
||||||
|
|
||||||
|
# 保存增强后的图像
|
||||||
|
img_enhanced.save("enhanced_image_pillow.png")
|
Reference in New Issue
Block a user