大模型WebUI:Gradio全解系列8——Additional Features:补充特性(上)

news2025/1/5 7:56:04

大模型WebUI:Gradio全解系列8——Additional Features:补充特性(上)

  • 前言
  • 本篇摘要
  • 8. Additional Features:补充特性
    • 8.1 队列
      • 8.1.1 使用方法
      • 8.1.2 配置队列演示
    • 8.2 输入输出流
      • 8.2.1 输出流
        • 1. 生成器yield
        • 2. 流媒体
      • 8.2.2 输入流
        • 1. 流事件
        • 2. 图像滤镜
      • 8.2.3 统一的输入输出流
      • 8.2.4 跟踪过去的输入或输出
    • 8.3 提示及进度条
      • 8.3.1 提示
      • 8.3.2 进度条
    • 8.4 批处理函数
      • 8.4.1 Interface与Blocks演示
      • 8.4.2 diffusers库与批处理
    • 参考文献

前言

本系列文章主要介绍WEB界面工具Gradio。Gradio是Hugging Face发布的简易webui开发框架,它基于FastAPI和svelte,便于部署人工智能模型,是当前热门的非常易于开发和展示机器学习大语言模型LLM及扩散模型DM的UI框架。本系列文章分为前置概念和实战演练两部分。前置概念先介绍Gradio的详细技术架构、历史、应用场景、与其他框架Gradio/NiceGui/StreamLit/Dash/PyWebIO的区别,然后详细介绍了大模型及数据的资源网站Hugging Face,包括三种资源models/datasets/spaces、六类开源库transformers/diffusers/datasets/PEFT/accelerate/optimum实战。实战演练部分先讲解了多种不同的安装、运行和部署方式,安装包括Linux/Win/Mac三类系统安装,运行包括普通方式和热重载方式,部署包括本地部署、HuggingFace托管、FastAPI挂载和Gradio-Lite浏览器集成;然后按照先整体再细节的逻辑,讲解Gradio的多种高级特性:三种Gradio Clients(python/javascript/curl)、Gradio Tools、Gradio的模块架构和环境变量等,方便读者对Gradio整体把握;最后深入细节,也是本系列文章的核心,先实践基础功能Interface、Blocks和Additional Features,再详解高级功能Chatbots、Data Science And Plots和Streaming。本系列文章注解详细,代码均可运行并附有大量运行截图,方便读者理解,Gradio一定会成为每个技术人员实现奇思妙想的最称手工具。

本系列文章目录如下:

  1. 《Gradio全解系列1——Gradio简介》
  2. 《Gradio全解系列1——Gradio的安装与运行》
  3. 《Gradio全解系列2——Gradio的3+1种部署方式实践》
  4. 《Gradio全解系列2——浏览器集成Gradio-Lite》
  5. 《Gradio全解系列3——Gradio Client:python客户端》
  6. 《Gradio全解系列3——Gradio Client:javascript客户端》
  7. 《Gradio全解系列3——Gradio Client:curl客户端》
  8. 《Gradio全解系列4——Gradio Tools:将Gradio用于LLM Agents》
  9. 《Gradio全解系列5——Gradio库的模块架构和环境变量》
  10. 《Gradio全解系列6——Interface:高级抽象界面类(上)》
  11. 《Gradio全解系列6——Interface:高级抽象界面类(下)》
  12. 《Gradio全解系列7——Blocks:底层区块类(上)》
  13. 《Gradio全解系列7——Blocks:底层区块类(下)》
  14. 《Gradio全解系列8——Additional Features:补充特性(上)》
  15. 《Gradio全解系列8——Additional Features:补充特性(下)》
  16. 《Gradio全解系列9——Chatbots:聊天机器人(上)》
  17. 《Gradio全解系列9——Chatbots:聊天机器人(下)》
  18. 《Gradio全解系列10——Data Science And Plots:数据科学与绘图》
  19. 《Gradio全解系列11——Streaming:数据流(上)》
  20. 《Gradio全解系列11——Streaming:数据流(下)》

本篇摘要

本篇介绍Gradio的其它附加功能,这些功能辅助Interface/Blocks实现更绚丽效果和更多功能。本章附加功能主要包括队列、输入输出流、提示及进度条、批处理函数、安全访问文件和资源清理,下面逐一讲述。

8. Additional Features:补充特性

本篇介绍Gradio的其它附加功能,这些功能辅助Interface/Blocks实现更绚丽效果和更多功能。本章附加功能主要包括队列、输入输出流、提示及进度条、批处理函数、安全访问文件和资源清理,下面逐一讲述。

8.1 队列

每个Gradio程序提供了一个内置的队列系统,可以处理数千个并发用户。由于许多事件监听器可能涉及繁重的处理任务,Gradio 会自动为每个事件监听器创建一个队列来处理后端的事件,因此每个事件监听器都会自动拥有一个队列来处理传入的事件。

8.1.1 使用方法

如果函数推理时间较长(比如目标检测),或者应用程序处理流量过大,则需要使用queue方法进行排队。queue方法使用websockets,可以防止网络超时,通过启用队列,可以控制用户在队列中的位置。

队列使用方式如下:

demo = gr.Interface(...).queue()
demo.launch()
#或
with gr.Blocks() as demo:
    #...
demo.queue()
demo.launch()

以Blocks为例,演示如下:

with gr.Blocks() as demo:
    button = gr.Button(label="Generate Image")
    button.click(fn=image_generator, inputs=gr.Textbox(), outputs=gr.Image())
demo.queue(max_size=10)
demo.launch()

8.1.2 配置队列演示

每个事件监听器默认都有自己的队列,一次处理一个请求,但可以通过事件监听器的两个参数进行配置:

  • concurrency_limit:设置事件监听器的最大并发执行数。默认情况下限制为 1,除非在 Blocks.queue() 中的参数default_concurrency_limit另行配置。你也可以将其设置为None以表示无限制(即无限数量的并发执行);
  • concurrency_id:允许事件监听器通过分配相同的ID来共享队列。当使用共享队列管理多个事件监听器时,使用concurrency_id指定队列,例如如果你的设置中只有2个GPU,但多个函数需要访问GPU访问,此时可以为所有这些函数创建一个共享队列。

两个参数的设置示例如下:

import gradio as gr

with gr.Blocks() as demo:
    prompt = gr.Textbox()
    image = gr.Image()
    generate_btn_1 = gr.Button("Generate Image via model 1")
    generate_btn_2 = gr.Button("Generate Image via model 2")
    generate_btn_3 = gr.Button("Generate Image via model 3")
    generate_btn_1.click(image_gen_1, prompt, image, concurrency_limit=2, concurrency_id="gpu_queue")
    generate_btn_2.click(image_gen_2, prompt, image, concurrency_id="gpu_queue")
    generate_btn_3.click(image_gen_3, prompt, image, concurrency_id="gpu_queue")

在这个例子中,所有三个事件监听器共享一个标识为 “gpu_queue” 的队列。该队列最多可以同时处理 2 个并发请求,额外的请求将被排队,直到有可用的槽位。这些配置易于Gradio管理队列行为。注意事项:

  1. 要确保事件监听器的并发无限制,可以将 concurrency_limit设置为None,这在用户函数调用外部 API(该API自行处理请求的速率限制)时非常有用。
  2. 所有队列的默认并发限制可以使用Blocks.queue()中的default_concurrency_limit参数全局设置。

8.2 输入输出流

对于图像、音频、视频等文件,需要用流进行操作,本节通过讲述输出流、输入流、统一的输入输出流以及跟踪流等,让大家掌握如何处理多媒体文件。

8.2.1 输出流

在某些情况下,我们需要流式输出一系列结果,而不是一次性显示单个输出。例如,某个图像生成模型希望显示每一步生成的图像,直到最终图像生成;或者某个聊天机器人,它逐词流式输出响应,而不是一次性返回所有内容。在这种情况下,可以向Gradio提供一个生成器函数。

1. 生成器yield

在Python中创建生成器非常简单:通常yield语句会放在某种循环中,该函数不是返回单个值,而是生成一系列值。可以像提供常规函数一样向Gradio提供生成器。

例如,以下是一个模拟的图像生成模型,它在输出图像之前生成若干步噪声,使用gr.Interface类进行演示:

import gradio as gr
import numpy as np
import time

def fake_diffusion(steps):
    rng = np.random.default_rng()
    for i in range(steps):
        time.sleep(1)
        image = rng.random(size=(600, 600, 3))
        yield image
    image = np.ones((1000,1000,3), np.uint8)
    image[:] = [255, 124, 0]
    yield image

demo = gr.Interface(fake_diffusion,
                    inputs=gr.Slider(1, 10, 3, step=1),
                    outputs="image")

demo.launch()

运行截图如下:
在这里插入图片描述
请注意,我们在迭代器中添加了 time.sleep(1),它在步骤之间创建一个人为的暂停,这样就能够观察到迭代器的每一步。但在真实的图像生成模型中,这可能是不必要的。

Gradio同样可以处理输入流,例如,每当用户在文本框中输入一个字母时,图像生成模型都会重新运行。这在关于构建响应式界面的中有更详细的介绍,请参考Interface章节中关于实时Interface的讲解。

2. 流媒体

Gradio可以直接从生成器函数中流式输出音频和视频,这让用户几乎在函数生成音频或视频的同时就能听到或看到。实现流式媒体只需执行以下操作:

  1. 在 gr.Audio 或 gr.Video 输出组件中设置 streaming=True,图像自动转换为 base64 格式;
  2. 编写一个 Python 生成器,生成下一个音频或视频的“chunk”块;
  3. 设置 autoplay=True,以便媒体自动开始播放。

对于音频,下一个“块”可以是 .mp3 或 .wav 文件,也可以是音频的字节序列。对于视频,下一个“块”必须是 .mp4 文件或使用 h.264 编解码器且扩展名为 .ts 的文件。为了确保流畅播放,请确保每个块的长度一致且大于 1 秒。

我们将通过一些简单的示例来说明这些要点,以音频为例:

import gradio as gr
from time import sleep

def keep_repeating(audio_file):
    for _ in range(10):
        sleep(0.5)
        yield audio_file

gr.Interface(keep_repeating,
             gr.Audio(sources=["microphone"], type="filepath"),
             gr.Audio(streaming=True, autoplay=True)
).launch()

运行后点击submit,输出与输入同步播放,截图如下:
在这里插入图片描述
同样也可以视频为例,只需将gr.Audio替换为gr.Video即可:gr.Video(sources=["webcam"], format="mp4"),设置也一样,不再重复。关于流的端到端演示请参考后续的数据流章节。

8.2.2 输入流

在上一小节中,我们介绍了如何从事件处理程序中流式输出一系列结果。Gradio还可以将用户摄像头中的图像或麦克风中的音频块流式输入到事件处理程序中,这可以用于创建实时对象检测应用程序或使用Gradio构建对话式聊天应用。

1. 流事件

目前,gr.Image 和 gr.Audio 组件支持通过 stream 事件实现输入流。比如创建一个最简单的流式应用程序,直接返回未修改的网络摄像头流:

import gradio as gr

with gr.Blocks() as demo:
    with gr.Row():
        with gr.Column():
            input_img = gr.Image(label="Input", sources="webcam")
        with gr.Column():
            output_img = gr.Image(label="Output")
        input_img.stream(lambda s: s, input_img, output_img, time_limit=15, stream_every=0.1, concurrency_limit=30)

if __name__ == "__main__":
    demo.launch()

运行截图如下:
在这里插入图片描述
可以自己运行看一下效果。当用户开始录制时,stream 事件会被触发。底层逻辑是网络摄像头每 0.1 秒拍摄一张照片并将其发送到服务器,然后服务器会返回该图像。

stream 事件有两个独特的关键字参数:

  • time_limit:这是 Gradio 服务器处理事件的时间限制。多媒体流本质上是无限制的,因此设置时间限制非常重要,以防止一个用户长期占用 Gradio 队列。时间限制仅计算处理流所花费的时间,不包括在队列中等待的时间。输入图像底部显示的橙色条表示剩余时间。当时间限制到期时,用户将自动重新加入队列。
  • stream_every:这是流捕获输入并将其发送到服务器的频率(以秒为单位)。对于图像检测或处理等演示,设置较小的值可以实现“real-time”实时效果。对于语音转录等演示,较高的值更有用,因为可以使转录算法可以更好地理解上下文。
2. 图像滤镜

本节创建一个图像滤镜演示,用户可以选择多种应用于网络摄像头输入流的滤镜,滤镜种类包括边缘检测滤镜、卡通滤镜,或者简单地垂直翻转流滤镜。代码如下:

import gradio as gr
import numpy as np
import cv2

def transform_cv2(frame, transform):
    if transform == "cartoon":
        # prepare color
        img_color = cv2.pyrDown(cv2.pyrDown(frame))
        for _ in range(6):
            img_color = cv2.bilateralFilter(img_color, 9, 9, 7)
        img_color = cv2.pyrUp(cv2.pyrUp(img_color))

        # prepare edges
        img_edges = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
        img_edges = cv2.adaptiveThreshold(
            cv2.medianBlur(img_edges, 7),
            255,
            cv2.ADAPTIVE_THRESH_MEAN_C,
            cv2.THRESH_BINARY,
            9,
            2,
        )
        img_edges = cv2.cvtColor(img_edges, cv2.COLOR_GRAY2RGB)
        # combine color and edges
        img = cv2.bitwise_and(img_color, img_edges)
        return img
    elif transform == "edges":
        # perform edge detection
        img = cv2.cvtColor(cv2.Canny(frame, 100, 200), cv2.COLOR_GRAY2BGR)
        return img
    else:
        return np.flipud(frame)

with gr.Blocks() as demo:
    with gr.Row():
        with gr.Column():
            transform = gr.Dropdown(choices=["cartoon", "edges", "flip"],
                                    value="flip", label="Transformation")
            input_img = gr.Image(sources=["webcam"], type="numpy")
        with gr.Column():
            output_img = gr.Image(streaming=True)
        dep = input_img.stream(transform_cv2, [input_img, transform], [output_img],
                                time_limit=30, stream_every=0.1, concurrency_limit=30)

demo.launch()

运行后选择边缘检测滤镜,截图如下:
在这里插入图片描述
需要注意的是:

  1. 当更改滤镜值时,它会立即在输出流中生效。这是流事件与其他 Gradio 事件的一个重要区别:在处理流的过程中,可以更改流相关的输入值并立即生效。
  2. 我们将图像输出组件的 streaming 参数设置为True,这样可以让服务器自动将输出图像转换为 base64 格式,这是一种适合流高效传输的格式。

8.2.3 统一的输入输出流

对于一些图像流式传输演示(如上面的示例),我们不需要分别显示输入和输出组件,只显示修改后的输出流,应用程序看起来会更简洁。

我们可以通过将输入图像组件指定为流事件的输出来实现这一点,省略重复代码,另外加入一些CSS格式,核心代码如下:

css=""".my-group {max-width: 500px !important; max-height: 500px !important;}
            .my-column {display: flex !important; justify-content: center !important; align-items: center !important};"""

with gr.Blocks(css=css) as demo:
    with gr.Column(elem_classes=["my-column"]):
        with gr.Group(elem_classes=["my-group"]):
            transform = gr.Dropdown(choices=["cartoon", "edges", "flip"],
                                    value="flip", label="Transformation")
            input_img = gr.Image(sources=["webcam"], type="numpy", streaming=True)
    input_img.stream(transform_cv2, [input_img, transform], [input_img], time_limit=30, stream_every=0.1)

demo.launch()

运行截图如下:
在这里插入图片描述

8.2.4 跟踪过去的输入或输出

通常流式函数应该是无状态的,它应该接受当前输入并返回相应的输出。然而在某些情况下,可能希望跟踪过去的输入或输出。例如可能希望在缓冲区保留前 k 个输入,以提高音频转录演示的准确性。可以使用 Gradio 的 gr.State()组件来实现这一点,示例如下:

def transcribe_handler(current_audio, state, transcript):
    next_text = transcribe(current_audio, history=state)
    state.append(current_audio)
    # 保留前3个输入
    state = state[-3:]
    return state, transcript + next_text

with gr.Blocks() as demo:
    with gr.Row():
        with gr.Column():
            mic = gr.Audio(sources="microphone")
            state = gr.State(value=[])
        with gr.Column():
            transcript = gr.Textbox(label="Transcript")
    mic.stream(transcribe_handler, [mic, state, transcript], [state, transcript],
               time_limit=10, stream_every=1)

demo.launch()

8.3 提示及进度条

当程序运行出现状况时,如何告知用户,以及让用户知道等待时间,这就用到提示和进度条。

8.3.1 提示

提示组件分为三类:gr.Error()、 gr.Warning() 和gr.Info() 。我们希望向用户显示提示错误信息,为此可以在函数中抛出 gr.Error(“自定义消息”) ,这时函数停止执行并向用户显示错误信息。

我们还可以通过在函数中单独使用 gr.Warning(“自定义消息”) 或 gr.Info(“自定义消息”) 来立即显示模态框,同时继续执行函数。gr.Info() 和 gr.Warning() 之间的唯一区别是提示框的颜色。演示如下:

import gradio as gr
from functools import partial

with gr.Blocks() as demo:
    with gr.Row():
        duration = gr.Number(label="Duration", info="Set to -1 for infinite", value=10, minimum=-1)
    with gr.Row():
        error = gr.Button("Error")
        info = gr.Button("Info")
        warning = gr.Button("Warning")

    def display_message(type, msg, duration):
        duration = None if duration < 0 else duration
        if type == "error":
            raise gr.Error(msg, duration=duration)
        elif type == "info":
            gr.Info(msg, duration=duration)
        elif type == "warning":
            gr.Warning(msg,  duration=duration)

    error.click(partial(display_message, "error", "ERROR 💥"),  [duration])
    info.click(partial(display_message, "info", "INFO ℹ️"), [duration])
    warning.click(partial(display_message, "warning", "WARNING ⚠️"), [duration])

运行结果如下:
在这里插入图片描述
提示:请注意gr.Error()是一个必须引发的异常,而gr.Warning()gr.Info()可以让函数继续运行。

8.3.2 进度条

Gradio 支持创建自定义进度条,可以自定义和控制进度更新,以便向用户展示。要启用此功能,只需在方法中添加一个参数,该参数的默认值为 gr.Progress 实例;然后通过直接调用此实例并传入一个介于 0 和 1 之间的浮点数来更新进度,或者使用 Progress 实例的 tqdm() 方法来跟踪可迭代对象的进度,演示如下:

import gradio as gr
import time

def slowly_reverse(word, progress=gr.Progress()):
    progress(0, desc="Starting")
    time.sleep(1)
    progress(0.05)
    new_string = ""
    for letter in progress.tqdm(word, desc="Reversing"):
        time.sleep(0.25)
        new_string = letter + new_string  
    return new_string

demo = gr.Interface(slowly_reverse, gr.Text(), gr.Text())
demo.launch()

运行截图如下:
在这里插入图片描述
如果使用 tqdm 库,甚至可以通过将gr.Progress()的参数track_tqdm设置为True,自动从任意函数中已存在的 tqdm.tqdm 报告进度更新!

8.4 批处理函数

Gradio 支持传入批处理函数,批处理函数是指接收输入列表并返回预测列表的函数。使用批处理函数的优势在于:如果启用了队列,Gradio服务器将传入的请求进行自动批处理和并行处理,从而可能加快演示执行速度。

8.4.1 Interface与Blocks演示

例如,下面演示是一个批处理函数,它接收两个输入列表(一个单词列表和一个整数列表),并返回一个修剪后的单词列表作为输出:

import gradio as gr
import time

def trim_words(words, lens):
    trimmed_words = []
    time.sleep(5)
    for w, l in zip(words, lens):
        trimmed_words.append(w[:int(l)])
    return [trimmed_words]

以下是 Gradio 代码示例(注意 batch=True 和 max_batch_size=16),使用gr.Interface:

gr.Interface(fn=image_classifier, inputs="image", outputs="label")
demo = gr.Interface(
    fn=trim_words, 
    inputs=["textbox", "number"], 
    outputs=["label"],
    batch=True, 
    max_batch_size=16
)

demo.launch()

运行界面如下:
在这里插入图片描述
使用gr.Blocks:

with gr.Blocks() as demo:
    with gr.Row():
        word = gr.Textbox(label="word")
        leng = gr.Number(label="leng")
        output = gr.Textbox(label="Output")
    with gr.Row():
        run = gr.Button()

    event = run.click(trim_words, [word, leng], output, batch=True, max_batch_size=16)

demo.launch()

在这里插入图片描述
在上面的示例中,可以并行处理 16 个请求(总推理时间为 5 秒),而不是每个请求单独处理(总推理时间为 80 秒)。

8.4.2 diffusers库与批处理

Hugging Face 的库 transformers 和 diffusers 中许多模型与 Gradio 的批处理模式可以非常自然地配合使用,比如下面是一个使用 diffusers 批量生成图像的示例演示:

import torch
from diffusers import DiffusionPipeline  # type: ignore
import gradio as gr

generator = DiffusionPipeline.from_pretrained("CompVis/ldm-text2im-large-256")
# move to GPU if available
if torch.cuda.is_available():
    generator = generator.to("cuda")

def generate(prompts):
  images = generator(list(prompts)).images  # type: ignore
  return [images]

demo = gr.Interface(generate,
             "textbox",
             "image",
             batch=True,
             max_batch_size=4  # Set the batch size based on your CPU/GPU memory
)

if __name__ == "__main__":
    demo.launch()

此处由于版本问题和引入库torch,可能出现错误提示,我们主要从代码理解批处理的使用方法即可,是否运行成功不必强求。

参考文献

  1. Gradio - guides - Additional Features

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2269624.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

音视频入门基础:MPEG2-PS专题(4)——FFmpeg源码中,判断某文件是否为PS文件的实现

一、引言 通过FFmpeg命令&#xff1a; ./ffmpeg -i XXX.ps 可以判断出某个文件是否为PS文件&#xff1a; 所以FFmpeg是怎样判断出某个文件是否为PS文件呢&#xff1f;它内部其实是通过mpegps_probe函数来判断的。从《FFmpeg源码&#xff1a;av_probe_input_format3函数和AVI…

【Leetcode】3280. 将日期转换为二进制表示

文章目录 题目思路代码复杂度分析时间复杂度空间复杂度 结果总结 题目 题目链接&#x1f517; 给你一个字符串 date&#xff0c;它的格式为 yyyy-mm-dd&#xff0c;表示一个公历日期。 date 可以重写为二进制表示&#xff0c;只需要将年、月、日分别转换为对应的二进制表示&a…

Spring实现输出带动态标签的日志

版权说明&#xff1a; 本文由博主keep丶原创&#xff0c;转载请保留此块内容在文首。 原文地址&#xff1a; https://blog.csdn.net/qq_38688267/article/details/144851857 文章目录 背景底层原理实现方案Tag缓存实现封装注解通过AOP实现日志缓存封装行为参数通用方法实现手动…

JAVA: 状态模式(State Pattern)的技术指南

1、简述 状态模式是一种行为型设计模式,允许对象在其内部状态改变时改变其行为。它将状态相关的行为抽取到独立的状态类中,使得增加新状态变得简单,且不影响其他状态。 设计模式样例:https://gitee.com/lhdxhl/design-pattern-example.git 本文将详细介绍状态模式的概念…

小程序基础 —— 02 微信小程序账号注册

微信小程序账号注册 小程序开发与网页开发不一样&#xff0c;在开始微信小程序开发之前&#xff0c;需要访问微信公众平台&#xff0c;注册一个微信小程序账号。 有了小程序的账号以后&#xff0c;才可以开发和管理小程序&#xff0c;后续需要通过该账号进行开发信息的设置、…

安卓入门十一 常用网络协议四

MQTT&#xff08;Message Queuing Telemetry Transport&#xff09; MQTT是一种轻量级的、发布/订阅模式的消息传输协议。它被设计用于在低带宽或不稳定网络环境下&#xff0c;实现物联网设备之间的可靠通信。 4.1 MQTT详细介绍 发布/订阅模式&#xff1a;MQTT 使用发布/订…

在 Swift 中使用 SQL 组合人员和地址数据

文章目录 摘要描述问题描述示例输入与输出 Swift 代码解决方案代码分析示例测试及结果时间复杂度空间复杂度总结 摘要 在本篇文章中&#xff0c;我们将讨论如何结合两个表——Person 和 Address&#xff0c;以便生成包含每个人的姓名和地址信息的结果表。如果某人的地址信息不…

AAL省电效果对比

AAL省电的原理主要是‌通过根据显示内容来降低背光&#xff0c;然后通过调节gamma来补偿显示亮度&#xff0c;从而达到省电的效果‌。具体来说&#xff0c;gamma值越高&#xff0c;灰度越低&#xff0c;图像越暗。因此&#xff0c;颜色越暗的图片越省电&#xff0c;这也是为什么…

ArcGIS中怎么进行水文分析?(思路介绍)

最近有人咨询&#xff0c;ArcGIS中怎么进行水文分析&#xff0c;大致的说一下河网提取的思路哈 解决思路&#xff1a;dem填洼→计算水流方向→计算水流累积矩阵→形成河网 dem填洼 计算水流方向 计算水流累积矩阵 用栅格计算器&#xff0c;设阈值&#xff08;自己多次尝试&…

Debian-linux运维-ssh配置(兼容Jenkins插件的ssh连接公钥类型)

系统版本&#xff1a;Debian 12.5、11.1 1 生成密钥对 可以用云服务商控制台生成的密钥对&#xff0c;也可以自己在客户端或者服务器上生成&#xff0c; 已经有密钥对就可以跳过这步 用户默认密钥文件路径为 ~/.ssh/id_rsa&#xff0c;可以在交互中指定路径&#xff0c;也可…

ZZNUOJ 1798:大小写判断(C/C++/Java)

题目描述 给定一个英文字母判断这个字母是大写还是小写。 输入 输入只包含一个英文字母c。 输出 如果c是大写字母,输出“upper”,否则输出“lower”。 样例输入 x 样例输出 lower 来源 蓝桥杯算法训练 常见的ASCII值 ASCII表中可以记下部分特殊的值(十进制)(字母从A到Z&am…

Wonder Dynamics技术浅析(二):人体姿态估计

Wonder Dynamics 的人体姿态估计模块旨在从图像或视频中检测并定位人体关键点&#xff08;如关节、肢体等&#xff09;&#xff0c;为后续的动作捕捉、虚拟角色动画等应用提供基础数据。 一、人体姿态估计概述 人体姿态估计是指从图像或视频中检测并定位人体关键点的位置&…

前端压缩字体包方法,8MB可压缩至900K!

1、先安装压缩工具 npm install font-spider -g2、新建个文件夹&#xff0c;把要压缩的字体放进去&#xff0c;然后新建一个html&#xff0c;如下图 目前没有经过压缩的字体包是接近8MB 新建的html内容如下&#xff0c;直接复制即可 解释&#xff1a; 1、在样式中定义要压缩…

mysql的索引类型和索引方法

前言 在 MySQL 中&#xff0c;索引类型和索引方法是两个不同的概念。索引类型决定了可以存储的数据种类以及索引的功能特性&#xff0c;而索引方法则定义了索引数据的组织方式和查找机制。在 MySQL 中&#xff0c;索引&#xff08;Index&#xff09;是用于加快数据检索速度的数…

七种改进爬山算法的方法

一、爬山算法 爬山算法(Hill Climbing Algorithm)是一种启发式的基于局部最优解的搜索算法,用于在给定的搜索空间中寻找全局最优解或足够好的解。它属于局部搜索算法,通常用于解决优化问题,包括连续和离散问题。 爬山算法模拟了爬山的过程,从某个随机起始点开始,不断向更…

推荐5款局域网IP扫描工具,支持电脑+Android!

在日常网络管理中&#xff0c;快速扫描局域网中的设备和IP地址是一项基本但非常重要的任务。无论是排查网络问题还是进行设备管理&#xff0c;一款好用的 IP 扫描工具都能让你事半功倍。 如何选择适合自己需求的局域网 IP 扫描工具&#xff1f;有哪些功能强大又易于上手的工具…

微信小程序调用 WebAssembly 烹饪指南

我们都是在夜里崩溃过的俗人&#xff0c;所幸终会天亮。明天就是新的开始&#xff0c;我们会变得与昨天不同。 一、Rust 导出 wasm 参考 wasm-bindgen 官方指南 https://wasm.rust-lang.net.cn/wasm-bindgen/introduction.html wasm-bindgen&#xff0c;这是一个 Rust 库和 CLI…

03-栈和队列

目录 3.1栈和队列的定义和特点 3.2栈的表示和操作的实现 顺序栈的表示和实现 Ⅰ.顺序栈的初始化 Ⅱ.顺序栈的入栈 Ⅲ.顺序栈的出栈 链栈的表示和实现 Ⅰ.链栈的初始化 Ⅱ.链栈的入栈 Ⅲ.链栈的出栈 Ⅳ.取栈顶元素 Ⅴ.判断链栈是否为空 3.3栈与递归 3.4队列的表示和操…

Vue 3.0 中 template 多个根元素警告问题

在 Vue 2.0 中&#xff0c;template 只允许存在一个根元素&#xff0c;但是这种情况在 Vue 3.0 里发生了一些变化。 在 Vue 3.0 中开始支持 template 存在多个根元素了。但是因为 VSCode 中的一些插件没有及时更新&#xff0c;所以当你在 template 中写入多个根元素时&#xf…

vue elementUI Plus实现拖拽流程图,不引入插件,纯手写实现。

vue elementUI Plus实现拖拽流程图&#xff0c;不引入插件&#xff0c;纯手写实现。 1.设计思路&#xff1a;2.设计细节3.详细代码实现 1.设计思路&#xff1a; 左侧button列表是要拖拽的组件。中间是拖拽后的流程图。右侧是拖拽后的数据列表。 我们拖动左侧组件放入中间的流…