前端开发笔记[4]-Gradio搭建演示页面

发布时间 2023-11-04 16:37:37作者: qsBye

摘要

使用Gradio在Python环境搭建图像处理和视频处理的演示网页.

Gradio简介

[https://www.gradio.app]
Gradio是一个开源的Python库,用于构建机器学习和数据科学演示应用。它可以帮助AI算法工程师将训练的模型分享给大众,并赋予模型与用户交互的能力。使用Gradio,你可以通过几行代码快速创建一个简单漂亮的用户界面,围绕你的机器学习模型或数据科学工作流程。Gradio支持多种输入输出格式,如图像分类中的图像到标签、超分辨率中的图像到图像等。它还可以生成可外部访问的链接,方便你与朋友、同事分享你的算法。

Gradio的优势包括自动生成页面且可交互、改动几行代码就能完成、支持自定义多种输入输出、支持生成可外部访问的链接进行分享。它已经被许多优秀的开源项目使用来构建演示页面。使用Gradio的步骤包括安装Gradio库和编写相应的代码。

总之,Gradio是一个方便快捷的工具,可以帮助你将机器学习模型转化为交互式的Web应用,使其更易于分享和使用。

GradioLite简介

[https://www.gradio.app/playground]

  • 可以无需网页服务器,直接运行在浏览器
    All the demos on this page are interactive - meaning you can change the code and the embedded demo will update automatically. Use this as a space to explore and play around with Gradio. This is made possible thanks to the Gradio Lite package.
    GradioLite是一个在浏览器中完全运行的无服务器Gradio。它是Gradio的轻量级版本,旨在提供简单易用的界面来展示和测试机器学习模型,而无需搭建服务器。GradioLite允许用户在本地环境中加载和运行模型,以进行实时的交互式预测。

使用GradioLite,您可以通过简单的代码将模型与用户界面进行绑定,从而创建一个交互式的应用程序。您可以定义输入和输出的类型,并为它们提供适当的界面元素,如文本框、滑块、下拉菜单等。然后,您可以使用GradioLite的预测函数将输入传递给模型,并将输出显示给用户。

GradioLite的主要优点是它的易用性和轻量级性质。它不需要复杂的服务器设置,可以在本地环境中快速部署和运行。此外,GradioLite支持多种输入和输出类型,包括文本、图像、音频和视频,使其适用于各种机器学习任务。

Gradio的两种布局方式:Interface和Block

Gradio提供了两种布局方式:Interface(界面)和Block(块)。

Interface是Gradio的高级抽象API,它提供了一种简单的方式来创建交互式的Web应用和演示。使用Interface,您可以通过定义输入和输出的类型来快速构建一个应用程序,并将其展示给用户。Interface的布局是自动排列的,它会根据输入和输出的类型自动创建适当的界面元素,如文本框、滑块等。这种布局方式适用于简单的场景,可以快速搭建一个交互页面。

Block是Gradio的低级API,它提供了更灵活和可定制的布局选项,使用户能够创建更复杂的Web应用和演示。使用Block,您可以自由地设计和排列组件,创建自定义的布局。Block提供了多种布局元素,如Row(行布局)、Column(列布局)、Tab(标签页布局)等,您可以根据需要选择合适的布局方式。这种布局方式适用于需要更多灵活性和控制的场景。

总结起来,Interface适用于简单的场景,提供了自动排列的布局方式;而Block适用于更复杂的场景,提供了更灵活和可定制的布局选项。

Gradio内嵌CSS和JS

[https://www.gradio.app/guides/custom-CSS-and-JS]
You can elem_id to add an HTML element id to any component, and elem_classes to add a class or list of classes. This will allow you to select elements more easily with CSS. This approach is also more likely to be stable across Gradio versions as built-in class names or ids may change (however, as mentioned in the warning above, we cannot guarantee complete compatibility between Gradio versions if you use custom CSS as the DOM elements may themselves change).
Event listeners have a _js argument that can take a Javascript function as a string and treat it just like a Python event listener function. You can pass both a Javascript function and a Python function (in which case the Javascript function is run first) or only Javascript (and set the Python fn to None).

实现

Gradio网页服务器

import cv2
import gradio
import random
import numpy as np
import os
import subprocess
import time
import shutil
from PIL import Image

##################start 功能函数##################
def fn_gray(image):
    '''
    图片转灰度函数
    '''
    output = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    return output

def fn_binary(image):
    '''
    将图像转换为二值图函数
    '''
    gray_image = fn_gray(image)
    _, binary_image = cv2.threshold(gray_image, 128, 255, cv2.THRESH_BINARY)
    return binary_image

def fn_salt_and_pepper_noise(image, prob=random.randint(1,100)):
    '''
    添加椒盐噪声函数
    参数:
    - image: 输入的图像
    - prob: 噪声概率,即每个像素被噪声影响的概率
    返回:
    - noisy_image: 添加了椒盐噪声的图像
    '''
    noisy_image = np.copy(image)
    h, w = noisy_image.shape[:2]
    num_noise_pixels = int(prob * h * w)
    
    # 随机选择像素位置并添加噪声
    for _ in range(num_noise_pixels):
        x = np.random.randint(0, w)
        y = np.random.randint(0, h)
        noisy_image[y, x] = 0 if np.random.rand() < 0.5 else 255
    
    return noisy_image

def fn_remove_noise(image, kernel_size=random.randint(1,100)):
    '''
    去除噪声函数
    参数:
    - image: 输入的图像
    - kernel_size: 平滑滤波器的大小
    返回:
    - denoised_image: 去除噪声后的图像
    '''
    denoised_image = cv2.medianBlur(image, kernel_size)
    return denoised_image

def fn_open_operation(image, kernel_size=random.randint(1,100)):
    '''
    开运算函数
    参数:
    - image: 输入的图像
    - kernel_size: 开运算的核大小
    返回:
    - opened_image: 开运算后的图像
    '''
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, kernel_size)
    opened_image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)
    return opened_image

def fn_close_operation(image: np.ndarray, kernel_size: int = 3) -> np.ndarray:
    '''
    闭运算函数
    参数:
    - image: 输入的图像
    - kernel_size: 卷积核大小,默认为3
    返回:
    - closed_image: 闭运算后的图像
    '''
    kernel = np.ones((kernel_size, kernel_size), np.uint8)
    closed_image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)
    return closed_image

def fn_connected_domains(image: np.ndarray) -> np.ndarray:
    '''
    连通域函数
    参数:
    - image: 输入的二值图像
    返回:
    - labeled_image: 标记了连通域的图像
    - num_labels: 连通域的数量
    '''
    _, labeled_image = cv2.connectedComponents(image)
    num_labels = np.max(labeled_image)
    return labeled_image

def fn_erosion(image: np.ndarray, kernel_size: int = 3) -> np.ndarray:
    '''
    腐蚀函数
    参数:
    - image: 输入的图像
    - kernel_size: 卷积核大小,默认为3
    返回:
    - eroded_image: 腐蚀后的图像
    '''
    kernel = np.ones((kernel_size, kernel_size), np.uint8)
    eroded_image = cv2.erode(image, kernel, iterations=1)
    return eroded_image

def fn_dilation(image: np.ndarray, kernel_size: int = 3) -> np.ndarray:
    '''
    膨胀函数
    参数:
    - image: 输入的图像
    - kernel_size: 卷积核大小,默认为3
    返回:
    - dilated_image: 膨胀后的图像
    '''
    kernel = np.ones((kernel_size, kernel_size), np.uint8)
    dilated_image = cv2.dilate(image, kernel, iterations=1)
    return dilated_image

def fn_watermark(image: np.ndarray, watermark: np.ndarray = [[1,0],[0,1]]) -> np.ndarray:
    '''
    添加水印函数
    参数:
    - image: 输入的图像
    - watermark: 水印图像
    返回:
    - watermarked_image: 添加水印后的图像
    '''
    watermarked_image = cv2.addWeighted(image, 1, watermark, 0.5, 0)
    return watermarked_image

def fn_gaussian_filter(image: np.ndarray, kernel_size: int = 3, sigma: float = 0) -> np.ndarray:
    '''
    高斯滤波函数
    参数:
    - image: 输入的图像
    - kernel_size: 卷积核大小,默认为3
    - sigma: 高斯核标准差,默认为0
    返回:
    - filtered_image: 滤波后的图像
    '''
    filtered_image = cv2.GaussianBlur(image, (kernel_size, kernel_size), sigma)
    return filtered_image

def fn_median_filter(image: np.ndarray, kernel_size: int = 3) -> np.ndarray:
    '''
    中值滤波函数
    参数:
    - image: 输入的图像
    - kernel_size: 卷积核大小,默认为3
    返回:
    - filtered_image: 滤波后的图像
    '''
    filtered_image = cv2.medianBlur(image, kernel_size)
    return filtered_image

def fn_mean_filter(image: np.ndarray, kernel_size: int = 3) -> np.ndarray:
    '''
    均值滤波函数
    参数:
    - image: 输入的图像
    - kernel_size: 卷积核大小,默认为3
    返回:
    - filtered_image: 滤波后的图像
    '''
    kernel = np.ones((kernel_size, kernel_size), np.float32) / (kernel_size * kernel_size)
    filtered_image = cv2.filter2D(image, -1, kernel)
    return filtered_image

def fn_gray_mapping(image: np.ndarray) -> np.ndarray:
    '''
    灰度映射函数
    参数:
    - image: 输入的图像
    - mapping_func: 灰度映射函数
    返回:
    - mapped_image: 映射后的图像
    '''
    mapped_image = fn_binary(image)
    return mapped_image

def fn_image_enhancement(image: np.ndarray) -> np.ndarray:
    '''
    图像增强函数
    参数:
    - image: 输入的图像
    - enhancement_func: 图像增强函数
    返回:
    - enhanced_image: 增强后的图像
    '''
    #enhanced_image = enhancement_func(image)
    return image

def fn_walsh_transform(signal: np.ndarray) -> np.ndarray:
    '''
    Walsh变换函数
    参数:
    - signal: 输入的信号
    返回:
    - transformed_signal: 变换后的信号
    '''
    transformed_signal = np.fft.ifftshift(np.fft.fft(signal))
    return transformed_signal

def fn_hadamard_transform(signal: np.ndarray) -> np.ndarray:
    '''
    Hadamard变换函数
    参数:
    - signal: 输入的信号
    返回:
    - transformed_signal: 变换后的信号
    '''
    transformed_signal = np.fft.ifftshift(np.fft.fft(signal))
    return transformed_signal

def fn_1d_dct(signal: np.ndarray) -> np.ndarray:
    '''
    一维离散余弦变换函数
    参数:
    - signal: 输入的信号
    返回:
    - transformed_signal: 变换后的信号
    '''
    transformed_signal = np.fft.ifftshift(np.fft.fft(signal))
    return transformed_signal

def fn_2d_dct(image: np.ndarray) -> np.ndarray:
    '''
    二维离散余弦变换函数
    参数:
    - image: 输入的图像
    返回:
    - transformed_image: 变换后的图像
    '''
    transformed_image = cv2.dct(image)
    return transformed_image

def fn_2d_continuous_fourier_transform(image: np.ndarray) -> np.ndarray:
    '''
    二维连续傅里叶变换函数
    参数:
    - image: 输入的图像
    返回:
    - transformed_image: 变换后的图像
    '''
    transformed_image = np.fft.fft2(image)
    return transformed_image

def fn_2d_discrete_fourier_transform(image: np.ndarray) -> np.ndarray:
    '''
    二维离散傅里叶变换函数
    参数:
    - image: 输入的图像
    返回:
    - transformed_image: 变换后的图像
    '''
    transformed_image = np.fft.fftshift(np.fft.fft2(image))
    return transformed_image

def fn_continuous_wavelet_transform(signal: np.ndarray) -> np.ndarray:
    '''
    连续小波变换函数
    参数:
    - signal: 输入的信号
    - wavelet: 小波函数
    返回:
    - transformed_signal: 变换后的信号
    '''
    #transformed_signal = pywt.cwt(signal, wavelet)
    return signal

def fn_1d_discrete_wavelet_transform(signal: np.ndarray) -> np.ndarray:
    '''
    一维离散小波变换函数
    参数:
    - signal: 输入的信号
    - wavelet: 小波函数
    返回:
    - transformed_signal: 变换后的信号
    '''
    #transformed_signal = pywt.wavedec(signal, wavelet)
    return signal

def fn_2d_discrete_wavelet_transform(image: np.ndarray) -> np.ndarray:
    '''
    二维离散小波变换函数
    参数:
    - image: 输入的图像
    - wavelet: 小波函数
    返回:
    - transformed_image: 变换后的图像
    '''
    #transformed_image = pywt.wavedec2(image, wavelet)
    return image

def fn_image_skeletonization(image: np.ndarray) -> np.ndarray:
    '''
    图像骨架化函数
    参数:
    - image: 输入的二值图像
    返回:
    - skeletonized_image: 骨架化后的图像
    '''
    skeletonized_image = cv2.ximgproc.thinning(image)
    return skeletonized_image

# 打开摄像头并显示视频
def fn_open_webcam(is_open:bool=True):
    '''
    打开摄像头画面
    '''
    global before_video_webcam
    global before_video
    before_video_webcam.visible = True
    before_video.source="webcam"

# 关闭摄像头画面
def fn_close_webcam(is_open:bool=False):
    '''
    关闭摄像头画面
    '''
    global before_video_webcam
    global before_video
    before_video_webcam.visible=False

# 获取摄像头当前帧并显示
def fn_screenshot_webcam(image):
    '''
    截图摄像头
    '''
    output = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    file_name = "screenshot_{}.jpg".format(random.randint(100,200))
    cv2.imwrite(file_name, output)
    return image,image

# 获取视频第五帧并显示
def fn_screenshot_frame_5(video):
    '''
    截图视频
    '''
    _video = cv2.VideoCapture(video)
    print("video类型:{}".format(type(video)))
    print(video)
    # 读取并丢弃前四帧
    for _ in range(4):
        _video.read()
    # 读取第五帧
    ret, image = _video.read()

    output = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    file_name = "screenshot_{}.jpg".format(random.randint(100,200))
    cv2.imwrite(file_name, image)
    return output,output

# 获取视频当前帧并显示
def fn_screenshot(video):
    '''
    截图视频
    '''
    _video = cv2.VideoCapture(video)
    print("video类型:{}".format(type(video)))
    print(video)
    # 读取并丢弃前n帧
    for _ in range(random.randint(1,200)):
        _video.read()
    # 读取第n+1帧
    ret, image = _video.read()

    output = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    file_name = "screenshot_{}.jpg".format(random.randint(100,200))
    cv2.imwrite(file_name, image)
    return output,output

# 随机剪辑视频
def fn_save_video_random(video_path):
    '''
    随机剪辑视频
    '''
    output_path = f'video_shotcut_{random.randint(100,200)}.mp4'
    shell_command = f'ffmpeg -i "{video_path}" -filter_complex "[0:v]split=2[top][bottom];[bottom]crop=iw:ih/2:0:0[bottom1];[top]crop=iw:ih/2:0:ih/2[top1];[top1][bottom1]vstack" -c:a copy "{output_path}"'
    subprocess.call(shell_command,shell=True)

    return output_path

# 视频添加水印
def fn_add_watermark_video(video_path):
    '''
    给视频添加水印
    '''
    output_path = f'video_watermark_{random.randint(100,200)}.mp4'
    watermark_text = f'qsbye_{random.randint(100,200)}'
    shell_command = f'ffmpeg -i "{video_path}" -vf "drawtext=text=\'{watermark_text}\':x=w-tw-10:y=10:fontsize=24:fontcolor=white:shadowcolor=black:shadowx=2:shadowy=2" -c:a copy "{output_path}"'
    subprocess.call(shell_command, shell=True)
    return output_path

# 视频倒放
def fn_video_upend(video_path):
    '''
    视频倒放
    '''
    output_path = f'video_upend_{random.randint(100,200)}.mp4'
    shell_command = f'ffmpeg -i "{video_path}" -vf "reverse" -af "areverse" "{output_path}"'
    subprocess.call(shell_command, shell=True)
    return output_path

# 保存3s摄像头视频
def fn_save_video_webcam(input_image):
    '''
    保存3s摄像头视频到文件
    输出:视频路径
    '''
    cap = cv2.VideoCapture(0)
    temp_folder = "temp"
    if not os.path.exists(temp_folder):
        os.makedirs(temp_folder)
    
    i = 0
    start_time = time.time()
    while time.time() - start_time < 3:
        image_path = os.path.join(temp_folder, f'image_{i}.jpg')
        #pil_image = Image.fromarray(np.uint8(input_image))
        #pil_image.save(image_path)
        ret, frame = cap.read()
        if ret:
            cv2.imwrite(image_path, frame)
        i += 1
        time.sleep(0.1)  # 暂停0.1秒钟

    cap.release()

    output_path = f'video_{random.randint(100,200)}.mp4'
    shell_command = f'ffmpeg -f image2 -r 1/3 -i {temp_folder}/image_%d.jpg -vf "fps=10" -y {output_path}'
    subprocess.call(shell_command, shell=True)
    
    shutil.rmtree(temp_folder)
    
    return output_path


##################end 功能函数##################

##################start 界面构建##################

# 示例图片
examples_imgs = [
    ["/Users/workspace/Desktop/2023课程归档/大三-上学期-第二周/数字图像处理实验/exp9/exp9-6/imgs/pikachu.jpeg"],
    ["/Users/workspace/Desktop/2023课程归档/大三-上学期-第二周/数字图像处理实验/exp9/exp9-6/imgs/leaf3.jpg"],
    ["/Users/workspace/Desktop/2023课程归档/大三-上学期-第二周/数字图像处理实验/exp9/exp9-6/imgs/qrcode2.png"],
    ["/Users/workspace/Desktop/2023课程归档/大三-上学期-第二周/数字图像处理实验/exp9/exp9-6/imgs/qrcode3.png"],
    ["/Users/workspace/Desktop/2023课程归档/大三-上学期-第二周/数字图像处理实验/exp9/exp9-6/imgs/shape4.bmp"],
    ["/Users/workspace/Desktop/2023课程归档/大三-上学期-第二周/数字图像处理实验/exp9/exp9-6/imgs/shapes.bmp"]
]

# 示例视频
examples_videos = [
    ["/Users/workspace/Desktop/2023课程归档/大三-上学期-第二周/数字图像处理实验/exp9/city_car_stream.mp4"],
]

# 构建界面Blocks上下文
with gradio.Blocks() as demo:
    gradio.Markdown("# 图像视频/处理")
    gradio.Markdown("## 图像处理")
    
    # 原图预览和处理后图像预览
    # 纵向排列
    with gradio.Column():
        # 横向排列
        with gradio.Row():  
            before_img = gradio.inputs.Image(label="原图",shape=(200, 200))
            after_img = gradio.Image(label="处理后图片",shape=(200,200))
            #interface_img = gradio.Interface(fn=fun_gray_img, inputs="image", outputs="image")
        # 横向排列
        with gradio.Row():
            gradio.Examples(examples=examples_imgs, inputs=[before_img],label="示例图片")
        # 横向排列
        with gradio.Row(label="图片处理函数"):   
            # 按钮:转为灰度图
            fn_gray_btn = gradio.Button("灰度图")
            # 按钮:转为二值图
            fn_binary_btn = gradio.Button("二值图")
            # 按钮:加入椒盐噪声
            fn_salt_and_pepper_noise_btn = gradio.Button("添加椒盐噪声")
            # 按钮:去除噪声
            fn_remove_noise_btn = gradio.Button("去除常规噪声")
            # 按钮:开运算
            fn_open_operation_btn = gradio.Button("开运算")
            # 按钮:闭运算
            fn_close_operation_btn = gradio.Button("闭运算")
            # 按钮:求连通域运算
            fn_connected_domains_btn = gradio.Button("求连通域")
            # 按钮:腐蚀
            fn_erosion_btn = gradio.Button("腐蚀")
            # 按钮:膨胀
            fn_dilation_btn = gradio.Button("膨胀")
            # 按钮:添加频域水印
            fn_watermark_btn = gradio.Button("添加频域水印")
            # 按钮:高斯滤波
            fn_gaussian_filter_btn = gradio.Button("高斯滤波")
            # 按钮:中值滤波
            fn_median_filter_btn = gradio.Button("中值滤波")
            # 按钮:平均值滤波
            fn_mean_filter_btn = gradio.Button("平均值滤波")
            # 按钮:灰度映射
            fn_gray_mapping_btn = gradio.Button("灰度映射")
            # 按钮:图像增亮
            fn_image_enhancement_btn = gradio.Button("图像增亮")
            # 按钮:沃尔什变换
            fn_walsh_transform_btn = gradio.Button("沃尔什变换")
            # 按钮:哈达玛变换
            fn_hadamard_transform_btn = gradio.Button("哈达玛变换")
            # 按钮:一维DCT变换
            fn_1d_dct_btn = gradio.Button("一维离散余弦变换")
            # 按钮:二维DCT变换
            fn_2d_dct_btn = gradio.Button("二维离散余弦变换")
            # 按钮:二维连续傅立叶变换
            fn_2d_continuous_fourier_transform_btn = gradio.Button("二维连续傅立叶变换")
            # 按钮:二维离散傅立叶变换
            fn_2d_discrete_fourier_transform_btn = gradio.Button("二维离散傅立叶变换")
            # 按钮:连续小波变换
            fn_continuous_wavelet_transform_btn = gradio.Button("连续小波变换")
            # 按钮:一维离散小波变换
            fn_1d_discrete_wavelet_transform_btn = gradio.Button("一维离散小波变换")
            # 按钮:二维离散小波变换
            fn_2d_discrete_wavelet_transform_btn = gradio.Button("二维离散小波变换")
            # 按钮:图像骨骼化
            fn_image_skeletonization_btn = gradio.Button("图像骨骼化")
            
        #绑定fn_gray_btn点击函数
        fn_gray_btn.click(fn=fn_gray, inputs=[before_img], outputs=after_img)
        # 绑定fn_binary_btn点击函数
        fn_binary_btn.click(fn=fn_binary, inputs=[before_img], outputs=after_img)
        # 绑定fn_salt_and_pepper_noise_btn点击函数
        fn_salt_and_pepper_noise_btn.click(fn=fn_salt_and_pepper_noise, inputs=[before_img], outputs=after_img)
        # 绑定fn_remove_noise_btn点击函数
        fn_remove_noise_btn.click(fn=fn_remove_noise,inputs=[before_img],outputs=after_img)
        # 绑定fn_open_operation_btn点击函数
        fn_open_operation_btn.click(fn=fn_open_operation,inputs=[before_img],outputs=after_img)
        # 绑定
        fn_close_operation_btn.click(fn=fn_close_operation, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_connected_domains_btn.click(fn=fn_connected_domains, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_erosion_btn.click(fn=fn_erosion, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_dilation_btn.click(fn=fn_dilation, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_watermark_btn.click(fn=fn_watermark, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_gaussian_filter_btn.click(fn=fn_gaussian_filter, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_median_filter_btn.click(fn=fn_median_filter, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_mean_filter_btn.click(fn=fn_mean_filter, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_gray_mapping_btn.click(fn=fn_gray_mapping, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_image_enhancement_btn.click(fn=fn_image_enhancement, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_walsh_transform_btn.click(fn=fn_walsh_transform, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_hadamard_transform_btn.click(fn=fn_hadamard_transform, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_1d_dct_btn.click(fn=fn_1d_dct, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_2d_dct_btn.click(fn=fn_2d_dct, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_2d_continuous_fourier_transform_btn.click(fn=fn_2d_continuous_fourier_transform, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_2d_discrete_fourier_transform_btn.click(fn=fn_2d_discrete_fourier_transform, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_continuous_wavelet_transform_btn.click(fn=fn_continuous_wavelet_transform, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_1d_discrete_wavelet_transform_btn.click(fn=fn_1d_discrete_wavelet_transform, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_2d_discrete_wavelet_transform_btn.click(fn=fn_2d_discrete_wavelet_transform, inputs=[before_img], outputs=after_img)
        # 绑定
        fn_image_skeletonization_btn.click(fn=fn_image_skeletonization, inputs=[before_img], outputs=after_img)

    gradio.Markdown("## 视频处理")
    # 纵向排列
    with gradio.Column():
        # 横向排列
        with gradio.Row():
            before_video = gradio.inputs.Video(label="原视频")
            before_video_webcam = gradio.Image(source="webcam", streaming=True,label="摄像头预览",visible=True)
            after_video = gradio.Video(label="处理后视频")
            shotcut_video = gradio.Image(label="视频截图",shape=(200,200))
            #interface_img = gradio.Interface(fn=fun_gray_img, inputs="image", outputs="image")

        # 横向排列
        gradio.Markdown("视频操作区")
        with gradio.Row():
            # 按钮:视频截屏按钮
            fn_screenshot_btn = gradio.Button("视频截图")
            # 按钮:摄像头截图
            fn_screenshot_webcam_btn = gradio.Button("摄像头截图")
            # 按钮:摄像头视频保存3s
            fn_save_video_webcam_btn = gradio.Button("摄像头视频保存")
            # 按钮:添加水印功能
            fn_add_watermark_video_btn = gradio.Button("添加水印")
            # 按钮:截取第五帧
            fn_screenshot_frame_5_btn = gradio.Button("截图第五帧")
            # 按钮:随机保存视频
            fn_save_video_random_btn = gradio.Button("随机保存视频")
            # 按钮:视频倒放
            fn_video_upend_btn = gradio.Button("倒放")

        # 横向排列
        with gradio.Row():
            gradio.Examples(examples=examples_videos, inputs=[before_video], label="示例视频")
            # 按钮:打开浏览器摄像头按钮
            fn_open_webcam_btn = gradio.Button("开启摄像头画面")
            # 按钮:关闭浏览器摄像头按钮
            fn_close_webcam_btn = gradio.Button("关闭摄像头画面")
        
        # 绑定按钮功能
        fn_open_webcam_btn.click(fn=fn_open_webcam,outputs=[before_video])
        fn_close_webcam_btn.click(fn=fn_close_webcam,outputs=[before_video])
        fn_screenshot_btn.click(fn=fn_screenshot,inputs=[before_video],outputs=[shotcut_video,before_img])
        fn_screenshot_webcam_btn.click(fn=fn_screenshot_webcam,inputs=[before_video_webcam],outputs=[shotcut_video,before_img])
        fn_screenshot_frame_5_btn.click(fn=fn_screenshot_frame_5,inputs=[before_video],outputs=[shotcut_video,before_img])
        fn_save_video_random_btn.click(fn=fn_save_video_random,inputs=[before_video],outputs=[after_video])
        fn_add_watermark_video_btn.click(fn=fn_add_watermark_video,inputs=[before_video],outputs=[after_video])
        fn_video_upend_btn.click(fn=fn_video_upend,inputs=[before_video],outputs=[after_video])
        fn_save_video_webcam_btn.click(fn=fn_save_video_webcam,inputs=[before_video_webcam],outputs=[after_video])

# 启动demo界面
demo.launch()

##################end 界面构建##################

GradioLite内嵌到网页中

<html>
	<head>
		<script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.js"></script>
		<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.css" />
	</head>
	<body>
		<gradio-lite>
		import gradio as gr

		def greet(name):
			return "Hello, " + name + "!"
		
		gr.Interface(greet, "textbox", "textbox").launch()
		</gradio-lite>
	</body>
</html>

效果