基于 Flask 的 Python 应用程序,主要功能包括用户认证、文件上传(CSV 和图片)、图像文字识别(OCR)以及根据识别结果进行一些数据处理和比对
·
#coding=utf-8
from jspt_data import jspt_cx_table,jspt_cx_table_sql
from jspt_forms import Login, RegisterForm
from flask_sqlalchemy import SQLAlchemy
from flask import Flask, render_template, session, redirect, url_for, flash, request, jsonify
# import os
# from flask_script import Manager, Shell
import records
from flask_login import UserMixin, LoginManager, login_required, login_user, logout_user, current_user
from werkzeug.security import generate_password_hash,check_password_hash
# from sklearn.metrics import f1_score 调试镜像去掉这个包
from thestr import strrelease
import pandas as pd
from datetime import datetime
import cv2
from paddleocr import PaddleOCR
import time
import math
# import requests
# import json
import base64
import numpy as np
import io
import sys
from fuzzywuzzy import fuzz
import gc
sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8') #解决print中文报错问题
app = Flask(__name__)
app.config['TEMPLATES_AUTO_RELOAD']=True
app.config['SECRET_KEY'] = 'dirneciemedmdua;APE;D'
strpg=f"//qzcx:{strrelease(99,'DDEAMDBFDFBFCFCFDFBFDF')}@134.140.1.157:5432/qzdatadb"
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.session_protection = 'basic'
# session_protection设置登录用户的安全级别,可选None、'basic'、'strong',安全等级由低到高。
# 最高的安全等级‘strong’,flask_login会记住客户端的IP地址和用户代理信息,如果发现有不一致就登出用户
login_manager.login_view = 'jspt_login'
#在用户未登录的情况下,输入http://localhost:5000/loginrq时,flask会提示我们先进行登录
#这是 初始化flask_login时要告诉它登录的视图函数的原因
login_manager.login_message = u"请先登录。"
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql:'+strpg
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
db = SQLAlchemy(app)
app.debug=True
v_yure = 3120
v_picsize=1.5
ocr = PaddleOCR(use_angle_cls=True, use_gpu=True, lang="ch", det_limit_side_len=320, det_limit_type="min")
class User(UserMixin, db.Model):
__tablename__ = 'jspt_user'
user_id = db.Column(db.Integer, primary_key=True)
user_name = db.Column(db.String(30))
_password = db.Column(db.String(200))
user_role = db.Column(db.String(30))
def __init__(self, user_id, user_name, password, user_role=''):
self.user_id = user_id
self.user_name = user_name
self.password = password
self.user_role = user_role
@property
def password(self):
return self._password
@password.setter
def password(self, raw):
self._password = generate_password_hash(raw)
def check_password(self, raw):
return check_password_hash(self._password, raw)
def get_id(self):
return self.user_id
def __repr__(self):
return '<User %r>' % self.user_name
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
@app.route('/', methods=['GET', 'POST'])
def index():
return render_template('jpg_upload.html')
@app.route('/jspt_upload_file/', methods=['POST'])
@login_required
def jspt_upload_file():
print('jspt_upload_file POST')
db = records.Database('postgresql+psycopg2:' + strpg)
print('连接成功')
rows = db.query(f"select b.*,b.up_count-a.cou lastcou from (select count(*) cou from jspt_upload_record a where user_id={current_user.user_id} )a,(select j.*,to_char(end_time,'yyyy-mm-dd hh24:mi:ss') time3 from jspt_setting j where type='开放')b ")
s_endtime=rows[0].as_dict()['time3']
endtime=datetime.strptime(s_endtime,'%Y-%m-%d %H:%M:%S')
print(endtime)
if endtime<datetime.now():
db.close()
return jsonify({
'code': 0,
'msg': f"您上传时间已超过赛题最后上传时间:{endtime},无法再次上传"
})
lastcou=rows[0].as_dict()['lastcou']
if lastcou<=0:
db.close()
return jsonify({
'code': 0,
'msg': f"您上传次数已超{rows[0].as_dict()['up_count']}次,无法再次上传"
})
f = request.files.get('file')
df2 = pd.read_csv(f, encoding='utf-8')
df2 = df2.sort_values('id')
f.save('./upload_csv/'+f.filename)
print('保存上传文件成功')
df1 = pd.read_csv(f"./static/{rows[0].as_dict()['da']}.csv", encoding='utf-8')
df1 = df1.sort_values('id')
if rows[0].as_dict()['scoretype']=='二分类':
print('二分类评分')
# score = f1_score(df1['lable'].tolist(), df2['lable'].tolist(), average="binary")
score =95
else:
print('多分类评分')
# score = f1_score(df1['lable'].tolist(), df2['lable'].tolist(), average="macro")
score =95
df1.drop(df1.index, inplace=True)
df2.drop(df2.index, inplace=True)
s_sql = f"insert into jspt_upload_record(user_id, upload_time,filename,score) values({current_user.user_id},now(),'{f.filename}',{score})"
print(s_sql)
db.bulk_query(s_sql)
rows = db.query(f'SELECT max(score) score FROM jspt_score where user_id={ current_user.user_id }')
if rows[0].as_dict()['score']:
print('上载过')
if rows[0].as_dict()['score']<score:
print('上载过,但这次分数比以前高')
s_sql = f"delete from jspt_score where user_id={current_user.user_id}"
db.bulk_query(s_sql)
s_sql = f"insert into jspt_score(user_id, score,up_time) values({current_user.user_id},{score},now())"
db.bulk_query(s_sql)
else:
print('上载过,这次分数没以前高')
else:
print('未上载过')
s_sql = f"insert into jspt_score(user_id, score,up_time) values({current_user.user_id},{score},now())"
db.bulk_query(s_sql)
print('保存数据库上传记录,评分成功')
db.close()
return jsonify({
'code': 0,
'msg': f'上传成功,分数:{score},还可以上传{lastcou-1}次'
})
def rotate_image(image, angle):
# 获取图片尺寸
(h, w) = image.shape[:2]
# 旋转中心设为图片中心
center = (w / 2, h / 2)
# 获取旋转矩阵
M = cv2.getRotationMatrix2D(center, angle, 1.0)
# 执行仿射变换
rotated = cv2.warpAffine(image, M, (w, h))
return rotated
def myocr(img,rotate=0,picsize=1.5,out="html"):
start_time = time.time()
if rotate == 1:
img = rotate_image(img, 90)
if picsize != 1:
y, x = img.shape[0:2]
v_size = y
if v_size < x:
v_size = x # 取大的那个
if (v_size * picsize > v_yure): # 如果比3120大,重新计算picsize
picsize = math.floor((v_yure / v_size) * 10) / 10 # 小数点向下取整1位,不四舍五入
img = cv2.resize(img, (int(x * picsize), int(y * picsize)))
reslut = ocr.ocr(img)
# print(reslut)
if out=="html":
s = '<table class="layui-table" style="width:50%">'
else:
list_out =[]
if reslut:
for line in reslut:
for word in line:
text_line=word[-1]
text=text_line[0]
# print('text',text)
if out == "html":
s=s+'<td>'+text+'</td></tr>'
else:
list_out.append(text)
end_time = time.time()
execution_time = round(end_time - start_time,2)
# del ocr
# gc.collect()
if out == "html":
s = s + '<td>' + f"耗时: {execution_time} 秒" + '</td></tr>'
s = s + '</table>'
return s
else:
list_out.append(f"耗时:{execution_time} 秒")
dict_out={}
dict_out['time']= execution_time
dict_out['str']= ' '.join(str(x) for x in list_out)
dict_out['list']= list_out
return dict_out
# file.write(text+'\n')
# print('text:',text)
@app.route('/jpg_upload_file/', methods=['POST'])
def jpg_upload_file():
print('jpg_upload_file POST')
f = request.files.get('file')
print(f.filename)
f.save('./upload_csv/' + f.filename)
img = cv2.imread(f"./upload_csv/{f.filename}")
s_result=myocr(img,rotate=0,picsize=v_picsize,out='html')
# s_result = myocr(img, rotate=0, picsize=1, out='html')
return ({
'code': 0,
'msg': f'上传成功',
'html_str': s_result
})
@app.route('/scan_pig/', methods=[ 'GET','POST'])
def scan_pig():
print('scan_pig')
# myocr()
return render_template('jpg_upload.html')
def list_append_check(list=[],append_key1='loid',append_key2='check_ok'):
list_append=[]
for i in list:
dict_temp = {}
dict_temp[append_key2] = 0
dict_temp[append_key1] = i
list_append.append(dict_temp)
return list_append
def list_check_ok(list=[]):
if len(list)>0:
result_list1 = 0
result_list2 = 0
for i in list:
if i['check_ok'] == 1:
result_list1 = 1
if i['check_ok'] == 0:
result_list2 = 1
if result_list1 == 1 :
if result_list2 == 1:
result_list = 0.5
else:
result_list = 1
else:
result_list = 0
else:
result_list = 1
return result_list
@app.route('/send_base64/', methods=[ 'GET','POST'])
def send_base64():
print('route send_base64')
print(f"ID:{ request.headers['X-APP-ID']}")
print(f"KEY:{request.headers['X-APP-KEY']}")
if (request.headers['X-APP-ID']=='65a65cb38f7110bae954f1550ef70c4c') and (request.headers['X-APP-KEY']=='6de19a1e5813386a24225d6223d033eu') :
f=request.get_json()
# print(f['imageContent'])
list_image = f['list_image']
list_dict_result = []
list_code, list_loid, list_ywh = [], [], []
if f['dict_map']['need_match'] == 1: # =1时需要比对串码,loid等信息
list_code = list_append_check(f['dict_map']['list_code'],append_key1='devCtei',append_key2='check_ok')
list_loid = list_append_check(f['dict_map']['list_loid'],append_key1='loid',append_key2='check_ok')
list_ywh = list_append_check(f['dict_map']['list_ywh'],append_key1='ywh',append_key2='check_ok')
i_gmt = 0
for i in range(len(list_image)):
imgdata=base64.b64decode(list_image[i])
nparr = np.frombuffer(imgdata, np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
dict_result={}
if f['rotate']==1:
dict_result1 = myocr(img, rotate=0, picsize=f['picsize'], out='dict')
dict_result2 = myocr(img, rotate=1, picsize=f['picsize'], out='dict')
dict_result['list']=list(set(dict_result1['list'] + dict_result2['list'])) # 合并去重
dict_result['str'] = ' '.join(str(x) for x in dict_result['list'])
dict_result['time']=round(dict_result1['time'] + dict_result2['time'],2)
else:
dict_result = myocr(img, rotate=0, picsize=f['picsize'], out='dict')
if f['dict_map']['need_match'] == 1: #=1时需要比对串码,loid等信息
ocr_f = dict_result['str']
if ("全心全意" in ocr_f) | ("10分满意" in ocr_f) | ("宽带自助排障" in ocr_f) | ("10 分满意" in ocr_f):
i_gmt = 1
for dict_loid in list_loid:
if 'loid' in dict_loid.keys():
if dict_loid['check_ok'] == 0:
loid = dict_loid['loid']
if loid in ocr_f:
dict_loid['check_ok'] = 1
elif fuzz.partial_ratio(ocr_f, loid) > 62:
dict_loid['check_ok'] = 1
elif (fuzz.partial_ratio(ocr_f[0:190], loid) > 62):
dict_loid['check_ok'] = 1
elif (fuzz.partial_ratio(ocr_f[100:290], loid) > 62):
dict_loid['check_ok'] = 1
for dict_ywh in list_ywh:
if 'ywh' in dict_ywh.keys():
if dict_ywh['check_ok'] == 0:
ywh = dict_ywh['ywh']
if ywh in ocr_f:
dict_ywh['check_ok'] = 1
elif fuzz.partial_ratio(ocr_f, ywh) > 62:
dict_ywh['check_ok'] = 1
elif (fuzz.partial_ratio(ocr_f[0:190], ywh) > 62):
dict_ywh['check_ok'] = 1
elif (fuzz.partial_ratio(ocr_f[100:290], ywh) > 62):
dict_ywh['check_ok'] = 1
for element in list_code:
if 'devCtei' in element.keys():
if element["check_ok"] == 0: # 该串码需要比对该照片,如果=1表示前面已经比对过该串码,比对成功了不需要再比对该串码
devCtei = element["devCtei"]
if len(devCtei) > 22:
devCtei = devCtei[13:100]
if devCtei in ocr_f:
element["check_ok"] = 1 # 比对成功,设置为1
elif fuzz.partial_ratio(ocr_f, devCtei) > 55:
element["check_ok"] = 1 # 比对成功,设置为1
elif (fuzz.partial_ratio(ocr_f[0:190], devCtei) > 55):
element['check_ok'] = 1
elif (fuzz.partial_ratio(ocr_f[100:290], devCtei) > 55):
element['check_ok'] = 1
list_dict_result.append(dict_result)
dict_map_result = {}
if f['dict_map']['need_match'] == 1: # =1时需要比对串码,loid等信息
dict_temp={}
dict_temp['float_check_result'] =list_check_ok(list_code)
dict_temp['list_check_detail'] = list_code
dict_map_result['dict_code_result']=dict_temp
dict_temp = {}
dict_temp['float_check_result'] = list_check_ok(list_loid)
dict_temp['list_check_detail'] = list_loid
dict_map_result['dict_loid_result'] = dict_temp
dict_temp = {}
dict_temp['float_check_result'] = list_check_ok(list_ywh)
dict_temp['list_check_detail'] = list_ywh
dict_map_result['dict_ywh_result'] = dict_temp
dict_map_result['i_gmt'] = i_gmt
gc.collect()
return ({
'code': 0,
'msg': f'上传成功',
'data': list_dict_result,
'dict_map_result' : dict_map_result
})
else:
return ({
'code': -1,
'msg': f'APPID KEY 验证失败',
'data': []
})
if __name__ == '__main__':
app.run(debug=True)
pic0 = cv2.imread(f"./templates/big_img{v_yure}.jpg")
result0 = ocr.ocr(pic0, cls=True)
# print(f"./templates/big_img{v_yure}.jpg")
以下是对这段Python代码的分析:
1. 代码概述
这是一个使用Flask框架构建的Web应用程序,功能涉及用户认证、文件上传(CSV和图像文件)、图像文字识别(OCR)、数据评分以及基于OCR结果的信息比对等。
2. 导入模块
from jspt_data import jspt_cx_table,jspt_cx_table_sql等:从自定义模块jspt_data中导入数据相关的内容,虽然不清楚具体是什么,但可能是与数据库查询表或SQL语句相关。from flask_sqlalchemy import SQLAlchemy等:导入Flask相关的扩展和库,如SQLAlchemy用于数据库操作,Flask用于创建Web应用,render_template等用于渲染模板,flask_login相关的用于用户认证。import records:用于数据库查询操作,可能是一个更方便的数据库访问库。import cv2、from paddleocr import PaddleOCR等:用于图像处理(cv2)和光学字符识别(PaddleOCR)。import pandas as pd:用于数据处理,特别是读取和操作CSV文件。from fuzzywuzzy import fuzz等:用于模糊字符串匹配。
3. 配置和初始化
app = Flask(__name__):创建Flask应用实例。app.config设置:'TEMPLATES_AUTO_RELOAD'=True:自动重新加载模板,方便开发过程中修改模板后即时看到效果。'SECRET_KEY' = 'dirneciemedmdua;APE;D':用于加密会话等安全相关的密钥。'SQLALCHEMY_DATABASE_URI':设置数据库连接字符串,这里连接的是PostgreSQL数据库。'SQLALCHEMY_COMMIT_ON_TEARDOWN' = True:在每次请求结束后自动提交数据库事务。
login_manager初始化:login_manager.init_app(app):将LoginManager与Flask应用关联。login_manager.session_protection和login_manager.login_view设置:定义用户会话保护级别和未登录时重定向的登录视图。
4. 数据库模型 - User类
- 继承关系:继承自
UserMixin和db.Model,UserMixin提供了Flask - Login所需的用户方法,db.Model是SQLAlchemy的模型基类。 - 属性:
user_id、user_name、_password、user_role:分别表示用户ID、用户名、密码(密码存储的是哈希值)和用户角色。
- 方法:
password属性的getter和setter方法:getter返回密码哈希值,setter将原始密码转换为哈希值存储。check_password方法:用于验证输入的原始密码与存储的哈希密码是否匹配。get_id方法:返回用户ID,满足Flask - Login的要求。
5. 路由和视图函数
@app.route('/')-index函数:渲染jpg_upload.html模板,作为应用的根路径。@app.route('/jspt_upload_file/', methods=['POST'])-jspt_upload_file函数(需要登录):- 功能:处理用户上传的CSV文件,进行评分并保存上传记录和评分到数据库。包括检查上传时间和剩余上传次数,根据赛题类型(二分类或多分类)计算评分(目前评分固定为95,原代码中可能使用
f1_score计算)。 - 数据库操作:连接数据库,查询相关设置信息,如上传次数限制和赛题结束时间,执行插入和删除等操作。
- 功能:处理用户上传的CSV文件,进行评分并保存上传记录和评分到数据库。包括检查上传时间和剩余上传次数,根据赛题类型(二分类或多分类)计算评分(目前评分固定为95,原代码中可能使用
@app.route('/jpg_upload_file/', methods=['POST'])-jpg_upload_file函数:- 功能:处理用户上传的图像文件(JPG),调用
myocr函数进行OCR识别,并返回识别结果。
- 功能:处理用户上传的图像文件(JPG),调用
@app.route('/scan_pig/', methods=['GET', 'POST'])-scan_pig函数:目前只是渲染jpg_upload.html模板,可能功能未完全实现。@app.route('/send_base64/', methods=['GET', 'POST'])-send_base64函数:- 功能:接收包含图像Base64编码数据、相关配置信息(如是否需要匹配、旋转设置、图像尺寸等)的JSON请求。验证请求头中的
APPID和APPKEY,对图像进行OCR识别,并根据配置进行信息比对(如串码、loid、ywh等信息),最后返回结果,包括OCR结果和比对结果。
- 功能:接收包含图像Base64编码数据、相关配置信息(如是否需要匹配、旋转设置、图像尺寸等)的JSON请求。验证请求头中的
6. 辅助函数
rotate_image函数:根据给定角度旋转图像。myocr函数:对图像进行OCR操作,可选择旋转图像、调整图像大小,根据输出格式(html或dict)返回不同形式的结果,同时计算并返回执行时间。list_append_check函数:创建一个包含指定键值对的字典列表,用于初始化比对信息的状态。list_check_ok函数:根据列表中字典的check_ok值判断整体的检查结果。
7. 主程序入口
if __name__ == '__main__':块:运行Flask应用,开启调试模式。同时读取一个大图像文件并进行OCR操作(这部分可能是测试代码)。
8. 代码改进建议
- 错误处理和日志记录:在数据库操作、文件读取和OCR等可能出错的地方添加更完善的错误处理和日志记录,方便排查问题。
- 安全性增强:对于接收的文件和请求数据进行更严格的安全检查,防止潜在的安全漏洞,如文件类型检查、输入数据的合法性验证等。
- 代码优化:减少代码中的重复逻辑,例如
jspt_upload_file和send_base64中都有部分OCR和结果处理逻辑,可以考虑提取成更通用的函数。同时,可以优化数据库查询语句,避免不必要的查询和数据处理。
更多推荐



所有评论(0)