在计算机视觉领域,裂缝检测是一项基础且重要的任务。之前我们已经掌握了使用OpenCV进行图像读取、预处理、自适应阈值和形态学操作等核心功能。今天,我们将把这些功能封装成一个轻量级的Web应用——使用Gradio框架,让检测结果从PyCharm的控制台跃升到浏览器界面,实现更直观、更吸引人的可视化交互。

Gradio简介:Python轻量级Web框架

Gradio是一个专为机器学习模型和Python函数设计的轻量级Web框架。与JavaScript、Java、C++等语言构建的复杂Web应用不同,Gradio可以让你用短短几行Python代码就创建一个可交互的网页。它有两种核心模式:

  • gr.Blocks:功能更强大,支持自定义页面布局和排版,适合需要复杂界面的场景。我们本次将使用这种模式。
  • gr.Interface:更简单,适合快速原型开发,但灵活性较低。

Gradio提供了丰富的UI组件,让我们可以像搭积木一样构建Web界面。以下是几个常用组件:

  • gr.Markdown:用于显示标题、说明文字等富文本内容。
  • gr.Textbox:既可输入也可输出,常用于显示检测报告等文本结果。
  • gr.Row:水平排列组件,配合with语句使用,实现横向布局。
  • gr.Column:垂直排列组件,实现纵向布局。
  • gr.Image:用于输入或输出图片,是裂缝检测的核心组件。
  • gr.Button:触发操作的按钮,比如“检测”按钮。

环境搭建:一行命令搞定

在开始编码之前,我们需要安装必要的Python库。打开终端,执行以下命令:

pip install gradio opencv-python numpy

如果你在国内,使用清华镜像源可以加速下载:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple gradio opencv-python numpy

✅ 安装完成后,我们就可以开始编写代码了。值得注意的是,Gradio支持Python 3.7及以上版本,建议使用最新稳定版。此外,如果你熟悉TypeScript或其他前端语言,会发现Gradio的组件设计理念与之类似——都是基于事件驱动的响应式编程。

核心代码解析:构建检测界面

让我们先来看一下创建Gradio应用的核心代码结构。首先,我们使用with gr.Blocks(title = ...) as demo创建一个Blocks容器。这里的title参数就是网页标题栏显示的文字,demo是这个容器的变量名。

with

上下文管理器:Python的with语句会自动处理资源的打开和关闭,确保应用运行稳定。

接下来,我们定义输入图片组件:

input_img = gr.Image(label = "上传裂缝图片", type = "numpy")

这里type = "numpy"表示图片将以NumPy数组的形式传递给处理函数。Gradio支持多种图片类型,包括文件路径、PIL Image等。下表总结了常用参数:

上传后转成 NumPy 数组因为OpenCV 处理需要 NumPy 格式

按钮组件可以设置样式,例如variant = "primary"可以让按钮高亮显示。Gradio提供了多种variant选项:

最关键的部分是按钮的点击事件绑定——detect_btn.click(),它连接了前端交互和后端逻辑:

# ============ 创建界面 ============
with gr.Blocks(title = "AI裂缝检测系统") as demo:
    gr.Markdown("AI裂缝检测系统")
    gr.Markdown("上传裂缝照片,自动检测裂并计算长度、宽度")
    with gr.Row():
        with gr.Column():
            input_img = gr.Image(label = "上传裂缝图片" , type = "numpy")
            detect_btn = gr.Button("开始检测" , variant = "primary")
        with gr.Column():
            output_img = gr.Image(label = "检测结果")
            output_text = gr.Textbox(label = "检测报告" , lines = 10)
    detect_btn.click(
        fn = detect_crack,
        inputs = input_img,
        outputs = [output_img , output_text]
    )
    gr.Markdown("====")
    gr.Markdown(
        "作者 : 棒 棒 | 学校 : 索菲亚大学 | Github : [crack-detector](https://github.com/12345678875/crack-detector)"
    )

为了帮助你更直观地理解整个流程,我让AI生成了一张整体代码流程图:

裂缝检测算法实现

裂缝检测的核心算法我们已经在前两篇文章中详细介绍过,这里只做简要回顾。整个流程包括:读取图片 → 灰度化 → 高斯模糊去噪 → Canny边缘检测 → 自适应阈值处理 → 形态学操作(开闭运算)→ 裂缝轮廓提取 → 计算裂缝面积、长度等指标 → 在原图上绘制结果并展示。

以下是核心检测函数的代码:

# # ============ 裂缝检测函数 ============
def detect_crack(image):
    if image is None:
        return None,"wrong , can not read the picture"
#gray->blurred->adaptive->kernel->close
    gray = cv2.cvtColor(image , cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray , (5 , 5) , 0)
    edges = cv2.Canny(blurred , 80 , 200)
    adaptive = cv2.adaptiveThreshold(
        edges , 255,
        cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
        cv2.THRESH_BINARY_INV,
        ADAPTIVE_BLOCK_SIZE,
        ADAPTIVE_C
    )
#=========形态学操作这里还是有点不太熟======
    kernel = np.ones((MORPH_KERNEL_SIZE , MORPH_KERNEL_SIZE ), np.uint8)
    closed = cv2.morphologyEx(adaptive , cv2.MORPH_CLOSE, kernel , iterations = MORPH_ITERATIONS)
    contours , _ = cv2.findContours(closed , cv2.RETR_EXTERNAL , cv2.CHAIN_APPROX_SIMPLE)
##========计算+画图========
    img_copy = image.copy()
    crack_count = 0
    report_lines = []
    for i , contour in enumerate(contours):
        area = cv2.contourArea(contour)
        if area > MIN_AREA:
            length = cv2.arcLength(contour , True)
            length_mm = length / PIXELS_PER_MM
            width = area / (length / 2)
            width_mm = width / PIXELS_PER_MM
            report_lines.append(f"裂缝{crack_count}: 长度={length_mm:.2f}mm, 宽度={width_mm:.2f}mm")
            #画轮廓
            cv2.drawContours(img_copy , [contour] , -1 ,(0 , 255 , 0) , 2)
            crack_count += 1
    if crack_count == 0:
        report = "未检测到有效裂缝(面积 < 100 像素²)"
    else:
        report = f"共检测到 {crack_count} 条裂缝"
        report += " ".join(report_lines)
        report += f"\n检测时间:{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
        report += f"标定参数:{PIXELS_PER_MM}像素 / mm(需根据实际标定调整)"
        return img_copy,report

⚠️ 注意事项

  • 自适应阈值参数blockSizeC需要根据实际图片调整,建议先在小样本上测试。
  • 形态学操作的内核大小(kernel)也会影响检测精度,过大会丢失细节,过小会引入噪声。
  • 如果检测效果不理想,可以尝试调整Canny算子的高低阈值。

完整代码整合

将上述所有部分整合在一起,就得到了完整的裂缝检测Web应用代码:

import gradio as gr
import cv2
import numpy as np
import datetime
import os
# # ============ 配置参数 ============
PIXELS_PER_MM = 12
MIN_AREA = 100
ADAPTIVE_BLOCK_SIZE = 11
ADAPTIVE_C = 2
MORPH_KERNEL_SIZE = 3
MORPH_ITERATIONS = 5
# # ============ 裂缝检测函数 ============
def detect_crack(image):
    if image is None:
        return None,"wrong , can not read the picture"
#gray->blurred->adaptive->kernel->close
    gray = cv2.cvtColor(image , cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray , (5 , 5) , 0)
    edges = cv2.Canny(blurred , 80 , 200)
    adaptive = cv2.adaptiveThreshold(
        edges , 255,
        cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
        cv2.THRESH_BINARY_INV,
        ADAPTIVE_BLOCK_SIZE,
        ADAPTIVE_C
    )
#=========形态学操作这里还是有点不太熟======
    kernel = np.ones((MORPH_KERNEL_SIZE , MORPH_KERNEL_SIZE ), np.uint8)
    closed = cv2.morphologyEx(adaptive , cv2.MORPH_CLOSE, kernel , iterations = MORPH_ITERATIONS)
    contours , _ = cv2.findContours(closed , cv2.RETR_EXTERNAL , cv2.CHAIN_APPROX_SIMPLE)
##========计算+画图========
    img_copy = image.copy()
    crack_count = 0
    report_lines = []
    for i , contour in enumerate(contours):
        area = cv2.contourArea(contour)
        if area > MIN_AREA:
            length = cv2.arcLength(contour , True)
            length_mm = length / PIXELS_PER_MM
            width = area / (length / 2)
            width_mm = width / PIXELS_PER_MM
            report_lines.append(f"裂缝{crack_count}: 长度={length_mm:.2f}mm, 宽度={width_mm:.2f}mm")
            #画轮廓
            cv2.drawContours(img_copy , [contour] , -1 ,(0 , 255 , 0) , 2)
            crack_count += 1
    if crack_count == 0:
        report = "未检测到有效裂缝(面积 < 100 像素²)"
    else:
        report = f"共检测到 {crack_count} 条裂缝"
        report += " ".join(report_lines)
        report += f"\n检测时间:{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
        report += f"标定参数:{PIXELS_PER_MM}像素 / mm(需根据实际标定调整)"
        return img_copy,report
# ============ 创建界面 ============
with gr.Blocks(title = "AI裂缝检测系统") as demo:
    gr.Markdown("AI裂缝检测系统")
    gr.Markdown("上传裂缝照片,自动检测裂并计算长度、宽度")
    with gr.Row():
        with gr.Column():
            input_img = gr.Image(label = "上传裂缝图片" , type = "numpy")
            detect_btn = gr.Button("开始检测" , variant = "primary")
        with gr.Column():
            output_img = gr.Image(label = "检测结果")
            output_text = gr.Textbox(label = "检测报告" , lines = 10)
    detect_btn.click(
        fn = detect_crack,
        inputs = input_img,
        outputs = [output_img , output_text]
    )
    gr.Markdown("====")
    gr.Markdown(
        "作者 : 棒 棒 | 学校 : 索菲亚大学 | Github : [crack-detector](https://github.com/12345678875/crack-detector)"
    )
# ============ 启动应用 ============
if __name__ == "__main__":
    demo.launch()

[AFFILIATE_SLOT_1]

运行这段代码后,你会在控制台看到类似如下的输出:

Running on local URL:  http://127.0.0.1:7860

打开浏览器访问该地址,即可看到我们的裂缝检测界面。上传一张裂缝图片,点击“检测裂缝”按钮,等待几秒钟,右侧就会显示处理后的结果,包括裂缝轮廓、面积占比等数据。

运行效果展示

下面是在本地测试的运行截图,展示了上传图片和检测结果:

从截图可以看出,Gradio界面简洁直观,左侧是输入区域,右侧是输出区域。检测结果不仅显示了裂缝的轮廓,还以文字形式输出了裂缝面积和长度,便于后续分析。

[AFFILIATE_SLOT_2]

总结与进阶

本文详细介绍了如何将OpenCV裂缝检测算法封装为Gradio Web应用。通过使用gr.Blocks模式,我们实现了自定义布局;通过gr.Imagegr.Button等组件,构建了完整的用户交互界面。整个过程只需几十行Python代码,无需任何前端知识(JavaScript、TypeScript等)。如果你希望进一步优化,可以考虑:

  • 添加批量处理功能,支持多张图片同时检测。
  • 集成更先进的深度学习模型(如U-Net)提升检测精度。
  • 部署到云服务器(如Hugging Face Spaces)实现公网访问。

希望本文能帮助你快速掌握Gradio的应用技巧,将你的计算机视觉项目从本地脚本升级为可分享的Web应用!

type="numpy"