将图像机器学习模型部署到Flask应用时遭遇UnboundLocalError问题求助
Fixing UnboundLocalError in Your Flask Image Prediction App
这个错误的根源很明确:在某些场景下,代码里的labels变量还没被赋值就被引用了。在Jupyter里测试正常是因为你用的图片都能检测到有效轮廓,但部署到Flask后,用户上传的图片可能不符合预期(比如没有数字区域、轮廓太小等),触发了代码里的边缘情况。
具体原因分析
当上传的图片满足以下任一情况时,会导致labels未定义:
- 图片中没有检测到任何轮廓(
cnts为空列表),此时计算avgcntarea会得到nan,后续循环不会向digits添加任何元素; - 所有检测到的轮廓面积都小于阈值(
cv2.contourArea(c)<avgcntarea/10),最终digits还是空数组; - 空的
digits数组传入模型的predict_classes方法时,抛出异常导致labels = ...这一行执行失败,变量未被赋值。
修复方案
我们需要在代码中添加边缘情况的处理,确保labels始终被定义,或者提前终止流程返回合理结果:
1. 修改predict函数,添加空值检查
def predict(file): images = cv2.imread(file) images = imutils.resize(images,width=320) gray = cv2.cvtColor(images,cv2.COLOR_BGR2GRAY) kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5)) blackhat = cv2.morphologyEx(gray,cv2.MORPH_BLACKHAT,kernel) _,thresh = cv2.threshold(blackhat,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU) thresh = cv2.dilate(thresh,None) (cnts,_) = cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) # 新增:检测到轮廓为空时直接返回空列表 if not cnts: return [] avgcntarea = np.mean([cv2.contourArea(k) for k in cnts]) digits = [] boxes = [] for (i,c) in enumerate(cnts): if cv2.contourArea(c)<avgcntarea/10: continue mask = np.zeros(gray.shape,dtype="uint8") (x,y,w,h) = cv2.boundingRect(c) hull = cv2.convexHull(c) cv2.drawContours(mask,[hull],-1,255,-1) mask = cv2.bitwise_and(thresh,thresh,mask=mask) digit = mask[y-8:y+h+8,x-8:x+w+8] digit = cv2.resize(digit,(28,28)) boxes.append((x,y,w,h)) digits.append(digit) # 新增:没有有效数字区域时返回空列表 if not digits: return [] digits = np.array(digits) digits = digits.reshape(digits.shape[0],28,28,1) # 注意:predict_classes在新版本Keras中已弃用,建议替换为以下写法 # labels = classifier.predict(digits).argmax(axis=1) labels = classifier.predict_classes(digits) lists = [] for (x,y,w,h),label in sorted(zip(boxes,labels)): cv2.rectangle(images,(x,y),(x+w,y+h),(0,0,255),1) cv2.putText(images,str(label),(x+2,y-5),cv2.FONT_HERSHEY_SIMPLEX,1.0,(0,255,0),2) lists.append(label) return lists
2. 在Flask路由中处理空预测结果
修改upload_predict函数,当output为空时,给用户显示友好提示:
@app.route("/",methods=["GET","POST"]) def upload_predict(): if request.method == "POST": image_file = request.files["image"] filename = image_file.filename if filename == '': newerr = " File is empty" return render_template("index.html",error=newerr) if allowed_files(filename) == False: newerr = " upload error : this file format is not allowed" return render_template("index.html",error=newerr) if image_file: image_location = os.path.join( UPLOAD_FOLDER, image_file.filename ) image_file.save(image_location) output = predict(image_location) # 新增:处理空预测结果 if not output: newerr = "No digits detected in the image" return render_template("index.html",error=newerr) return render_template("index.html",prediction=output) return render_template("index.html",prediction=0)
额外建议
- 检查你的Keras/TensorFlow版本:
predict_classes在TensorFlow 2.6+中已被移除,建议替换为classifier.predict(digits).argmax(axis=1),避免部署环境版本不兼容的问题; - 在Jupyter中测试一些极端情况(比如空白图片、没有数字的图片),确保代码在这些场景下也能正常运行。
内容的提问来源于stack exchange,提问作者afnan
相关产品推荐
相关产品推荐

