《Cocos Creator游戏实战》AIGC之将草稿内容转为真实内容

news2024/10/6 8:22:12

目录

前言

训练AI

从识别结果中提取必要数据

发送图片并生成最终代码

总结与提高

资源下载


前言

当创作灵感来的时候,我们可能会先把灵感记录在草稿上,之后再去实现它。比方说有一天,我突然来了游戏创作灵感,想着那可以先把一些简单的组件和布局设计出来,于是就在草稿上画了几个框。

注:L代表Label组件,B代表Button组件,S代表Sprite组件。

几天过去了,就算当时的灵感再好,我也不想打开电脑,所以草稿还是只是草稿。我想着,如果有一个AI能够识别我画的草稿,然后自动生成对应组件以及布局的话该有多好啊。

于是,我决定训练一个AI,准确来说是一个图像目标检测AI模型,我将用它来识别并定位我草稿上的各个方框。

训练AI

首先第一步,制作数据集,花了我半天时间画了很多大大小小的方框,草稿数量为150张。有在便利贴上画的,有在纸巾上画的,还有在白纸上画的。

用于训练的图片已经有了,接下来又花了半天时间去进行标注,不得不说想要训练AI,首先就要训练自己的毅力。

训练集已经做好,接下来就是丢给yolov5训练,训练结果如下。

验证之后,识别效果还是可以的,现在AI可以大概率将我画的SLB三个方框识别出来。 

注:由于时间有限,我就先实现三种组件:Sprite,Label以及Button,如果加入更多组件的话就需要更多数据。

从识别结果中提取必要数据

AI在识别之后可以生成一个txt文件,其中保存着各个矩形的数据,如下所示。

1 0.682994 0.452703 0.127743 0.0981199
2 0.683777 0.452115 0.123041 0.0992949
2 0.562696 0.65188 0.12069 0.103995
2 0.5721 0.343713 0.125392 0.0963572
0 0.290752 0.341657 0.111285 0.0887192
1 0.302116 0.543772 0.0979624 0.0851939
1 0.320533 0.73913 0.111285 0.0951821

第一列中的数字0,1,2分别代表Button方框,Sprite方框和Label方框;第二列是矩形中心的x坐标(归一化后的,下同);第二列是矩形中心的y坐标;第三列是矩形宽度;第四列是矩形高度。通过这些数据我们能够得出各个矩形方框在图片上的真实坐标以及大小。代码编写如下:

def get_rect_list_from_detect_result(pic_path, detect_result_path):
    """从预测结果中得出各个矩形的真实大小和位置数据"""
    with open(detect_result_path, "r", encoding="utf-8") as f:
        result = f.readlines()

    pic_width, pic_height = Image.open(pic_path).size
    class_dict = {"0": "B", "1": "S", "2": "L"}
    rect_list = []

    for line in result:
        _class, x_center_norm, y_center_norm, width_norm, height_norm = line.strip().split(" ")
        rect_width = round(float(width_norm)*pic_width, 2)
        rect_height = round(float(height_norm)*pic_height, 2)
        rect_x = round(float(x_center_norm)*pic_width, 2) - rect_width/2
        rect_y = round(float(y_center_norm)*pic_height, 2) - rect_height/2
        rect_list.append({
            "type": class_dict[_class],
            "width": rect_width,
            "height": rect_height,
            "x": rect_x,
            "y": rect_y
        })
    
    return rect_list

为了在Cocos场景的Canvas画布上生成组件,我们需要先确定画布大小和方向。初始大小我们可以用960和640这两个值,但是方向呢?是采用横向还是竖向?我这里采用的解决方案是:找出草稿上最左边和最右边的矩形,然后得出横向距离,接着找出最上边和最下边的矩形并得出纵向距离。比较横向距离和纵向距离,假如前者大,则画布为横向,反之则为竖向。代码编写如下:

def decide_canvas_size(rect_list):
    """通过各个矩形的最小和最大x/y值来确定画布尺寸"""
    
    # 获取所有矩形中的最小x值
    min_x = min(rect_list, key=lambda rect: rect["x"])["x"]
    max_x = max(rect_list, key=lambda rect: rect["x"])["x"]
    min_y = min(rect_list, key=lambda rect: rect["y"])["y"]
    max_y = max(rect_list, key=lambda rect: rect["y"])["y"]

    # 根据x和y的距离差判断是要横屏还是竖屏
    distance_x = max_x - min_x
    distance_y = max_y - min_y

    if distance_x >= distance_y:
        canvas_width = 960
        canvas_height = 640
    else:
        canvas_width = 640
        canvas_height = 960

    width_prop = distance_x / canvas_width if canvas_width > distance_x else canvas_width / distance_x
    height_prop = distance_y / canvas_height if canvas_height > distance_y else canvas_height / distance_y

    return canvas_width, canvas_height, width_prop, height_prop

AI在识别时,是以图片左上角为原点的,往右为x轴正方向,往下为y轴正方向。

但是Cocos Creator的坐标原点在画布中心,所以我们肯定要将得到的矩形坐标进行转换,否则生成的组件将会都排布在画布右上角。

另外在草稿上画方框时,我们可能是想着让几个矩形对齐,还有几个矩形的大小应该是一样的。但是在画的时候并不会太精准,所以我们还应该将位置和大小相近的矩形进行调整,让它们的x或y值或宽高相等。代码编写如下:

注:关于其他方面的调整,我就不再赘述了,读者可以看下注释。

def adjust_rect_data(rect_list, canvas_width, canvas_height, width_prop, height_prop):
    """调整各个矩形的值"""
    # 找到最小x和最小y值(也就是找到最左上角的矩形的x和y值)
    # 然后其他将其他矩形的x和y坐标减去最小x和最小y,求出相对距离
    # 所有相对距离包括宽高全部乘以宽高比例
    # 同时将坐标转换为Cocos类型,以画布中心为原点
    min_x = min(rect_list, key=lambda rect: rect["x"])["x"]
    min_y = min(rect_list, key=lambda rect: rect["y"])["y"]

    for rect in rect_list:
        rect["x"] = (rect["x"] - min_x) * width_prop - canvas_width/2
        rect["y"] = canvas_height/2 - (rect["y"] - min_y) * height_prop
        rect["width"] *= width_prop
        rect["height"] *= height_prop

    # 算出下边和右边的空白距离,将所有矩形往下和往右平移空白距离/2个像素点
    max_x = max(rect_list, key=lambda rect: rect["x"])["x"]
    min_y = min(rect_list, key=lambda rect: rect["y"])["y"]
    right_distance = (canvas_width/2 - max_x) / 2
    bottom_distance = abs(-canvas_height/2 - min_y) / 2

    for rect in rect_list:
        rect["x"] += right_distance
        rect["y"] -= bottom_distance

    # 将x或y坐标距离不相差15像素的矩形对齐
    diff = 15
    for rect1 in rect_list:
        for rect2 in rect_list:
            if rect1 == rect2:
                continue
                
            if abs(rect1["x"] - rect2["x"]) <= diff:
                average_x = (rect1["x"] + rect2["x"]) / 2
                rect1["x"] = average_x
                rect2["x"] = average_x
            
            if abs(rect1["y"] - rect2["y"]) <= diff:
                average_y = (rect1["y"] + rect2["y"]) / 2
                rect1["x"] = average_y
                rect2["x"] = average_y

            if abs(rect1["width"] - rect2["width"]) <= diff:
                average_width = (rect1["width"] + rect2["width"]) / 2
                rect1["width"] = average_width
                rect2["width"] = average_width
            
            if abs(rect1["height"] - rect2["height"]) <= diff:
                average_height= (rect1["height"] + rect2["height"]) / 2
                rect1["height"] = average_height
                rect2["height"] = average_height

    # 四舍五入保留整数
    for rect in rect_list:
        rect["x"] = round(rect["x"])
        rect["y"] = round(rect["y"])
        rect["width"] = round(rect["width"])
        rect["height"] = round(rect["height"])
    
    return rect_list

数据调整完毕后,我们就可以把画布数据和各个矩形数据一同返回。我这里用flask框架开发了一个后端,完整代码如下:

import os
import uuid
from PIL import Image
from pathlib import Path
from flask import Flask, request


app = Flask(__name__)
app.config["UPLOAD_FOLDER"] = str(Path(__file__).parent / "upload")
app.config["SECRET_KEY"] = "SECRET_KEY"


@app.route("/d2r", methods=["POST"])
def draft_to_reality():
    """将草稿转成真实布局所需要的数据格式"""

    # 从前端获取图片
    file = request.files.get("file")
    if not file.filename.endswith(".png") and not file.filename.endswith(".jpg") and not file.filename.endswith("jpeg"):
        return {
            "code": "1",
            "message": "图片格式错误"
        }
    
    # 保存图片
    pic_path = Path(app.config["UPLOAD_FOLDER"]) / f"{uuid.uuid4()}.jpg"
    file.save(pic_path)

    # 目标识别
    is_ok, detect_result_path = detect(pic_path)
    if not is_ok:
        return {
             "code": "2",
             "message": "图片识别失败"
        }
    
    # 制作数据
    rect_list = get_rect_list_from_detect_result(pic_path, detect_result_path)
    canvas_width, canvas_height, width_prop, height_prop = decide_canvas_size(rect_list)
    rect_list = adjust_rect_data(rect_list, canvas_width, canvas_height, width_prop, height_prop)
    final_data = make_final_data(rect_list, canvas_width, canvas_height)
    
    return {
        "code": "0",
        "message": final_data
    }


def detect(pic_path):
    os.system(f"python ./yolov5/detect.py --weights ./yolov5/best.pt --source {pic_path} --save-txt --exist-ok")
    
    # 如果识别成功,则会生成一个txt文件
    detect_result_path = f"./yolov5/runs/detect/exp/labels/{Path(pic_path).name.split('.')[0]}.txt"
    if Path(detect_result_path).exists():
        return True, detect_result_path
    else:
        return False, None
    

def get_rect_list_from_detect_result(pic_path, detect_result_path):
    """从预测结果中得出各个矩形的真实大小和位置数据"""
    with open(detect_result_path, "r", encoding="utf-8") as f:
        result = f.readlines()

    pic_width, pic_height = Image.open(pic_path).size
    class_dict = {"0": "B", "1": "S", "2": "L"}
    rect_list = []

    for line in result:
        _class, x_center_norm, y_center_norm, width_norm, height_norm = line.strip().split(" ")
        rect_width = round(float(width_norm)*pic_width, 2)
        rect_height = round(float(height_norm)*pic_height, 2)
        rect_x = round(float(x_center_norm)*pic_width, 2) - rect_width/2
        rect_y = round(float(y_center_norm)*pic_height, 2) - rect_height/2
        rect_list.append({
            "type": class_dict[_class],
            "width": rect_width,
            "height": rect_height,
            "x": rect_x,
            "y": rect_y
        })
    
    return rect_list


def decide_canvas_size(rect_list):
    """通过各个矩形的最小和最大x/y值来确定画布尺寸"""
    
    # 获取所有矩形中的最小x值
    min_x = min(rect_list, key=lambda rect: rect["x"])["x"]
    max_x = max(rect_list, key=lambda rect: rect["x"])["x"]
    min_y = min(rect_list, key=lambda rect: rect["y"])["y"]
    max_y = max(rect_list, key=lambda rect: rect["y"])["y"]

    # 根据x和y的距离差判断是要横屏还是竖屏
    distance_x = max_x - min_x
    distance_y = max_y - min_y

    if distance_x >= distance_y:
        canvas_width = 960
        canvas_height = 640
    else:
        canvas_width = 640
        canvas_height = 960

    width_prop = distance_x / canvas_width if canvas_width > distance_x else canvas_width / distance_x
    height_prop = distance_y / canvas_height if canvas_height > distance_y else canvas_height / distance_y

    return canvas_width, canvas_height, width_prop, height_prop


def adjust_rect_data(rect_list, canvas_width, canvas_height, width_prop, height_prop):
    """调整各个矩形的值"""
    # 找到最小x和最小y值(也就是找到最左上角的矩形的x和y值)
    # 然后其他将其他矩形的x和y坐标减去最小x和最小y,求出相对距离
    # 所有相对距离包括宽高全部乘以宽高比例
    # 同时将坐标转换为Cocos类型,以画布中心为原点
    min_x = min(rect_list, key=lambda rect: rect["x"])["x"]
    min_y = min(rect_list, key=lambda rect: rect["y"])["y"]

    for rect in rect_list:
        rect["x"] = (rect["x"] - min_x) * width_prop - canvas_width/2
        rect["y"] = canvas_height/2 - (rect["y"] - min_y) * height_prop
        rect["width"] *= width_prop
        rect["height"] *= height_prop

    # 算出下边和右边的空白距离,将所有矩形往下和往右平移空白距离/2个像素点
    max_x = max(rect_list, key=lambda rect: rect["x"])["x"]
    min_y = min(rect_list, key=lambda rect: rect["y"])["y"]
    right_distance = (canvas_width/2 - max_x) / 2
    bottom_distance = abs(-canvas_height/2 - min_y) / 2

    for rect in rect_list:
        rect["x"] += right_distance
        rect["y"] -= bottom_distance

    # 将x或y坐标距离不相差15像素的矩形对齐
    diff = 15
    for rect1 in rect_list:
        for rect2 in rect_list:
            if rect1 == rect2:
                continue
                
            if abs(rect1["x"] - rect2["x"]) <= diff:
                average_x = (rect1["x"] + rect2["x"]) / 2
                rect1["x"] = average_x
                rect2["x"] = average_x
            
            if abs(rect1["y"] - rect2["y"]) <= diff:
                average_y = (rect1["y"] + rect2["y"]) / 2
                rect1["x"] = average_y
                rect2["x"] = average_y

            if abs(rect1["width"] - rect2["width"]) <= diff:
                average_width = (rect1["width"] + rect2["width"]) / 2
                rect1["width"] = average_width
                rect2["width"] = average_width
            
            if abs(rect1["height"] - rect2["height"]) <= diff:
                average_height= (rect1["height"] + rect2["height"]) / 2
                rect1["height"] = average_height
                rect2["height"] = average_height

    # 四舍五入保留整数
    for rect in rect_list:
        rect["x"] = round(rect["x"])
        rect["y"] = round(rect["y"])
        rect["width"] = round(rect["width"])
        rect["height"] = round(rect["height"])
    
    return rect_list


def make_final_data(rect_list, canvas_width, canvas_height):
    return {
        "canvas": {
            "width": canvas_width,
            "height": canvas_height
        },
        "rects": rect_list
    }



if __name__ == "__main__":
    app.run()

发送图片并生成最终代码

后端代码已经开发完毕,前端功能的话我们就用Cocos插件来实现,插件界面如下。

选择一张草稿图片,然后点击生成。

此时后端就会识别上传的图片,然后返回画布和矩形数据。

{ 
  canvas: { height: 960, width: 640 }, 
  rects: [ 
    { height: 93, type: 'S', width: 122, x: 215, y: 128 }, 
    { height: 208, type: 'B', width: 241, x: 193, y: -165 }, 
    { height: 119, type: 'S', width: 148, x: -171, y: -56 }, 
    { height: 119, type: 'L', width: 148, x: -215, y: 165 } 
  ] 
} 

最后插件会根据该数据在assets中生成一个d2r.ts文件,我们将该ts文件挂载到画布上然后运行就可以看到效果了。

注:resources文件夹是插件在开启时自动生成的,其中包含着用于Sprite和Button组件的初始图片。

运行效果如下:

总结与提高

我们通过AI将草稿内容识别了出来,然后提取并调整了必要数据,最后得到了一个可以生成真实组件和布局的代码。

有待提高的点:

1. 用于AI的训练数据量可以再大一些,这样可以提高识别精度,也可以生成更多种类的组件。

2. 个别草稿识别出来的结果不是很满意,有些组件在草稿上是分开的,但生成后确是重合的,还有方框的大小比例还需要更精确。所以在对提取的数据做调整时,调整方面的算法还有待改进。

3. 目前AI的作用就在识别,并不能很好的体现AIGC的魅力。可以加入自然语言处理(NLP)技术,获取用户意向,然后再用爬虫爬取相应的图片资源,生成一个更好看更完善的内容。

资源下载

后端代码下载地址,需要安装的库已经写在yolov5/requirements.txt文件中:

链接:https://pan.baidu.com/s/1Z-q2mc2jsX5h_fWD_QjaOA 
提取码:cr9t

前端插件下载地址,Cocos Creator版本要>=3.6.0:

链接:https://pan.baidu.com/s/141gpeSjGunKMf9SlqY0H7Q 
提取码:9e6t

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

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

相关文章

gpt 怎么用-免费gpt下载使用方法

gpt 怎么用 GPT&#xff08;Generative Pre-trained Transformer&#xff09;是一种基于Transformer的神经网络模型&#xff0c;用于自然语言处理任务&#xff0c;例如文本生成、摘要生成、翻译、问答等。以下是使用GPT进行文本生成的一般步骤&#xff1a; 首先&#xff0c;您…

编译预处理

编译预处理 1、宏定义1.1、 无参宏定义1.2、使用宏定义的优点1.3、宏定义注意点1.4、带参数的宏(重点)1.5、条件编译1.6、宏定义的一些巧妙用法(有用)1.7、结构体占用字节数的计算原则&#xff08;考题经常考&#xff0c;要会画图&#xff09;1.8、#在宏定义中的作用&#xff0…

转型产业互联网,新氧能否再造辉煌?

近年来&#xff0c;“颜值经济”推动医美行业快速发展&#xff0c;在利润驱动下&#xff0c;除了专注医美赛道的企业之外&#xff0c;也有不少第三方互联网平台正强势进入医美领域&#xff0c;使以新氧为代表的医美企业面对不小发展压力&#xff0c;同时也展现出强大的发展韧性…

六、CANdelaStudio入门-通信参数编辑

本专栏将由浅入深的展开诊断实际开发与测试的数据库编辑,包含大量实际开发过程中的步骤、使用技巧与少量对Autosar标准的解读。希望能对大家有所帮助,与大家共同成长,早日成为一名车载诊断、通信全栈工程师。 本文介绍CANdelaStudio的通信参数编辑,欢迎各位朋友订阅、评论,…

Kubernetes 笔记(16)— 集群管理、使用名字空间分隔系统资源、给名字空间设置资源限额、默认资源配额的使用

1. 为什么要有名字空间 首先要明白&#xff0c;Kubernetes 的名字空间并不是一个实体对象&#xff0c;只是一个逻辑上的概念。它可以把集群切分成一个个彼此独立的区域&#xff0c;然后我们把对象放到这些区域里&#xff0c;就实现了类似容器技术里 namespace 的隔离效果&…

MATLAB符号运算(七) 更新中...

目录 1、实验目的&#xff1a; 2、实验内容&#xff1a; 1、实验目的&#xff1a; 1&#xff09;掌握定义符号对象和创建符号表达式的方法&#xff1b; 2&#xff09;掌握符号运算基本命令和规则&#xff1b; 3&#xff09;掌握符号表达式的运算法则以及符号矩阵运算&#xf…

93、Dehazing-NeRF: Neural Radiance Fields from Hazy Images

简介 论文&#xff1a;https://arxiv.org/pdf/2304.11448.pdf 从模糊图像输入中恢复清晰NeRF 使用大气散射模型模拟有雾图像的物理成像过程&#xff0c;联合学习大气散射模型和干净的NeRF模型&#xff0c;用于图像去雾和新视图合成 通过将NeRF 3D场景的深度估计与大气散射模…

【牛客刷题专栏】23:JZ22 链表中倒数最后k个结点(C语言编程题)

前言 个人推荐在牛客网刷题(点击可以跳转)&#xff0c;它登陆后会保存刷题记录进度&#xff0c;重新登录时写过的题目代码不会丢失。个人刷题练习系列专栏&#xff1a;个人CSDN牛客刷题专栏。 题目来自&#xff1a;牛客/题库 / 在线编程 / 剑指offer&#xff1a; 目录 前言问…

LeetCode-344. 反转字符串

题目链接 LeetCode-344. 反转字符串 题目描述 题解 题解一&#xff08;Java&#xff09; 作者&#xff1a;仲景 直接双指针前后一直交换即可 class Solution {public void reverseString(char[] s) {if (s.length 1)return;// 双指针int lp 0, rp s.length - 1;while (lp…

【百度智能云】基于http3的xcdn 开放直播方案设计与实践

大神:柯老师 现有的融合CDN 0 需要集成sdksdk 是集成在端侧缺点 sdk 对端侧有影响多云模式下,sdk不互通、 XCDN 设计目标 :保持现有cdn的优势 承载各种业务:直播点播让各家的cdn互通cdn 厂家屏蔽了差异性,集成起来比较简单,对接简单开发的互联网生态。使用统一的http3标…

理解缓冲区

文章目录 一.缓冲区1.什么是缓冲区2.缓冲区的意义3.缓冲区的刷新策略4.我们目前谈论的缓冲区在哪里5.仿写FILE5.1myStdio.h5.2myStdio.c 6.操作系统的缓冲区 一.缓冲区 int main() {printf("hello linux");sleep(2);return 0; }对于这样的代码&#xff0c;首先可以肯…

C++11 unique_ptr智能指针

#include<iostream> using namespace std;class test { public:test() {cout << "调用构造函数" << endl;}~test() {cout << "调用析构函数" << endl;} };int main(void) {//1.构造函数unique_ptr<test>t1;unique_ptr…

数据结构之KMP算法:彻底搞懂kmp算法

数据结构的学习&#xff0c;kmp匹配算法困扰我许久&#xff0c;此处来一个总结&#xff08;仅供自己复习了解参考使用&#xff09;&#xff0c;如果有不对的地方请多多指点。好了废话不多说我们直接开始好吧。 目录 关于暴力匹配原理的讲解&#xff1a; kmp算法&#xff1a; …

ChatGPT - 如何高效的调教ChatGPT (指令建构模型-LACES问题模型)

文章目录 定义1. Limitation&#xff08;限定条件&#xff09;2. Assignment&#xff08;分配角色&#xff09;3. Context&#xff08;背景或上下文&#xff09;4. Example&#xff08;示例&#xff09;5. Step by Step&#xff08;拆分任务&#xff09; 小Demo 定义 LACES问题…

尚硅谷大数据技术Spark教程-笔记04【SparkCore(核心编程,RDD-行动算子-序列化-依赖关系-持久化-分区器-文件读取与保存)】

视频地址&#xff1a;尚硅谷大数据Spark教程从入门到精通_哔哩哔哩_bilibili 尚硅谷大数据技术Spark教程-笔记01【Spark&#xff08;概述、快速上手、运行环境、运行架构&#xff09;】尚硅谷大数据技术Spark教程-笔记02【SparkCore&#xff08;核心编程&#xff0c;RDD-核心属…

加强人工智能共性技术研发与产业化协同发展

央视网消息&#xff1a;“以5G为代表的新一代信息技术与制造业、交通、旅游等实体经济重要领域深度融合。”4月20日下午&#xff0c;国新办举行一季度工业和信息化发展情况新闻发布会&#xff0c;相关部门负责人在答问时表示&#xff0c;将用好融合应用这把金钥匙&#xff0c;开…

ReactHook学习(第一篇-N)

文章目录 Hook简介概述class组件的不足什么是 Hook?Hook 使用规则 state的研究&#xff08;useState&#xff09;State&#xff1a;组件的记忆&#xff08;响应式数据&#xff09;当普通的变量无法满足时添加一个 state 变量遇见你的第一个 Hook剖析 useState 赋予一个组件多个…

【C++】面向对象

文章目录 3.1 类与对象3.1.1 类成员的访问控制3.1.2 类的成员函数对象的访问方式成员函数的实现内联成员函数 3.1.3 构造函数复制构造函数调用复制构造函数的三种情况深复制与浅复制&#xff1f; 析构函数类的组合 3.1.4 前向引用声明3.1.5 结构体与类对比3.1.6 UML类图属性表示…

IMX6ULL裸机篇之按键消抖实验

一. 按键消抖 在之前的 按键中断实验时&#xff0c;我们讲了如何使用中断的方式驱动按键或GPIO。如果通过中断的方式处理按键的话&#xff0c;按键是需要消抖处理的。 而在之前 按键中断实验中&#xff0c;在中断处理函数中对按键进行消抖&#xff0c;调用了 delay 延时函数。…

剑指 Offer 32 - II. 从上到下打印二叉树 II

目录 题目思路BFS 题目来源 剑指 Offer 32 - II. 从上到下打印二叉树 II 题目思路 I. 按层打印&#xff1a; 题目要求的二叉树的 从上至下 打印&#xff08;即按层打印&#xff09;&#xff0c;又称为二叉树的 广度优先搜索&#xff08;BFS&#xff09;。BFS 通常借助 队列 的…