ImgTextParser / app.py
SynaptechX's picture
Update app.py
3bf5b34 verified
import gradio as gr
import torch
from PIL import Image
from transformers import AutoModel, AutoTokenizer
import warnings
import os
import spaces
# 禁用警告信息
warnings.filterwarnings("ignore")
# 全局变量存储模型
model = None
tokenizer = None
@spaces.GPU
def load_model():
"""加载MiniCPM-o模型"""
global model, tokenizer
if model is None:
print("正在加载MiniCPM-o模型...")
device = "cuda" if torch.cuda.is_available() else "cpu"
model = AutoModel.from_pretrained(
'openbmb/MiniCPM-o-2_6',
trust_remote_code=True,
torch_dtype=torch.bfloat16 if device == "cuda" else torch.float32,
device_map="auto" if device == "cuda" else None,
init_vision=True,
init_audio=False,
init_tts=False
)
model = model.eval().to(device)
tokenizer = AutoTokenizer.from_pretrained('openbmb/MiniCPM-o-2_6', trust_remote_code=True)
print("模型加载完成")
return model, tokenizer
def clean_markdown_output(text):
"""清理输出文本,只保留markdown表格"""
lines = text.strip().split('\n')
markdown_lines = []
# 查找markdown表格的开始和结束
in_table = False
for line in lines:
line = line.strip()
# 检查是否是表格行(包含|符号)
if '|' in line and not line.startswith('```'):
in_table = True
markdown_lines.append(line)
elif in_table and line == '':
# 空行可能表示表格结束
break
elif in_table and not line.startswith('```'):
# 继续收集表格相关行
markdown_lines.append(line)
# 如果没有找到表格,返回原始清理后的文本
if not markdown_lines:
# 移除代码块标记和多余的说明文字
cleaned_text = text.replace('```markdown', '').replace('```', '').strip()
# 移除常见的解释性文字
lines = cleaned_text.split('\n')
result_lines = []
for line in lines:
line = line.strip()
if line and not line.startswith('这个表格') and not line.startswith('该表格') and not line.startswith('表格显示'):
result_lines.append(line)
return '\n'.join(result_lines)
return '\n'.join(markdown_lines)
def clean_formula_output(text):
"""清理输出文本,只保留LaTeX公式"""
lines = text.strip().split('\n')
formula_lines = []
for line in lines:
line = line.strip()
# 跳过解释性文字
if line and not any(line.startswith(prefix) for prefix in [
'这个公式', '该公式', '公式表示', '根据图片', '图片中的', '识别结果'
]):
# 保留包含LaTeX语法的行
if any(symbol in line for symbol in ['$', '\\', '{', '}', '^', '_']) or '=' in line:
formula_lines.append(line)
# 或者保留纯数学表达式
elif any(char.isdigit() or char in '+-*/=()[]{}^_' for char in line):
formula_lines.append(line)
# 如果没有找到公式,返回原始清理后的文本
if not formula_lines:
cleaned_text = text.replace('```latex', '').replace('```', '').strip()
lines = cleaned_text.split('\n')
result_lines = []
for line in lines:
line = line.strip()
if line and not any(line.startswith(prefix) for prefix in [
'这个公式', '该公式', '公式表示', '根据图片', '图片中的'
]):
result_lines.append(line)
return '\n'.join(result_lines)
return '\n'.join(formula_lines)
def clean_text_output(text):
"""清理输出文本,只保留识别的文字内容"""
# 移除代码块标记
cleaned_text = text.replace('```text', '').replace('```', '').strip()
lines = cleaned_text.split('\n')
text_lines = []
for line in lines:
line = line.strip()
# 跳过解释性文字和标签信息
if line and not any(line.startswith(prefix) for prefix in [
'图片中的文字', '识别结果', '文字内容', '根据图片', '这张图片', '该图片',
'标题:', '正文:', '内容:', '文本:', '题目:', '段落:', '文字:'
]):
# 移除行首的标签格式(如 "标题:内容" -> "内容")
if ':' in line:
# 检查是否是标签格式
parts = line.split(':', 1)
if len(parts) == 2 and len(parts[0]) <= 10: # 标签通常很短
# 可能的标签词
label_keywords = ['标题', '正文', '内容', '文本', '题目', '段落', '文字', '主题', '副标题']
if any(keyword in parts[0] for keyword in label_keywords):
# 只保留标签后的内容
text_lines.append(parts[1].strip())
else:
# 不是标签格式,保留整行
text_lines.append(line)
else:
text_lines.append(line)
else:
text_lines.append(line)
return '\n'.join(text_lines)
@spaces.GPU
def parse_image(image, parse_type):
"""解析图片内容为指定格式"""
try:
# 确保模型已加载
model, tokenizer = load_model()
if image is None:
return "请上传一张图片", ""
# 转换图片格式
if isinstance(image, str):
image = Image.open(image).convert('RGB')
elif hasattr(image, 'convert'):
image = image.convert('RGB')
# 根据解析类型设置不同的提示词
questions = {
"表格解析": "解析一下这个表格为markdown格式,不需要任何解释和思考,直接输出markdown格式",
"公式解析": "识别并提取图片中的数学公式,用LaTeX格式输出,不需要任何解释,直接输出公式",
"文本解析": "识别并提取图片中的所有文字内容,保持原有格式,不需要任何解释,直接输出文字内容"
}
question = questions.get(parse_type, questions["表格解析"])
msgs = [{'role': 'user', 'content': [image, question]}]
# 使用流式输出获取结果
res = model.chat(
msgs=msgs,
tokenizer=tokenizer,
sampling=True,
stream=True
)
# 收集所有输出文本
generated_text = ""
for new_text in res:
generated_text += new_text
# 根据类型清理输出
if parse_type == "表格解析":
result = clean_markdown_output(generated_text)
output_format = "Markdown"
elif parse_type == "公式解析":
result = clean_formula_output(generated_text)
output_format = "LaTeX"
elif parse_type == "文本解析":
result = clean_text_output(generated_text)
output_format = "纯文本"
else:
result = generated_text.strip()
output_format = "原始输出"
return result, f"解析完成 - 输出格式: {output_format}"
except Exception as e:
return f"解析失败: {str(e)}", "错误"
def create_interface():
"""创建Gradio界面"""
# 自定义CSS样式
css = """
.gradio-container {
font-family: 'Helvetica Neue', Arial, sans-serif;
}
.output-text {
font-family: 'Courier New', monospace;
font-size: 14px;
}
"""
with gr.Blocks(css=css, title="MiniCPM 多模态内容解析工具", analytics_enabled=False) as interface:
gr.Markdown("""
# 🚀 MiniCPM 多模态内容解析工具
基于MiniCPM-o多模态模型的智能图片内容解析工具,支持表格、公式、文本三种解析模式。
## 📋 使用说明
1. **上传图片**: 支持 PNG、JPG、JPEG 等格式
2. **选择解析类型**: 根据图片内容选择相应的解析模式
3. **获取结果**: 自动清理输出,获得纯净的解析结果
## 🎯 解析类型说明
- **📊 表格解析**: 将表格图片转换为Markdown格式
- **🧮 公式解析**: 识别数学公式并输出LaTeX格式
- **📝 文本解析**: 提取图片中的所有文字内容
""")
with gr.Row():
with gr.Column(scale=1):
# 输入组件
image_input = gr.Image(
label="📷 上传图片",
type="pil",
height=400
)
parse_type = gr.Radio(
choices=["表格解析", "公式解析", "文本解析"],
value="表格解析",
label="🎛️ 选择解析类型",
info="根据图片内容选择合适的解析模式"
)
parse_button = gr.Button(
"🔍 开始解析",
variant="primary",
size="lg"
)
with gr.Column(scale=1):
# 输出组件
status_output = gr.Textbox(
label="📊 解析状态",
value="等待上传图片...",
interactive=False
)
result_output = gr.Textbox(
label="📄 解析结果",
lines=20,
max_lines=30,
show_copy_button=True,
elem_classes=["output-text"],
placeholder="解析结果将在这里显示...",
interactive=True
)
# 示例图片
gr.Markdown("## 📖 示例图片")
with gr.Row():
gr.Examples(
examples=[
["./table.png", "表格解析"],
["./formulas.png", "公式解析"],
["./text.png", "文本解析"]
],
inputs=[image_input, parse_type],
label="点击示例快速体验",
cache_examples=False
)
# 绑定事件
parse_button.click(
fn=parse_image,
inputs=[image_input, parse_type],
outputs=[result_output, status_output]
)
# 添加页脚信息
gr.Markdown("""
---
### 💡 使用提示
- 确保图片清晰,内容结构明显
- 复杂表格建议分段处理
- 公式图片建议使用高分辨率
- 文字图片避免模糊、倾斜或光线不足
### 🔧 技术支持
- 模型: MiniCPM-o-2.6
- 框架: Gradio + Transformers
- GPU: CUDA加速推理
""")
return interface
if __name__ == "__main__":
# 在ZeroGPU环境中不预加载模型,按需加载以节省资源
print("🚀 启动MiniCPM多模态内容解析工具")
print("📝 模型将在首次使用时自动加载")
# 创建并启动界面
interface = create_interface()
interface.launch(
server_name="0.0.0.0", # 允许外部访问
server_port=7860, # Hugging Face Spaces默认端口
share=False, # 在Hugging Face上部署时设为False
show_error=True, # 显示详细错误信息
quiet=False, # 显示启动信息
debug=False, # 关闭调试模式
)