Browse Source

Update project dependencies and enhance OCR functionality with PaddleOCR integration. Add image preprocessing utilities for improved recognition accuracy. Expand .gitignore for better environment management and update README with new features and setup instructions.

liuyuqi-cnb 2 weeks ago
parent
commit
fc1bbf29df
9 changed files with 2495 additions and 921 deletions
  1. 73 2
      .gitignore
  2. 200 12
      README.md
  3. 1875 880
      poetry.lock
  4. 5 3
      pyproject.toml
  5. 6 5
      requirements.txt
  6. 3 6
      routes.py
  7. 47 0
      test_id_card_ocr.py
  8. 209 0
      utils/image_preprocess.py
  9. 77 13
      utils/ocr_utils.py

+ 73 - 2
.gitignore

@@ -1,2 +1,73 @@
-*.pyc
-instance/database.db
+# Python
+__pycache__/
+*.py[cod]
+*$py.class
+*.so
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# Virtual Environment
+venv/
+ENV/
+env/
+
+# IDE
+.vscode/
+.idea/
+*.swp
+*.swo
+*~
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Logs
+*.log
+logs/
+
+# Database
+*.db
+*.sqlite
+*.sqlite3
+
+# Cache
+.cache/
+.pytest_cache/
+.mypy_cache/
+.dmypy.json
+
+# Environment variables
+.env
+.env.local
+
+# Temporary files
+/tmp/
+*.tmp
+processed.jpg
+enhanced.jpg
+resized.jpg
+
+# Test images (可能包含隐私信息)
+test_images/
+
+# Model cache
+.paddleocr/
+~/.paddleocr/
+
+# Hugging Face cache
+.cache/

+ 200 - 12
README.md

@@ -1,30 +1,218 @@
 # campus_activity_system
 # campus_activity_system
 
 
-nlp 校园活动信息进行智能分析处理系统,主要上传身份证图片,提取姓名和身份证号,然后进行活动类型分类,并发布活动信息
+NLP 校园活动信息智能分析处理系统,主要功能包括上传身份证图片、提取姓名和身份证号、活动类型分类和活动信息发布
 
 
-[x]学生信息管理
-[]社团负责人管理
-[]活动信息发布
-[x]活动类型分类
+## 功能模块
 
 
-依赖 transformers , pytesseract ocr识别
+- [x] 学生信息管理
+- [x] 身份证 OCR 识别(使用 PaddleOCR)
+- [ ] 社团负责人管理
+- [ ] 活动信息发布
+- [x] 活动类型分类(基于 NLP)
 
 
-缓存目录
+## 技术栈
 
 
+### 后端
+- **框架**: Flask + SQLAlchemy
+- **数据库**: MySQL 5.7
+- **缓存**: Redis
+- **OCR**: PaddleOCR(中文身份证识别)
+- **NLP**: Transformers (BERT)
+
+### 核心依赖
+- `paddleocr`: 高精度中文 OCR 识别引擎
+- `transformers`: 用于活动类型分类的 NLP 模型
+- `opencv-python`: 图像处理
+- `Flask`: Web 框架
+- `SQLAlchemy`: ORM
+
+## 快速开始
+
+### 1. 环境准备
+
+```bash
+# 加载环境变量
+source /etc/profile
+
+# 确认数据库和 Redis 已启动
+# MySQL: localhost:3306 (用户: lyq, 密码: 123456)
+# Redis: localhost:6379
+```
+
+### 2. 安装依赖
+
+```bash
+pip install -r requirements.txt
+```
+
+### 3. 初始化数据库
+
+```bash
+python init_db.py
+```
+
+### 4. 启动服务
+
+```bash
+python app.py
+```
+
+服务将在 `http://localhost:5000` 启动。
+
+## OCR 识别说明
+
+本项目使用 **PaddleOCR** 进行身份证识别,相比 Tesseract 有以下优势:
+
+### 优势
+- ✓ 更高的中文识别准确率
+- ✓ 无需系统级依赖安装
+- ✓ 专门优化的证件识别
+- ✓ 自动角度纠正
+
+### 使用方法
+
+```python
+from utils.ocr_utils import extract_id_card_info
+
+# 识别身份证
+id_info = extract_id_card_info('path/to/id_card.jpg')
+print(f"姓名: {id_info['name']}")
+print(f"身份证号: {id_info['id_number']}")
+```
+
+### 测试 OCR
+
+```bash
+# 测试身份证识别
+python test_id_card_ocr.py test_images/id_card.jpg
+
+# 测试图像预处理
+python utils/image_preprocess.py test_images/id_card.jpg
+```
+
+详细的 OCR 配置说明请参考:[docs/PADDLEOCR_SETUP.md](docs/PADDLEOCR_SETUP.md)
+
+## API 接口
+
+### 用户注册(含身份证识别)
+
+```bash
+POST /register
+Content-Type: multipart/form-data
+
+参数:
+- username: 用户名
+- email: 邮箱
+- image: 身份证图片文件
+
+返回:
+{
+  "message": "用户注册成功",
+  "user": {
+    "id": 1,
+    "username": "zhangsan",
+    "email": "zhangsan@example.com",
+    "name": "张三",
+    "id_number": "110101199001011234"
+  }
+}
+```
+
+### 活动类型分类
+
+```bash
+POST /classify
+Content-Type: application/json
+
+{
+  "description": "人工智能与机器学习讲座"
+}
+
+返回:
+{
+  "activity_type": "学术讲座"
+}
+```
+
+## 项目结构
+
+```
+campus_activity_system/
+├── app.py                      # 主应用入口
+├── routes.py                   # 路由定义
+├── models.py                   # 数据模型
+├── utils/
+│   ├── ocr_utils.py           # OCR 识别工具(PaddleOCR)
+│   ├── nlp_utils.py           # NLP 分类工具
+│   └── image_preprocess.py    # 图像预处理工具
+├── docs/
+│   └── PADDLEOCR_SETUP.md     # PaddleOCR 配置文档
+├── test_id_card_ocr.py        # OCR 测试脚本
+├── requirements.txt            # Python 依赖
+├── CLAUDE.md                   # 项目规范
+└── README.md                   # 本文档
+```
+
+## 模型缓存
+
+### Transformers 模型
 ```
 ```
 ~/.cache/huggingface/hub/models--dslim--bert-base-NER
 ~/.cache/huggingface/hub/models--dslim--bert-base-NER
+```
+
+### PaddleOCR 模型
+首次运行会自动下载模型文件(约 50MB)
+
+## 数据库配置
+
+```python
+# MySQL 配置
+SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://lyq:123456@localhost:3306/campus_activity'
+
+# Redis 配置
+REDIS_HOST = 'localhost'
+REDIS_PORT = 6379
+```
+
+## 常见问题
 
 
+### Q: OCR 识别准确率不高?
+**A**: 请确保:
+1. 图片清晰,文字可辨识
+2. 光线均匀,避免反光
+3. 尽量正面拍摄
+4. 建议宽度 1000-2000px
 
 
-apt-get update
-apt-get install tesseract-ocr
-apt-get install tesseract-ocr-chi-sim
+可以使用图像预处理工具提高识别率:
+```bash
+python utils/image_preprocess.py your_id_card.jpg
+```
 
 
+### Q: 如何启用 GPU 加速?
+**A**: 
+```bash
+pip uninstall paddlepaddle
+pip install paddlepaddle-gpu
 ```
 ```
 
 
+然后在 `utils/ocr_utils.py` 中修改:
+```python
+ocr = PaddleOCR(use_angle_cls=True, lang='ch', use_gpu=True)
+```
+
+### Q: 模型下载慢?
+**A**: 首次运行会自动下载模型,如下载慢可使用国内镜像或手动下载。
+
+## 开发指南
+
+详细的开发规范和技术选型请参考:[CLAUDE.md](CLAUDE.md)
 
 
-## Reference
+## 参考资料
 
 
-- [huggingface/dslim/bert-base-NER](https://huggingface.co/dslim/bert-base-NER)
+- [PaddleOCR 官方文档](https://github.com/PaddlePaddle/PaddleOCR)
+- [Hugging Face - dslim/bert-base-NER](https://huggingface.co/dslim/bert-base-NER)
+- [Flask 文档](https://flask.palletsprojects.com/)
+- [SQLAlchemy 文档](https://www.sqlalchemy.org/)
 
 
 ## License
 ## License
 
 

File diff suppressed because it is too large
+ 1875 - 880
poetry.lock


+ 5 - 3
pyproject.toml

@@ -10,12 +10,14 @@ python = "^3.12"
 flask = "^3.1.0"
 flask = "^3.1.0"
 sqlalchemy = "^2.0.38"
 sqlalchemy = "^2.0.38"
 pymysql = "^1.1.1"
 pymysql = "^1.1.1"
-transformers = "^4.49.0"
-torch = "^2.6.0"
-pytesseract = "^0.3.13"
+#transformers = "^4.49.0"
+#torch = "^2.6.0"
+#pytesseract = "^0.3.13"
 opencv-python = "^4.11.0.86"
 opencv-python = "^4.11.0.86"
 pillow = "^11.1.0"
 pillow = "^11.1.0"
 flask-sqlalchemy = "^3.1.1"
 flask-sqlalchemy = "^3.1.1"
+paddleocr = "^2.7.0.2"
+paddlepaddle = "^2.4.2"
 
 
 
 
 [build-system]
 [build-system]

+ 6 - 5
requirements.txt

@@ -1,9 +1,10 @@
-Flask 
-SQLAlchemy 
-PyMySQL 
-transformers 
+Flask
+SQLAlchemy
+PyMySQL
+transformers
 torch
 torch
-pytesseract
+paddleocr
+paddlepaddle
 opencv-python
 opencv-python
 pillow
 pillow
 flask_sqlalchemy
 flask_sqlalchemy

+ 3 - 6
routes.py

@@ -3,7 +3,7 @@ from models import db, Activity
 from utils.nlp_utils import classify_activity_type
 from utils.nlp_utils import classify_activity_type
 from flask import Blueprint, request, jsonify
 from flask import Blueprint, request, jsonify
 from models import db, User
 from models import db, User
-from utils.ocr_utils import extract_text_from_image
+from utils.ocr_utils import extract_text_from_image, extract_id_card_info
 from utils.nlp_utils import extract_information
 from utils.nlp_utils import extract_information
 import os
 import os
 activity_bp = Blueprint('activity', __name__)
 activity_bp = Blueprint('activity', __name__)
@@ -58,11 +58,8 @@ def register_user():
     image_file.save(image_path)
     image_file.save(image_path)
 
 
     try:
     try:
-        # 提取文本
-        extracted_text = extract_text_from_image(image_path)
-
-        # 抽取姓名和身份证号
-        user_info = extract_information(extracted_text)
+        # 使用 PaddleOCR 直接识别身份证信息
+        user_info = extract_id_card_info(image_path)
 
 
         # 创建新用户
         # 创建新用户
         new_user = User(
         new_user = User(

+ 47 - 0
test_id_card_ocr.py

@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+身份证 OCR 识别测试脚本
+使用 PaddleOCR 进行身份证信息提取测试
+"""
+
+from utils.ocr_utils import extract_id_card_info, extract_text_from_image
+import sys
+
+def test_id_card_recognition(image_path):
+    """测试身份证识别"""
+    print(f"\n{'='*60}")
+    print(f"正在识别身份证图片: {image_path}")
+    print(f"{'='*60}\n")
+
+    # 方法1: 提取所有文本
+    print("方法1: 提取所有文本")
+    print("-" * 60)
+    all_text = extract_text_from_image(image_path)
+    print(all_text)
+    print()
+
+    # 方法2: 直接提取身份证信息
+    print("方法2: 提取身份证关键信息")
+    print("-" * 60)
+    id_info = extract_id_card_info(image_path)
+    print(f"姓名: {id_info.get('name')}")
+    print(f"身份证号: {id_info.get('id_number')}")
+    print()
+
+    # 验证结果
+    if id_info.get('name') and id_info.get('id_number'):
+        print("✓ 识别成功!")
+    else:
+        print("✗ 识别失败,请检查图片质量或格式")
+
+    print(f"{'='*60}\n")
+
+if __name__ == "__main__":
+    if len(sys.argv) < 2:
+        print("使用方法: python test_id_card_ocr.py <身份证图片路径>")
+        print("示例: python test_id_card_ocr.py ./test_images/id_card.jpg")
+        sys.exit(1)
+
+    image_path = r'/workspace/campus_activity_system/BaiduHi_2026-8-4_11-9-55.jpg' #sys.argv[1]
+    test_id_card_recognition(image_path)

+ 209 - 0
utils/image_preprocess.py

@@ -0,0 +1,209 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+图像预处理工具
+用于提高 OCR 识别准确率
+"""
+
+import cv2
+import numpy as np
+
+
+def preprocess_id_card_image(image_path, output_path=None):
+    """
+    预处理身份证图片以提高 OCR 识别率
+
+    参数:
+        image_path: 输入图片路径
+        output_path: 输出图片路径(可选,如果提供则保存预处理后的图片)
+
+    返回:
+        预处理后的图片(numpy array)
+    """
+    # 读取图片
+    img = cv2.imread(image_path)
+    if img is None:
+        raise ValueError(f"无法读取图片: {image_path}")
+
+    # 1. 转为灰度图
+    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
+
+    # 2. 降噪
+    denoised = cv2.fastNlMeansDenoising(gray, None, 10, 7, 21)
+
+    # 3. 自适应二值化
+    binary = cv2.adaptiveThreshold(
+        denoised, 255,
+        cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
+        cv2.THRESH_BINARY,
+        11, 2
+    )
+
+    # 4. 形态学操作去除噪点
+    kernel = np.ones((2, 2), np.uint8)
+    morph = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
+
+    # 5. 锐化(可选)
+    kernel_sharpen = np.array([
+        [-1, -1, -1],
+        [-1,  9, -1],
+        [-1, -1, -1]
+    ])
+    sharpened = cv2.filter2D(morph, -1, kernel_sharpen)
+
+    # 保存预处理后的图片
+    if output_path:
+        cv2.imwrite(output_path, sharpened)
+
+    return sharpened
+
+
+def enhance_image_quality(image_path):
+    """
+    增强图片质量(对比度、亮度等)
+
+    参数:
+        image_path: 输入图片路径
+
+    返回:
+        增强后的图片(numpy array)
+    """
+    img = cv2.imread(image_path)
+    if img is None:
+        raise ValueError(f"无法读取图片: {image_path}")
+
+    # 转换到 LAB 色彩空间
+    lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
+    l, a, b = cv2.split(lab)
+
+    # 对 L 通道进行直方图均衡化
+    clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
+    cl = clahe.apply(l)
+
+    # 合并通道
+    enhanced_lab = cv2.merge((cl, a, b))
+    enhanced = cv2.cvtColor(enhanced_lab, cv2.COLOR_LAB2BGR)
+
+    return enhanced
+
+
+def auto_rotate_image(image_path):
+    """
+    自动旋转图片使文字方向正确
+
+    参数:
+        image_path: 输入图片路径
+
+    返回:
+        旋转后的图片(numpy array)
+    """
+    img = cv2.imread(image_path)
+    if img is None:
+        raise ValueError(f"无法读取图片: {image_path}")
+
+    # 转为灰度图
+    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
+
+    # 边缘检测
+    edges = cv2.Canny(gray, 50, 150, apertureSize=3)
+
+    # 霍夫变换检测直线
+    lines = cv2.HoughLines(edges, 1, np.pi / 180, 200)
+
+    if lines is not None:
+        # 计算平均角度
+        angles = []
+        for rho, theta in lines[:, 0]:
+            angle = np.rad2deg(theta)
+            angles.append(angle)
+
+        # 获取主要角度
+        median_angle = np.median(angles)
+
+        # 旋转图片
+        if abs(median_angle - 90) < 45:
+            rotation_angle = median_angle - 90
+        else:
+            rotation_angle = median_angle
+
+        # 执行旋转
+        (h, w) = img.shape[:2]
+        center = (w // 2, h // 2)
+        M = cv2.getRotationMatrix2D(center, rotation_angle, 1.0)
+        rotated = cv2.warpAffine(
+            img, M, (w, h),
+            flags=cv2.INTER_CUBIC,
+            borderMode=cv2.BORDER_REPLICATE
+        )
+
+        return rotated
+
+    return img
+
+
+def resize_for_ocr(image_path, target_width=1500):
+    """
+    调整图片尺寸以适合 OCR
+
+    参数:
+        image_path: 输入图片路径
+        target_width: 目标宽度(像素)
+
+    返回:
+        调整大小后的图片(numpy array)
+    """
+    img = cv2.imread(image_path)
+    if img is None:
+        raise ValueError(f"无法读取图片: {image_path}")
+
+    # 获取原始尺寸
+    h, w = img.shape[:2]
+
+    # 如果宽度小于目标宽度,则放大
+    if w < target_width:
+        ratio = target_width / w
+        new_width = target_width
+        new_height = int(h * ratio)
+        resized = cv2.resize(
+            img, (new_width, new_height),
+            interpolation=cv2.INTER_CUBIC
+        )
+        return resized
+
+    # 如果宽度大于目标宽度,则缩小
+    elif w > target_width * 1.5:
+        ratio = target_width / w
+        new_width = target_width
+        new_height = int(h * ratio)
+        resized = cv2.resize(
+            img, (new_width, new_height),
+            interpolation=cv2.INTER_AREA
+        )
+        return resized
+
+    return img
+
+
+# 示例用法
+if __name__ == "__main__":
+    import sys
+
+    if len(sys.argv) < 2:
+        print("使用方法: python image_preprocess.py <图片路径>")
+        sys.exit(1)
+
+    image_path = sys.argv[1]
+
+    # 预处理
+    processed = preprocess_id_card_image(image_path, "processed.jpg")
+    print("预处理完成,保存为 processed.jpg")
+
+    # 增强
+    enhanced = enhance_image_quality(image_path)
+    cv2.imwrite("enhanced.jpg", enhanced)
+    print("质量增强完成,保存为 enhanced.jpg")
+
+    # 调整大小
+    resized = resize_for_ocr(image_path)
+    cv2.imwrite("resized.jpg", resized)
+    print("尺寸调整完成,保存为 resized.jpg")

+ 77 - 13
utils/ocr_utils.py

@@ -1,22 +1,86 @@
-import pytesseract
-from PIL import Image
+
+import os
+os.environ["FLAGS_use_mkldnn"] = "0"
+os.environ["FLAGS_prim_skip_onednn"] = "1"
+os.environ["FLAGS_enable_pir_api"] = "0"
+
 import cv2
 import cv2
-import numpy as np
+import re
+from paddleocr import PaddleOCR
+
+# 初始化 PaddleOCR(只需初始化一次,可复用)
+ocr = PaddleOCR(use_angle_cls=True, lang='ch')
 
 
 def extract_text_from_image(image_path):
 def extract_text_from_image(image_path):
-    """从图片中提取文本"""
-    # 使用OpenCV读取图像
-    img = cv2.imread(image_path)
-    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
+    """使用 PaddleOCR 从图片中提取文本"""
+    try:
+        # 使用 PaddleOCR 进行识别
+        result = ocr.ocr(image_path)
+
+        # 提取所有文本
+        text_list = []
+        if result and result[0]:
+            for line in result[0]:
+                text_list.append(line[1][0])
+
+        # 合并所有识别的文本
+        full_text = '\n'.join(text_list)
+        return full_text.strip()
+    except Exception as e:
+        print(f"OCR 识别错误: {str(e)}")
+        return ""
 
 
-    # 使用PIL将灰度图像转换为二值图像(黑白)
-    _, thresh = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY_INV)
+def extract_id_card_info(image_path):
+    """
+    从身份证图片中提取姓名和身份证号
+    返回: {'name': 姓名, 'id_number': 身份证号}
+    """
+    try:
+        # 使用 PaddleOCR 识别身份证
+        result = ocr.ocr(image_path)
 
 
-    # 使用pytesseract进行OCR
-    text = pytesseract.image_to_string(thresh, lang='chi_sim')  # 支持中文
-    return text.strip()
+        name = None
+        id_number = None
+
+        if result and result[0]:
+            for line in result[0]:
+                text = line[1][0]
+                confidence = line[1][1]
+
+                # 识别身份证号(18位或15位)
+                id_match = re.search(r'\d{17}[\dxX]|\d{15}', text)
+                if id_match and confidence > 0.8:
+                    id_number = id_match.group(0).upper()
+
+                # 识别姓名(通常在"姓名"关键字后面)
+                if '姓名' in text:
+                    # 获取姓名字段
+                    name_match = re.search(r'姓名[:\s]*([^\s]+)', text)
+                    if name_match:
+                        name = name_match.group(1)
+                # 或者直接识别中文姓名(2-4个汉字)
+                elif not name and confidence > 0.9:
+                    name_match = re.search(r'^[一-龥]{2,4}$', text)
+                    if name_match:
+                        name = name_match.group(0)
+
+        return {
+            'name': name,
+            'id_number': id_number
+        }
+    except Exception as e:
+        print(f"身份证识别错误: {str(e)}")
+        return {
+            'name': None,
+            'id_number': None
+        }
 
 
 # 示例调用
 # 示例调用
 if __name__ == "__main__":
 if __name__ == "__main__":
+    # 测试普通 OCR
     text = extract_text_from_image('path_to_image.png')
     text = extract_text_from_image('path_to_image.png')
-    print("Extracted Text:", text)
+    print("提取的文本:", text)
+
+    # 测试身份证识别
+    id_info = extract_id_card_info('path_to_id_card.jpg')
+    print("身份证信息:", id_info)

Some files were not shown because too many files changed in this diff