You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将图像机器学习模型部署到Flask应用时遭遇UnboundLocalError问题求助

Fixing UnboundLocalError in Your Flask Image Prediction App

这个错误的根源很明确:在某些场景下,代码里的labels变量还没被赋值就被引用了。在Jupyter里测试正常是因为你用的图片都能检测到有效轮廓,但部署到Flask后,用户上传的图片可能不符合预期(比如没有数字区域、轮廓太小等),触发了代码里的边缘情况。

具体原因分析

当上传的图片满足以下任一情况时,会导致labels未定义:

  1. 图片中没有检测到任何轮廓(cnts为空列表),此时计算avgcntarea会得到nan,后续循环不会向digits添加任何元素;
  2. 所有检测到的轮廓面积都小于阈值(cv2.contourArea(c)<avgcntarea/10),最终digits还是空数组;
  3. 空的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 07:27:43