305 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			305 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import base64
 | 
						||
from flask import Flask, request, render_template, redirect, url_for, jsonify
 | 
						||
import os
 | 
						||
import uuid
 | 
						||
from PIL import Image
 | 
						||
import re
 | 
						||
import json
 | 
						||
from ESConnect import *
 | 
						||
from json_converter import json_to_string, string_to_json
 | 
						||
from openai import OpenAI
 | 
						||
# import config
 | 
						||
 | 
						||
# 创建Flask应用实例
 | 
						||
app = Flask(__name__)
 | 
						||
# app.config.from_object(config.Config)
 | 
						||
 | 
						||
# OCR和信息提取函数,使用大模型API处理图片并提取结构化信息
 | 
						||
def ocr_and_extract_info(image_path):
 | 
						||
    """
 | 
						||
    使用大模型API进行OCR识别并提取图片中的结构化信息
 | 
						||
    
 | 
						||
    参数:
 | 
						||
        image_path (str): 图片文件路径
 | 
						||
        
 | 
						||
    返回:
 | 
						||
        dict: 包含提取信息的字典,格式为 {'id': '', 'name': '', 'students': '', 'teacher': ''}
 | 
						||
    """
 | 
						||
    def encode_image(image_path):
 | 
						||
        """
 | 
						||
        将图片编码为base64格式
 | 
						||
        
 | 
						||
        参数:
 | 
						||
            image_path (str): 图片文件路径
 | 
						||
            
 | 
						||
        返回:
 | 
						||
            str: base64编码的图片字符串
 | 
						||
        """
 | 
						||
        with open(image_path, "rb") as image_file:
 | 
						||
            return base64.b64encode(image_file.read()).decode('utf-8')
 | 
						||
 | 
						||
    # 将图片转换为base64编码
 | 
						||
    base64_image = encode_image(image_path)
 | 
						||
 | 
						||
    # 初始化OpenAI客户端,使用百度AI Studio的API
 | 
						||
    client = OpenAI(
 | 
						||
        api_key="188f57db3766e02ed2c7e18373996d84f4112272",
 | 
						||
        # 含有 AI Studio 访问令牌的环境变量,https://aistudio.baidu.com/account/accessToken,
 | 
						||
        base_url="https://aistudio.baidu.com/llm/lmapi/v3",  # aistudio 大模型 api 服务域名
 | 
						||
    )
 | 
						||
 | 
						||
    # 调用大模型API进行图片识别和信息提取
 | 
						||
    chat_completion = client.chat.completions.create(
 | 
						||
        messages=[
 | 
						||
            {'role': 'system', 'content': '你是一个能理解图片和文本的助手,请根据用户提供的信息进行回答。'},
 | 
						||
            {'role': 'user', "content": [
 | 
						||
                {"type": "text", "text": "请识别这张图片中的信息,将你认为重要的数据转换为不包含嵌套的json,不要显示其它信息以便于解析"
 | 
						||
                                         "直接输出json结果即可"
 | 
						||
                                         "你可以自行决定使用哪些json字段"},
 | 
						||
                {
 | 
						||
                    "type": "image_url",
 | 
						||
                    "image_url": {
 | 
						||
                        "url": f"data:image/png;base64,{base64_image}"
 | 
						||
                    }
 | 
						||
                }
 | 
						||
            ]}
 | 
						||
        ],
 | 
						||
        model="ernie-4.5-turbo-vl-32k",  # 使用百度文心大模型
 | 
						||
    )
 | 
						||
 | 
						||
    # 获取API返回的文本内容
 | 
						||
    response_text = chat_completion.choices[0].message.content
 | 
						||
    
 | 
						||
    # 添加调试信息:输出模型返回的原始字符串
 | 
						||
    print("=" * 50)
 | 
						||
    print("模型返回的原始字符串:")
 | 
						||
    print(response_text)
 | 
						||
    print("=" * 50)
 | 
						||
 | 
						||
    def parse_respound(text):
 | 
						||
        """
 | 
						||
        解析API返回的文本,提取JSON数据
 | 
						||
        
 | 
						||
        参数:
 | 
						||
            text (str): API返回的文本
 | 
						||
            
 | 
						||
        返回:
 | 
						||
            dict or None: 解析成功返回字典,失败返回None
 | 
						||
        """
 | 
						||
        # 尝试直接解析标准JSON
 | 
						||
        try:
 | 
						||
            result=json.loads(text)
 | 
						||
            if result:
 | 
						||
                print("✓ 成功解析标准JSON格式")
 | 
						||
                return result
 | 
						||
        except json.JSONDecodeError:
 | 
						||
            print("✗ 无法解析标准JSON格式")
 | 
						||
            pass
 | 
						||
 | 
						||
        # 提取markdown代码块中的内容
 | 
						||
        code_block = re.search(r'```json\n(.*?)```', text, re.DOTALL)
 | 
						||
        if code_block:
 | 
						||
            try:
 | 
						||
                result=json.loads(code_block.group(1))
 | 
						||
                if result:
 | 
						||
                    print("✓ 成功解析markdown代码块中的JSON")
 | 
						||
                    return result
 | 
						||
            except json.JSONDecodeError:
 | 
						||
                print("✗ 无法解析markdown代码块中的JSON")
 | 
						||
                pass
 | 
						||
 | 
						||
        # 尝试替换单引号并解析
 | 
						||
        try:
 | 
						||
            fixed_json = text.replace("'", "\"")
 | 
						||
            result=json.loads(fixed_json)
 | 
						||
            if(result):
 | 
						||
                print("✓ 成功解析替换单引号后的JSON")
 | 
						||
                return result
 | 
						||
        except json.JSONDecodeError:
 | 
						||
            print("✗ 无法解析替换单引号后的JSON")
 | 
						||
            pass
 | 
						||
 | 
						||
    # 解析API返回的文本
 | 
						||
    result_data = parse_respound(response_text)
 | 
						||
    
 | 
						||
    # 添加调试信息:输出解析结果
 | 
						||
    print("解析结果:")
 | 
						||
    if result_data:
 | 
						||
        print(f"✓ 解析成功: {result_data}")
 | 
						||
    else:
 | 
						||
        print("✗ 解析失败,返回None")
 | 
						||
    print("=" * 50)
 | 
						||
    
 | 
						||
    return result_data
 | 
						||
 | 
						||
    """
 | 
						||
    模拟大模型识别图像并返回结构化JSON。
 | 
						||
    实际应调用Qwen-VL或其他OCR+解析服务。
 | 
						||
    """
 | 
						||
 | 
						||
 | 
						||
# 首页路由
 | 
						||
@app.route('/')
 | 
						||
def index():
 | 
						||
    """
 | 
						||
    渲染首页模板
 | 
						||
    
 | 
						||
    返回:
 | 
						||
        str: 渲染后的HTML页面
 | 
						||
    """
 | 
						||
    return render_template('index.html')
 | 
						||
 | 
						||
# 图片上传路由
 | 
						||
@app.route('/upload', methods=['POST'])
 | 
						||
def upload_image():
 | 
						||
    """
 | 
						||
    处理图片上传请求,调用OCR识别并存储结果
 | 
						||
    
 | 
						||
    返回:
 | 
						||
        JSON: 上传成功或失败的响应
 | 
						||
    """
 | 
						||
    # 获取上传的文件
 | 
						||
    file = request.files.get('file')
 | 
						||
    if not file:
 | 
						||
        return jsonify({"error": "No file uploaded"}), 400
 | 
						||
 | 
						||
    # 保存上传的图片
 | 
						||
    filename = f"{uuid.uuid4()}_{file.filename}"
 | 
						||
    image_path = os.path.join("image", filename)
 | 
						||
    file.save(image_path)
 | 
						||
 | 
						||
    # 调用大模型进行识别
 | 
						||
    try:
 | 
						||
        print(f"开始处理图片: {image_path}")
 | 
						||
        original_data = ocr_and_extract_info(image_path)  # 获取原始JSON数据
 | 
						||
        if original_data:
 | 
						||
            # 使用json_converter将JSON数据转换为字符串
 | 
						||
            data_string = json_to_string(original_data)
 | 
						||
            print(f"转换后的数据字符串: {data_string}")
 | 
						||
            
 | 
						||
            # 构造新的数据结构,只包含data和image字段
 | 
						||
            processed_data = {
 | 
						||
                "data": data_string,
 | 
						||
                "image": filename  # 存储图片文件名
 | 
						||
            }
 | 
						||
            print(f"准备存储的数据: {processed_data}")
 | 
						||
            
 | 
						||
            insert_data(processed_data)  # 存入ES
 | 
						||
            print("✓ 数据成功存储到Elasticsearch")
 | 
						||
            return jsonify({"message": "成功录入", "data": original_data, "processed": processed_data})
 | 
						||
        else:
 | 
						||
            print("✗ 无法识别图片内容")
 | 
						||
            return jsonify({"error": "无法识别图片内容"}), 400
 | 
						||
    except Exception as e:
 | 
						||
        print(f"✗ 处理过程中发生错误: {str(e)}")
 | 
						||
        return jsonify({"error": str(e)}), 500
 | 
						||
 | 
						||
# 搜索路由
 | 
						||
@app.route('/search')
 | 
						||
def search():
 | 
						||
    """
 | 
						||
    处理搜索请求,从Elasticsearch中检索匹配的数据
 | 
						||
    
 | 
						||
    返回:
 | 
						||
        JSON: 搜索结果列表
 | 
						||
    """
 | 
						||
    keyword = request.args.get('q')
 | 
						||
    if not keyword:
 | 
						||
        return jsonify([])
 | 
						||
    results = search_by_any_field(keyword)
 | 
						||
    
 | 
						||
    # 处理搜索结果,将data字段转换回JSON格式
 | 
						||
    processed_results = []
 | 
						||
    for result in results:
 | 
						||
        if '_source' in result and 'data' in result['_source']:
 | 
						||
            try:
 | 
						||
                # 将data字段的字符串转换回JSON
 | 
						||
                original_data = string_to_json(result['_source']['data'])
 | 
						||
                # 构造新的结果格式
 | 
						||
                processed_result = {
 | 
						||
                    '_id': result.get('_id', ''),
 | 
						||
                    '_source': {
 | 
						||
                        'image': result['_source'].get('image', ''),
 | 
						||
                        **original_data  # 展开原始数据字段
 | 
						||
                    }
 | 
						||
                }
 | 
						||
                processed_results.append(processed_result)
 | 
						||
            except Exception as e:
 | 
						||
                # 如果转换失败,保持原始格式
 | 
						||
                processed_results.append(result)
 | 
						||
        else:
 | 
						||
            processed_results.append(result)
 | 
						||
    
 | 
						||
    print(processed_results)
 | 
						||
    return jsonify(processed_results)
 | 
						||
 | 
						||
# 结果页面路由
 | 
						||
@app.route('/results')
 | 
						||
def results_page():
 | 
						||
    """
 | 
						||
    渲染搜索结果页面
 | 
						||
    
 | 
						||
    返回:
 | 
						||
        str: 渲染后的HTML页面
 | 
						||
    """
 | 
						||
    return render_template('results.html')
 | 
						||
 | 
						||
# 显示所有数据路由
 | 
						||
@app.route('/all')
 | 
						||
def show_all():
 | 
						||
    """
 | 
						||
    获取所有数据并渲染到页面
 | 
						||
    
 | 
						||
    返回:
 | 
						||
        str: 渲染后的HTML页面,包含所有数据
 | 
						||
    """
 | 
						||
    all_data = search_all()
 | 
						||
    # 将data字段从字符串转换回JSON格式以便显示
 | 
						||
    processed_data = []
 | 
						||
    for item in all_data:
 | 
						||
        if 'data' in item and item['data']:
 | 
						||
            try:
 | 
						||
                # 将data字段的字符串转换回JSON
 | 
						||
                original_data = string_to_json(item['data'])
 | 
						||
                # 合并原始数据和其他字段
 | 
						||
                display_item = {
 | 
						||
                    '_id': item['_id'],
 | 
						||
                    'image': item.get('image', ''),
 | 
						||
                    **original_data  # 展开原始数据字段
 | 
						||
                }
 | 
						||
                processed_data.append(display_item)
 | 
						||
            except Exception as e:
 | 
						||
                # 如果转换失败,保持原始格式
 | 
						||
                processed_data.append(item)
 | 
						||
        else:
 | 
						||
            processed_data.append(item)
 | 
						||
    
 | 
						||
    return render_template('all.html', data=processed_data)
 | 
						||
 | 
						||
# 删除数据路由
 | 
						||
@app.route('/delete/<doc_id>', methods=['POST'])
 | 
						||
def delete_entry(doc_id):
 | 
						||
    """
 | 
						||
    根据文档ID删除数据
 | 
						||
    
 | 
						||
    参数:
 | 
						||
        doc_id (str): 要删除的文档ID
 | 
						||
        
 | 
						||
    返回:
 | 
						||
        重定向到所有数据页面或错误信息
 | 
						||
    """
 | 
						||
    if delete_by_id(doc_id):
 | 
						||
        return redirect(url_for('show_all'))
 | 
						||
    else:
 | 
						||
        return "删除失败", 500
 | 
						||
 | 
						||
 | 
						||
 | 
						||
# 主程序入口
 | 
						||
if __name__ == '__main__':
 | 
						||
    # 创建Elasticsearch索引
 | 
						||
    create_index_with_mapping()  
 | 
						||
    # 创建图片存储目录
 | 
						||
    os.makedirs("image", exist_ok=True)
 | 
						||
    # 启动Flask应用
 | 
						||
    app.run(use_reloader=False) |