项目初始化
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