nihuajian commited on
Commit
4ce6318
·
verified ·
1 Parent(s): 8b6067a

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +302 -0
app.py ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import torch
3
+ from PIL import Image
4
+ from transformers import AutoModel, AutoTokenizer
5
+ import warnings
6
+ import os
7
+
8
+ # 禁用警告信息
9
+ warnings.filterwarnings("ignore")
10
+
11
+ # 全局变量存储模型
12
+ model = None
13
+ tokenizer = None
14
+
15
+ def load_model():
16
+ """加载MiniCPM-o模型"""
17
+ global model, tokenizer
18
+ if model is None:
19
+ print("正在加载MiniCPM-o模型...")
20
+ model = AutoModel.from_pretrained(
21
+ 'openbmb/MiniCPM-o-2_6',
22
+ trust_remote_code=True,
23
+ attn_implementation='sdpa',
24
+ torch_dtype=torch.bfloat16,
25
+ init_vision=True,
26
+ init_audio=False,
27
+ init_tts=False
28
+ )
29
+ model = model.eval().cuda()
30
+ tokenizer = AutoTokenizer.from_pretrained('openbmb/MiniCPM-o-2_6', trust_remote_code=True)
31
+ print("模型加载完成")
32
+ return model, tokenizer
33
+
34
+ def clean_markdown_output(text):
35
+ """清理输出文本,只保留markdown表格"""
36
+ lines = text.strip().split('\n')
37
+ markdown_lines = []
38
+
39
+ # 查找markdown表格的开始和结束
40
+ in_table = False
41
+ for line in lines:
42
+ line = line.strip()
43
+ # 检查是否是表格行(包含|符号)
44
+ if '|' in line and not line.startswith('```'):
45
+ in_table = True
46
+ markdown_lines.append(line)
47
+ elif in_table and line == '':
48
+ # 空行可能表示表格结束
49
+ break
50
+ elif in_table and not line.startswith('```'):
51
+ # 继续收集表格相关行
52
+ markdown_lines.append(line)
53
+
54
+ # 如果没有找到表格,返回原始清理后的文本
55
+ if not markdown_lines:
56
+ # 移除代码块标记和多余的说明文字
57
+ cleaned_text = text.replace('```markdown', '').replace('```', '').strip()
58
+ # 移除常见的解释性文字
59
+ lines = cleaned_text.split('\n')
60
+ result_lines = []
61
+ for line in lines:
62
+ line = line.strip()
63
+ if line and not line.startswith('这个表格') and not line.startswith('该表格') and not line.startswith('表格显示'):
64
+ result_lines.append(line)
65
+ return '\n'.join(result_lines)
66
+
67
+ return '\n'.join(markdown_lines)
68
+
69
+ def clean_formula_output(text):
70
+ """清理输出文本,只保留LaTeX公式"""
71
+ lines = text.strip().split('\n')
72
+ formula_lines = []
73
+
74
+ for line in lines:
75
+ line = line.strip()
76
+ # 跳过解释性文字
77
+ if line and not any(line.startswith(prefix) for prefix in [
78
+ '这个公式', '该公式', '公式表示', '根据图片', '图片中的', '识别结果'
79
+ ]):
80
+ # 保留包含LaTeX语法的行
81
+ if any(symbol in line for symbol in ['$', '\\', '{', '}', '^', '_']) or '=' in line:
82
+ formula_lines.append(line)
83
+ # 或者保留纯数学表达式
84
+ elif any(char.isdigit() or char in '+-*/=()[]{}^_' for char in line):
85
+ formula_lines.append(line)
86
+
87
+ # 如果没有找到公式,返回原始清理后的文本
88
+ if not formula_lines:
89
+ cleaned_text = text.replace('```latex', '').replace('```', '').strip()
90
+ lines = cleaned_text.split('\n')
91
+ result_lines = []
92
+ for line in lines:
93
+ line = line.strip()
94
+ if line and not any(line.startswith(prefix) for prefix in [
95
+ '这个公式', '该公式', '公式表示', '根据图片', '图片中的'
96
+ ]):
97
+ result_lines.append(line)
98
+ return '\n'.join(result_lines)
99
+
100
+ return '\n'.join(formula_lines)
101
+
102
+ def clean_text_output(text):
103
+ """清理输出文本,只保留识别的文字内容"""
104
+ lines = text.strip().split('\n')
105
+ text_lines = []
106
+
107
+ # 移除代码块标记
108
+ cleaned_text = text.replace('```text', '').replace('```', '').strip()
109
+ lines = cleaned_text.split('\n')
110
+
111
+ for line in lines:
112
+ line = line.strip()
113
+ # 跳过解释性文字
114
+ if line and not any(line.startswith(prefix) for prefix in [
115
+ '图片中的文字', '识别结果', '文字内容', '根据图片', '这张图片', '该图片'
116
+ ]):
117
+ text_lines.append(line)
118
+
119
+ return '\n'.join(text_lines)
120
+
121
+ def parse_image(image, parse_type):
122
+ """解析图片内容为指定格式"""
123
+ try:
124
+ # 确保模型已加载
125
+ model, tokenizer = load_model()
126
+
127
+ if image is None:
128
+ return "请上传一张图片", ""
129
+
130
+ # 转换图片格式
131
+ if isinstance(image, str):
132
+ image = Image.open(image).convert('RGB')
133
+ elif hasattr(image, 'convert'):
134
+ image = image.convert('RGB')
135
+
136
+ # 根据解析类型设置不同的提示词
137
+ questions = {
138
+ "表格解析": "解析一下这个表格为markdown格式,不需要任何解释和思考,直接输出markdown格式",
139
+ "公式解析": "识别并提取���片中的数学公式,用LaTeX格式输出,不需要任何解释,直接输出公式",
140
+ "文本解析": "识别并提取图片中的所有文字内容,保持原有格式,不需要任何解释,直接输出文字内容"
141
+ }
142
+
143
+ question = questions.get(parse_type, questions["表格解析"])
144
+ msgs = [{'role': 'user', 'content': [image, question]}]
145
+
146
+ # 使用流式输出获取结果
147
+ res = model.chat(
148
+ msgs=msgs,
149
+ tokenizer=tokenizer,
150
+ sampling=True,
151
+ stream=True
152
+ )
153
+
154
+ # 收集所有输出文本
155
+ generated_text = ""
156
+ for new_text in res:
157
+ generated_text += new_text
158
+
159
+ # 根据类型清理输出
160
+ if parse_type == "表格解析":
161
+ result = clean_markdown_output(generated_text)
162
+ output_format = "Markdown"
163
+ elif parse_type == "公式解析":
164
+ result = clean_formula_output(generated_text)
165
+ output_format = "LaTeX"
166
+ elif parse_type == "文本解析":
167
+ result = clean_text_output(generated_text)
168
+ output_format = "纯文本"
169
+ else:
170
+ result = generated_text.strip()
171
+ output_format = "原始输出"
172
+
173
+ return result, f"解析完成 - 输出格式: {output_format}"
174
+
175
+ except Exception as e:
176
+ return f"解析失败: {str(e)}", "错误"
177
+
178
+ def create_interface():
179
+ """创建Gradio界面"""
180
+
181
+ # 自定义CSS样式
182
+ css = """
183
+ .gradio-container {
184
+ font-family: 'Helvetica Neue', Arial, sans-serif;
185
+ }
186
+ .output-text {
187
+ font-family: 'Courier New', monospace;
188
+ font-size: 14px;
189
+ }
190
+ """
191
+
192
+ with gr.Blocks(css=css, title="MiniCPM 多模态内容解析工具") as interface:
193
+ gr.Markdown("""
194
+ # 🚀 MiniCPM 多模态内容解析工具
195
+
196
+ 基于MiniCPM-o多模态模型的智能图片内容解析工具,支持表格、公式、文本三种解析模式。
197
+
198
+ ## 📋 使用说明
199
+ 1. **上传图片**: 支持 PNG、JPG、JPEG 等格式
200
+ 2. **选择解析类型**: 根据图片内容选择相应的解析模式
201
+ 3. **获取结果**: 自动清理输出,获得纯净的解析结果
202
+
203
+ ## 🎯 解析类型说明
204
+ - **📊 表格解析**: 将表格图片转换为Markdown格式
205
+ - **🧮 公式解析**: 识别数学公式并输出LaTeX格式
206
+ - **📝 文本解析**: 提取图片中的所有文字内容
207
+ """)
208
+
209
+ with gr.Row():
210
+ with gr.Column(scale=1):
211
+ # 输入组件
212
+ image_input = gr.Image(
213
+ label="📷 上传图片",
214
+ type="pil",
215
+ height=400
216
+ )
217
+
218
+ parse_type = gr.Radio(
219
+ choices=["表格解析", "公式解析", "文本解析"],
220
+ value="表格解析",
221
+ label="🎛️ 选择解析类型",
222
+ info="根据图片内容选择合适的解析模式"
223
+ )
224
+
225
+ parse_button = gr.Button(
226
+ "🔍 开始解析",
227
+ variant="primary",
228
+ size="lg"
229
+ )
230
+
231
+ with gr.Column(scale=1):
232
+ # 输出组件
233
+ status_output = gr.Textbox(
234
+ label="📊 解析状态",
235
+ value="等待上传图片...",
236
+ interactive=False
237
+ )
238
+
239
+ result_output = gr.Textbox(
240
+ label="📄 解析结果",
241
+ lines=20,
242
+ max_lines=30,
243
+ show_copy_button=True,
244
+ elem_classes=["output-text"],
245
+ placeholder="解析结果将在这里显示..."
246
+ )
247
+
248
+ # 示例图片
249
+ gr.Markdown("## 📖 示例图片")
250
+ with gr.Row():
251
+ gr.Examples(
252
+ examples=[
253
+ ["table.png", "表格解析"],
254
+ ["formulas.png", "公式解析"],
255
+ ["text.png", "文本解析"]
256
+ ],
257
+ inputs=[image_input, parse_type],
258
+ label="点击示例快速体验"
259
+ )
260
+
261
+ # 绑定事件
262
+ parse_button.click(
263
+ fn=parse_image,
264
+ inputs=[image_input, parse_type],
265
+ outputs=[result_output, status_output]
266
+ )
267
+
268
+ # 添加页脚信息
269
+ gr.Markdown("""
270
+ ---
271
+ ### 💡 使用提示
272
+ - 确保图片清晰,内容结构明显
273
+ - 复杂表格建议分段处理
274
+ - 公式图片建议使用高分辨率
275
+ - 文字图片避免模糊、倾斜或光线不足
276
+
277
+ ### 🔧 技术支持
278
+ - 模���: MiniCPM-o-2.6
279
+ - 框架: Gradio + Transformers
280
+ - GPU: CUDA加速推理
281
+ """)
282
+
283
+ return interface
284
+
285
+ if __name__ == "__main__":
286
+ # 预加载模型(可选,在启动时加载以减少首次使用延迟)
287
+ try:
288
+ load_model()
289
+ print("✅ 模型预加载完成")
290
+ except Exception as e:
291
+ print(f"⚠️ 模型预加载失败: {e}")
292
+ print("模型将在首次使用时加载")
293
+
294
+ # 创建并启动界面
295
+ interface = create_interface()
296
+ interface.launch(
297
+ server_name="0.0.0.0", # 允许外部访问
298
+ server_port=7860, # Hugging Face Spaces默认端口
299
+ share=False, # 在Hugging Face上部署时设为False
300
+ show_error=True, # 显示详细错误信息
301
+ quiet=False # 显示启动信息
302
+ )