Compare commits
14 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
5579bdd315 | 3 weeks ago |
|
|
a8bbdffcf3 | 4 weeks ago |
|
|
dafdc5699a | 1 month ago |
|
|
1e46cecc7f | 1 month ago |
|
|
d54e27e149 | 1 month ago |
|
|
f329248bf9 | 2 months ago |
|
|
12f15d86b1 | 2 months ago |
|
|
509a6a6f40 | 2 months ago |
|
|
90a57e9d67 | 2 months ago |
|
|
39b286a44f | 2 months ago |
|
|
91e4a0f324 | 2 months ago |
|
|
fb024ced51 | 2 months ago |
|
|
f4476dd9a8 | 2 months ago |
|
|
0edaab698a | 2 months ago |
@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
@ -0,0 +1,6 @@
|
||||
projectKey=EGA
|
||||
serverUrl=http://localhost:9000
|
||||
serverVersion=25.3.0.104237
|
||||
dashboardUrl=http://localhost:9000/dashboard?id=EGA
|
||||
ceTaskId=d2b0bc8f-f9f4-469a-8e42-440fcd7f4bf5
|
||||
ceTaskUrl=http://localhost:9000/api/ce/task?id=d2b0bc8f-f9f4-469a-8e42-440fcd7f4bf5
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"Codegeex.RepoIndex": true
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 21 KiB |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -0,0 +1 @@
|
||||
Revision:6239268765d383704e5cb48b62c9ead0898ce22d,CreatedAt:unknown
|
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,111 @@
|
||||
{
|
||||
"Global": {
|
||||
"model_name": "PP-OCRv5_server_det"
|
||||
},
|
||||
"Hpi": {
|
||||
"backend_configs": {
|
||||
"paddle_infer": {
|
||||
"trt_dynamic_shapes": {
|
||||
"x": [
|
||||
[
|
||||
1,
|
||||
3,
|
||||
32,
|
||||
32
|
||||
],
|
||||
[
|
||||
1,
|
||||
3,
|
||||
736,
|
||||
736
|
||||
],
|
||||
[
|
||||
1,
|
||||
3,
|
||||
4000,
|
||||
4000
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"tensorrt": {
|
||||
"dynamic_shapes": {
|
||||
"x": [
|
||||
[
|
||||
1,
|
||||
3,
|
||||
32,
|
||||
32
|
||||
],
|
||||
[
|
||||
1,
|
||||
3,
|
||||
736,
|
||||
736
|
||||
],
|
||||
[
|
||||
1,
|
||||
3,
|
||||
4000,
|
||||
4000
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"PreProcess": {
|
||||
"transform_ops": [
|
||||
{
|
||||
"DecodeImage": {
|
||||
"channel_first": false,
|
||||
"img_mode": "BGR"
|
||||
}
|
||||
},
|
||||
{
|
||||
"DetLabelEncode": null
|
||||
},
|
||||
{
|
||||
"DetResizeForTest": {
|
||||
"resize_long": 960
|
||||
}
|
||||
},
|
||||
{
|
||||
"NormalizeImage": {
|
||||
"mean": [
|
||||
0.485,
|
||||
0.456,
|
||||
0.406
|
||||
],
|
||||
"order": "hwc",
|
||||
"scale": "1./255.",
|
||||
"std": [
|
||||
0.229,
|
||||
0.224,
|
||||
0.225
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"ToCHWImage": null
|
||||
},
|
||||
{
|
||||
"KeepKeys": {
|
||||
"keep_keys": [
|
||||
"image",
|
||||
"shape",
|
||||
"polys",
|
||||
"ignore_tags"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"PostProcess": {
|
||||
"name": "DBPostProcess",
|
||||
"thresh": 0.3,
|
||||
"box_thresh": 0.6,
|
||||
"max_candidates": 1000,
|
||||
"unclip_ratio": 1.5
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -0,0 +1,53 @@
|
||||
Global:
|
||||
model_name: PP-OCRv5_server_det
|
||||
Hpi:
|
||||
backend_configs:
|
||||
paddle_infer:
|
||||
trt_dynamic_shapes: &id001
|
||||
x:
|
||||
- - 1
|
||||
- 3
|
||||
- 32
|
||||
- 32
|
||||
- - 1
|
||||
- 3
|
||||
- 736
|
||||
- 736
|
||||
- - 1
|
||||
- 3
|
||||
- 4000
|
||||
- 4000
|
||||
tensorrt:
|
||||
dynamic_shapes: *id001
|
||||
PreProcess:
|
||||
transform_ops:
|
||||
- DecodeImage:
|
||||
channel_first: false
|
||||
img_mode: BGR
|
||||
- DetLabelEncode: null
|
||||
- DetResizeForTest:
|
||||
resize_long: 960
|
||||
- NormalizeImage:
|
||||
mean:
|
||||
- 0.485
|
||||
- 0.456
|
||||
- 0.406
|
||||
order: hwc
|
||||
scale: 1./255.
|
||||
std:
|
||||
- 0.229
|
||||
- 0.224
|
||||
- 0.225
|
||||
- ToCHWImage: null
|
||||
- KeepKeys:
|
||||
keep_keys:
|
||||
- image
|
||||
- shape
|
||||
- polys
|
||||
- ignore_tags
|
||||
PostProcess:
|
||||
name: DBPostProcess
|
||||
thresh: 0.3
|
||||
box_thresh: 0.6
|
||||
max_candidates: 1000
|
||||
unclip_ratio: 1.5
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
Revision:08795e15fc7eb7ccc79a9b8d5241f59cfa61e5de,CreatedAt:unknown
|
||||
@ -0,0 +1,169 @@
|
||||
---
|
||||
license: Apache License 2.0
|
||||
library_name: PaddleOCR
|
||||
language:
|
||||
- English
|
||||
pipeline_tag: image-to-text
|
||||
tags:
|
||||
- OCR
|
||||
- PaddlePaddle
|
||||
- PaddleOCR
|
||||
- textline_recognition
|
||||
---
|
||||
|
||||
# en_PP-OCRv5_mobile_rec
|
||||
|
||||
## Introduction
|
||||
|
||||
en_PP-OCRv5_mobile_rec is one of the PP-OCRv5_rec that are the latest generation text line recognition models developed by PaddleOCR team. It aims to efficiently and accurately support the recognition of English. The key accuracy metrics are as follow:
|
||||
|
||||
| Model | Accuracy (%) |
|
||||
|-|-|
|
||||
| en_PP-OCRv5_mobile_rec | 85.3|
|
||||
|
||||
|
||||
|
||||
**Note**: If any character (including punctuation) in a line was incorrect, the entire line was marked as wrong. This ensures higher accuracy in practical applications.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Installation
|
||||
|
||||
1. PaddlePaddle
|
||||
|
||||
Please refer to the following commands to install PaddlePaddle using pip:
|
||||
|
||||
```bash
|
||||
# for CUDA11.8
|
||||
python -m pip install paddlepaddle-gpu==3.0.0 -i https://www.paddlepaddle.org.cn/packages/stable/cu118/
|
||||
|
||||
# for CUDA12.6
|
||||
python -m pip install paddlepaddle-gpu==3.0.0 -i https://www.paddlepaddle.org.cn/packages/stable/cu126/
|
||||
|
||||
# for CPU
|
||||
python -m pip install paddlepaddle==3.0.0 -i https://www.paddlepaddle.org.cn/packages/stable/cpu/
|
||||
```
|
||||
|
||||
For details about PaddlePaddle installation, please refer to the [PaddlePaddle official website](https://www.paddlepaddle.org.cn/en/install/quick).
|
||||
|
||||
2. PaddleOCR
|
||||
|
||||
Install the latest version of the PaddleOCR inference package from PyPI:
|
||||
|
||||
```bash
|
||||
python -m pip install paddleocr
|
||||
```
|
||||
|
||||
### Model Usage
|
||||
|
||||
You can quickly experience the functionality with a single command:
|
||||
|
||||
```bash
|
||||
paddleocr text_recognition \
|
||||
--model_name en_PP-OCRv5_mobile_rec \
|
||||
-i https://cdn-uploads.huggingface.co/production/uploads/681c1ecd9539bdde5ae1733c/QmaPtftqwOgCtx0AIvU2z.png
|
||||
```
|
||||
|
||||
You can also integrate the model inference of the text recognition module into your project. Before running the following code, please download the sample image to your local machine.
|
||||
|
||||
```python
|
||||
from paddleocr import TextRecognition
|
||||
model = TextRecognition(model_name="en_PP-OCRv5_mobile_rec")
|
||||
output = model.predict(input="QmaPtftqwOgCtx0AIvU2z.png", batch_size=1)
|
||||
for res in output:
|
||||
res.print()
|
||||
res.save_to_img(save_path="./output/")
|
||||
res.save_to_json(save_path="./output/res.json")
|
||||
```
|
||||
|
||||
After running, the obtained result is as follows:
|
||||
|
||||
```json
|
||||
{'res': {'input_path': '/root/.paddlex/predict_input/QmaPtftqwOgCtx0AIvU2z.png', 'page_index': None, 'rec_text': 'the number of model parameters and FLOPs get larger, it', 'rec_score': 0.993655264377594}}
|
||||
```
|
||||
|
||||
The visualized image is as follows:
|
||||
|
||||

|
||||
|
||||
For details about usage command and descriptions of parameters, please refer to the [Document](https://paddlepaddle.github.io/PaddleOCR/latest/en/version3.x/module_usage/text_recognition.html#iii-quick-start).
|
||||
|
||||
### Pipeline Usage
|
||||
|
||||
The ability of a single model is limited. But the pipeline consists of several models can provide more capacity to resolve difficult problems in real-world scenarios.
|
||||
|
||||
#### PP-OCRv5
|
||||
|
||||
The general OCR pipeline is used to solve text recognition tasks by extracting text information from images and outputting it in string format. And there are 5 modules in the pipeline:
|
||||
* Document Image Orientation Classification Module (Optional)
|
||||
* Text Image Unwarping Module (Optional)
|
||||
* Text Line Orientation Classification Module (Optional)
|
||||
* Text Detection Module
|
||||
* Text Recognition Module
|
||||
|
||||
Run a single command to quickly experience the OCR pipeline:
|
||||
|
||||
```bash
|
||||
paddleocr ocr -i https://cdn-uploads.huggingface.co/production/uploads/681c1ecd9539bdde5ae1733c/c3hSldnYVQXp48T5V0Ze4.png \
|
||||
--text_recognition_model_name en_PP-OCRv5_mobile_rec \
|
||||
--use_doc_orientation_classify False \
|
||||
--use_doc_unwarping False \
|
||||
--use_textline_orientation True \
|
||||
--save_path ./output \
|
||||
--device gpu:0
|
||||
```
|
||||
|
||||
Results are printed to the terminal:
|
||||
|
||||
```json
|
||||
{'res': {'input_path': '/root/.paddlex/predict_input/c3hSldnYVQXp48T5V0Ze4.png', 'page_index': None, 'model_settings': {'use_doc_preprocessor': True, 'use_textline_orientation': False}, 'doc_preprocessor_res': {'input_path': None, 'page_index': None, 'model_settings': {'use_doc_orientation_classify': False, 'use_doc_unwarping': False}, 'angle': -1}, 'dt_polys': array([[[252, 172],
|
||||
...,
|
||||
[254, 241]],
|
||||
|
||||
...,
|
||||
|
||||
[[665, 566],
|
||||
...,
|
||||
[663, 601]]], dtype=int16), 'text_det_params': {'limit_side_len': 64, 'limit_type': 'min', 'thresh': 0.3, 'max_side_limit': 4000, 'box_thresh': 0.6, 'unclip_ratio': 1.5}, 'text_type': 'general', 'textline_orientation_angles': array([-1, ..., -1]), 'text_rec_score_thresh': 0.0, 'return_word_box': False, 'rec_texts': ['The moon tells the sky', 'The sky tells the sea', 'The sea tells the tide', 'And the tide tells me', 'Lemn Sissay'], 'rec_scores': array([0.98405874, ..., 0.9837752 ]), 'rec_polys': array([[[252, 172],
|
||||
...,
|
||||
[254, 241]],
|
||||
|
||||
...,
|
||||
|
||||
[[665, 566],
|
||||
...,
|
||||
[663, 601]]], dtype=int16), 'rec_boxes': array([[252, ..., 241],
|
||||
...,
|
||||
[663, ..., 612]], dtype=int16)}}
|
||||
```
|
||||
|
||||
If save_path is specified, the visualization results will be saved under `save_path`. The visualization output is shown below:
|
||||
|
||||

|
||||
|
||||
The command-line method is for quick experience. For project integration, also only a few codes are needed as well:
|
||||
|
||||
```python
|
||||
from paddleocr import PaddleOCR
|
||||
|
||||
ocr = PaddleOCR(
|
||||
text_recognition_model_name="en_PP-OCRv5_mobile_rec",
|
||||
use_doc_orientation_classify=False, # Use use_doc_orientation_classify to enable/disable document orientation classification model
|
||||
use_doc_unwarping=False, # Use use_doc_unwarping to enable/disable document unwarping module
|
||||
use_textline_orientation=True, # Use use_textline_orientation to enable/disable textline orientation classification model
|
||||
device="gpu:0", # Use device to specify GPU for model inference
|
||||
)
|
||||
result = ocr.predict("https://cdn-uploads.huggingface.co/production/uploads/681c1ecd9539bdde5ae1733c/6KQKOS42DKVEUnrticvhd.png")
|
||||
for res in result:
|
||||
res.print()
|
||||
res.save_to_img("output")
|
||||
res.save_to_json("output")
|
||||
```
|
||||
|
||||
The default model used in pipeline is `PP-OCRv5_server_rec`, so it is needed that specifing to `en_PP-OCRv5_mobile_rec` by argument `text_recognition_model_name`. And you can also use the local model file by argument `text_recognition_model_dir`. For details about usage command and descriptions of parameters, please refer to the [Document](https://paddlepaddle.github.io/PaddleOCR/latest/en/version3.x/pipeline_usage/OCR.html#2-quick-start).
|
||||
|
||||
## Links
|
||||
|
||||
[PaddleOCR Repo](https://github.com/paddlepaddle/paddleocr)
|
||||
|
||||
[PaddleOCR Documentation](https://paddlepaddle.github.io/PaddleOCR/latest/en/index.html)
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,188 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
桌面应用打包脚本
|
||||
使用PyInstaller将应用打包为可执行文件
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
def check_dependencies():
|
||||
"""检查打包依赖"""
|
||||
required_packages = ['pywebview', 'flask', 'pyinstaller']
|
||||
missing_packages = []
|
||||
|
||||
for package in required_packages:
|
||||
try:
|
||||
__import__(package)
|
||||
except ImportError:
|
||||
missing_packages.append(package)
|
||||
|
||||
if missing_packages:
|
||||
print(f"❌ 缺少必要的包: {', '.join(missing_packages)}")
|
||||
print("请运行: pip install " + " ".join(missing_packages))
|
||||
return False
|
||||
|
||||
print("✓ 所有依赖包已安装")
|
||||
return True
|
||||
|
||||
def create_spec_file():
|
||||
"""创建PyInstaller spec文件"""
|
||||
spec_content = """
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到Python路径
|
||||
project_root = Path(__file__).parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
block_cipher = None
|
||||
|
||||
a = Analysis(
|
||||
['desktop_app.py'],
|
||||
pathex=[str(project_root)],
|
||||
binaries=[],
|
||||
datas=[
|
||||
('templates/*', 'templates'),
|
||||
('static/*', 'static'),
|
||||
('config.env', '.'),
|
||||
('requirements.txt', '.'),
|
||||
],
|
||||
hiddenimports=[
|
||||
'flask',
|
||||
'webview',
|
||||
'json_dao',
|
||||
'ai_service',
|
||||
'ocr_service',
|
||||
'auth_utils',
|
||||
'performance',
|
||||
'scoring_service',
|
||||
'article_scoring_standards',
|
||||
],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='AI写作辅导软件',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=False, # 设置为True可显示控制台窗口
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon='static/favicon.ico' if os.path.exists('static/favicon.ico') else None,
|
||||
)
|
||||
"""
|
||||
|
||||
spec_path = Path('desktop_app.spec')
|
||||
with open(spec_path, 'w', encoding='utf-8') as f:
|
||||
f.write(spec_content)
|
||||
|
||||
return spec_path
|
||||
|
||||
def build_application():
|
||||
"""打包应用"""
|
||||
try:
|
||||
print("🚀 开始打包应用...")
|
||||
|
||||
# 创建spec文件
|
||||
spec_file = create_spec_file()
|
||||
print("✓ Spec文件创建完成")
|
||||
|
||||
# 运行PyInstaller
|
||||
cmd = [sys.executable, '-m', 'PyInstaller', '--clean', str(spec_file)]
|
||||
|
||||
print("📦 正在打包,这可能需要几分钟...")
|
||||
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
print("✅ 应用打包成功!")
|
||||
|
||||
# 显示输出目录
|
||||
dist_dir = Path('dist')
|
||||
if dist_dir.exists():
|
||||
exe_files = list(dist_dir.glob('*.exe'))
|
||||
if exe_files:
|
||||
print(f"📁 可执行文件位置: {exe_files[0]}")
|
||||
|
||||
return True
|
||||
else:
|
||||
print(f"❌ 打包失败: {result.stderr}")
|
||||
return False
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"❌ 打包过程出错: {e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ 打包失败: {e}")
|
||||
return False
|
||||
|
||||
def cleanup():
|
||||
"""清理临时文件"""
|
||||
temp_dirs = ['build', 'dist', '__pycache__']
|
||||
temp_files = ['desktop_app.spec']
|
||||
|
||||
for temp_dir in temp_dirs:
|
||||
if Path(temp_dir).exists():
|
||||
shutil.rmtree(temp_dir)
|
||||
print(f"🧹 清理目录: {temp_dir}")
|
||||
|
||||
for temp_file in temp_files:
|
||||
if Path(temp_file).exists():
|
||||
Path(temp_file).unlink()
|
||||
print(f"🧹 清理文件: {temp_file}")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("=" * 60)
|
||||
print("AI智能写作辅导软件 - 桌面应用打包工具")
|
||||
print("=" * 60)
|
||||
|
||||
# 检查依赖
|
||||
if not check_dependencies():
|
||||
return
|
||||
|
||||
# 清理之前的构建
|
||||
cleanup()
|
||||
|
||||
# 打包应用
|
||||
if build_application():
|
||||
print("\n🎉 打包完成!")
|
||||
print("\n📋 使用说明:")
|
||||
print("1. 在 'dist' 目录中找到可执行文件")
|
||||
print("2. 可以直接运行或分发给其他用户")
|
||||
print("3. 首次启动可能需要几秒钟初始化时间")
|
||||
else:
|
||||
print("\n💥 打包失败,请检查错误信息")
|
||||
|
||||
# 询问是否清理
|
||||
if input("\n是否清理临时文件? (y/n): ").lower() == 'y':
|
||||
cleanup()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -0,0 +1,61 @@
|
||||
"""
|
||||
桌面应用配置文件
|
||||
"""
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# 应用基本信息
|
||||
APP_NAME = "EGA"
|
||||
APP_VERSION = "2.0.0"
|
||||
APP_DESCRIPTION = "AI智能写作辅导软件 - 桌面版"
|
||||
|
||||
# 窗口配置
|
||||
WINDOW_CONFIG = {
|
||||
'title': APP_NAME, # 统一使用APP_NAME
|
||||
'width': 1800,
|
||||
'height': 1200,
|
||||
'min_width': 1000,
|
||||
'min_height': 700,
|
||||
'resizable': True,
|
||||
'fullscreen': False,
|
||||
'frameless': False,
|
||||
'easy_drag': True,
|
||||
'on_top': False,
|
||||
'confirm_close': True,
|
||||
'hidpi': True,
|
||||
'high_dpi': True,
|
||||
'text_scale': 1.2 # 文本缩放比例
|
||||
}
|
||||
|
||||
# 服务器配置
|
||||
SERVER_CONFIG = {
|
||||
'host': '127.0.0.1',
|
||||
'port': 8080,
|
||||
'debug': False
|
||||
}
|
||||
|
||||
# 路径配置
|
||||
BASE_DIR = Path(__file__).parent
|
||||
DATA_DIR = BASE_DIR / 'data'
|
||||
LOG_DIR = BASE_DIR / 'logs'
|
||||
TEMP_DIR = BASE_DIR / 'temp'
|
||||
|
||||
# 创建必要目录
|
||||
for directory in [DATA_DIR, LOG_DIR, TEMP_DIR]:
|
||||
directory.mkdir(exist_ok=True)
|
||||
|
||||
# 特殊功能配置
|
||||
FEATURE_FLAGS = {
|
||||
'enable_tray_icon': True,
|
||||
'enable_system_notifications': True,
|
||||
'enable_auto_update': False,
|
||||
'enable_hot_reload': False
|
||||
}
|
||||
|
||||
# 主题配置
|
||||
THEME_CONFIG = {
|
||||
'dark_mode': False,
|
||||
'accent_color': '#2196F3',
|
||||
'background_color': '#FFFFFF',
|
||||
'font_family': 'Microsoft YaHei, Arial, sans-serif'
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
# diagnostic.py
|
||||
import sys
|
||||
import socket
|
||||
import psutil
|
||||
from pathlib import Path
|
||||
|
||||
def run_diagnostics():
|
||||
print("=== 应用诊断信息 ===")
|
||||
|
||||
# 检查端口占用
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
sock.bind(('127.0.0.1', 8080))
|
||||
print("✓ 端口8080可用")
|
||||
sock.close()
|
||||
except OSError:
|
||||
print("❌ 端口8080被占用")
|
||||
|
||||
# 检查必要目录
|
||||
required_dirs = ['data', 'logs', 'temp']
|
||||
for dir_name in required_dirs:
|
||||
dir_path = Path(__file__).parent / dir_name
|
||||
if dir_path.exists():
|
||||
print(f"✓ 目录 {dir_name} 存在")
|
||||
else:
|
||||
print(f"❌ 目录 {dir_name} 缺失")
|
||||
|
||||
# 检查Python包
|
||||
required_packages = ['webview', 'flask', 'pillow']
|
||||
for package in required_packages:
|
||||
try:
|
||||
__import__(package)
|
||||
print(f"✓ 包 {package} 已安装")
|
||||
except ImportError:
|
||||
print(f"❌ 包 {package} 未安装")
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_diagnostics()
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,122 @@
|
||||
"""
|
||||
OCR相关路由
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
from flask import Blueprint, request, jsonify, current_app
|
||||
from werkzeug.utils import secure_filename
|
||||
import base64
|
||||
|
||||
from ocr_service import ocr_service
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 创建OCR蓝图
|
||||
ocr_bp = Blueprint('ocr', __name__)
|
||||
|
||||
def allowed_file(filename):
|
||||
"""检查文件类型是否允许"""
|
||||
allowed_extensions = {'png', 'jpg', 'jpeg', 'gif', 'bmp', 'tiff'}
|
||||
return '.' in filename and \
|
||||
filename.rsplit('.', 1)[1].lower() in allowed_extensions
|
||||
|
||||
@ocr_bp.route('/api/ocr/recognize', methods=['POST'])
|
||||
def recognize_text():
|
||||
"""
|
||||
图片文字识别接口
|
||||
支持文件上传和base64数据
|
||||
"""
|
||||
print("收到OCR识别请求")
|
||||
try:
|
||||
# 检查是否有文件上传
|
||||
if 'image' in request.files:
|
||||
file = request.files['image']
|
||||
if file.filename == '':
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": "未选择文件",
|
||||
"text": ""
|
||||
}), 400
|
||||
print("处理上传的文件")
|
||||
if file and allowed_file(file.filename):
|
||||
# 读取文件数据
|
||||
image_data = file.read()
|
||||
result = ocr_service.recognize_text_from_bytes(image_data)
|
||||
print("OCR识别完成,from file upload")
|
||||
return jsonify(result)
|
||||
else:
|
||||
print("不支持的文件格式")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": "不支持的文件格式",
|
||||
"text": ""
|
||||
}), 400
|
||||
|
||||
# 检查是否有base64数据
|
||||
elif 'image_data' in request.json:
|
||||
image_data_base64 = request.json['image_data']
|
||||
try:
|
||||
# 解码base64数据
|
||||
if ',' in image_data_base64:
|
||||
# 处理data URL格式
|
||||
image_data_base64 = image_data_base64.split(',')[1]
|
||||
|
||||
image_data = base64.b64decode(image_data_base64)
|
||||
result = ocr_service.recognize_text_from_bytes(image_data)
|
||||
print("OCR识别完成,from base64 data")
|
||||
return jsonify(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Base64解码失败: {e}")
|
||||
print("Base64解码失败")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": "图片数据格式错误",
|
||||
"text": ""
|
||||
}), 400
|
||||
|
||||
else:
|
||||
print("未提供图片文件或base64数据")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": "请提供图片文件或base64数据",
|
||||
"text": ""
|
||||
}), 400
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"OCR识别接口错误: {e}")
|
||||
print("OCR识别接口发生错误")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": f"服务器错误: {str(e)}",
|
||||
"text": ""
|
||||
}), 500
|
||||
|
||||
@ocr_bp.route('/api/ocr/status', methods=['GET'])
|
||||
def get_ocr_status():
|
||||
"""获取OCR服务状态"""
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"initialized": ocr_service.is_initialized,
|
||||
"status": "ready" if ocr_service.is_initialized else "not_initialized"
|
||||
})
|
||||
|
||||
@ocr_bp.route('/api/ocr/initialize', methods=['POST'])
|
||||
def initialize_ocr():
|
||||
"""手动初始化OCR服务"""
|
||||
print("收到OCR初始化请求")
|
||||
try:
|
||||
success = ocr_service.initialize_ocr()
|
||||
if success:
|
||||
print("OCR服务初始化成功")
|
||||
else:
|
||||
print("OCR服务初始化失败")
|
||||
return jsonify({
|
||||
"success": success,
|
||||
"message": "OCR初始化成功" if success else "OCR初始化失败"
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": f"初始化失败: {str(e)}"
|
||||
}), 500
|
||||
@ -0,0 +1,108 @@
|
||||
{
|
||||
"vocabulary": [
|
||||
{
|
||||
"word": "perseverance",
|
||||
"meaning": "坚持不懈,毅力",
|
||||
"example": "Through perseverance, she overcame all obstacles and achieved her dream.",
|
||||
"category": "品质"
|
||||
},
|
||||
{
|
||||
"word": "eloquent",
|
||||
"meaning": "雄辩的,有说服力的",
|
||||
"example": "His eloquent speech moved the entire audience to tears.",
|
||||
"category": "表达"
|
||||
},
|
||||
{
|
||||
"word": "resilient",
|
||||
"meaning": "有弹性的,适应力强的",
|
||||
"example": "Children are remarkably resilient and can adapt to new environments quickly.",
|
||||
"category": "品质"
|
||||
},
|
||||
{
|
||||
"word": "conscientious",
|
||||
"meaning": "认真的,自觉的",
|
||||
"example": "She is a conscientious worker who always completes her tasks with great care and attention to detail.",
|
||||
"category": "品质"
|
||||
},
|
||||
{
|
||||
"word": "amicable",
|
||||
"meaning": "友善的,心平气和的",
|
||||
"example": "They reached an amicable agreement after a friendly discussion.",
|
||||
"category": "品质"
|
||||
},
|
||||
{
|
||||
"word": "meticulous",
|
||||
"meaning": "一丝不苟的,极其谨慎的",
|
||||
"example": "His meticulous approach to research ensured the accuracy of all his findings.",
|
||||
"category": "品质"
|
||||
},
|
||||
{
|
||||
"word": "superior",
|
||||
"meaning": "优质的,上等的",
|
||||
"example": "Our products are of superior quality compared to others in the market. [1,2](@ref)",
|
||||
"category": "质量"
|
||||
},
|
||||
{
|
||||
"word": "durable",
|
||||
"meaning": "耐用的,持久的",
|
||||
"example": "Customers like the goods durable in use because they last for a long time. [5](@ref)",
|
||||
"category": "质量"
|
||||
}
|
||||
],
|
||||
"expressions": [
|
||||
{
|
||||
"phrase": "A blessing in disguise",
|
||||
"meaning": "因祸得福",
|
||||
"example": "Losing that job was a blessing in disguise because it led me to a better opportunity.",
|
||||
"category": "谚语"
|
||||
},
|
||||
{
|
||||
"phrase": "Food for thought",
|
||||
"meaning": "发人深省的东西",
|
||||
"example": "The professor's lecture provided plenty of food for thought about climate change.",
|
||||
"category": "习语"
|
||||
},
|
||||
{
|
||||
"phrase": "Have a good one",
|
||||
"meaning": "祝你好,再见(地道告别语)",
|
||||
"example": "As I left the store, the cashier smiled and said, 'Have a good one!'",
|
||||
"category": "日常表达"
|
||||
},
|
||||
{
|
||||
"phrase": "I'm good",
|
||||
"meaning": "不用了,我很好(委婉拒绝或表示满足)",
|
||||
"example": "'Would you like more coffee?' 'No, I'm good. Thanks.'",
|
||||
"category": "日常表达"
|
||||
},
|
||||
{
|
||||
"phrase": "A touch of",
|
||||
"meaning": "一点儿,略有",
|
||||
"example": "I have a touch of flu and need some rest this weekend.",
|
||||
"category": "习语"
|
||||
},
|
||||
{
|
||||
"phrase": "Off the hook",
|
||||
"meaning": "摆脱麻烦,脱身",
|
||||
"example": "After he paid all the fines, he was finally off the hook.",
|
||||
"category": "习语"
|
||||
},
|
||||
{
|
||||
"phrase": "Hands down",
|
||||
"meaning": "毫无疑问,绝对",
|
||||
"example": "This is hands down the best movie I've seen this year.",
|
||||
"category": "习语"
|
||||
}
|
||||
],
|
||||
"writing_tips": [
|
||||
"使用具体的例子来支持你的观点",
|
||||
"尝试使用比喻和类比让文章更生动",
|
||||
"注意段落之间的过渡要自然流畅",
|
||||
"使用多样化的句式结构避免单调",
|
||||
"开头要吸引读者,结尾要给人留下深刻印象",
|
||||
"在邮件开头使用礼貌用语,如'I hope this e-mail finds you well'",
|
||||
"使用状语前置来提升表达层次,如'In God we trust'",
|
||||
"在正式请求中使用委婉表达,如'It would be great if you could...'",
|
||||
"学会使用模糊语气词如-ish, or so, something等使表达更自然",
|
||||
"使用'appreciate it'代替'thank you'表达更深刻的感激之情"
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 316 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue