Python计算机视觉 第7章-图像搜索

Python计算机视觉 第7章-图像搜索

7.1 基于内容的图像检索

在大型图像数据库上,CBIR(Content-Based Image Retrieval,基于内容的图像检索)技术用于检索在视觉上具相似性的图像。这样返回的图像可以是颜色相似、纹理相似、图像中的物体或场景相似;总之,基本上可以是这些图像自身共有的任何信息。

对于高层查询,比如寻找相似的物体,将查询图像与数据库中所有的图像进行完全比较(比如用特征匹配)往往是不可行的。在数据库很大的情况下,这样的查询方式会耗费过多时间。在过去的几年里,研究者成功地引入文本挖掘技术到CBIR中处理问题,使在数百万图像中搜索具有相似内容的图像成为可能。

从文本挖掘中获取灵感——矢量空间模型

矢量空间模型是一个用于表示和搜索文本文档的模型。我们将看到,它基本上可以应用于任何对象类型,包括图像。该名字来源于用矢量来表示文本文档,这些矢量是由文本词频直方图构成的。换句话说,矢量包含了每个单词出现的次数,而且在其他别的地方包含很多0元素。由于其忽略了单词出现的顺序及位置,该模型也被称为BOW表示模型。

通过单词计数来构建文档直方图向量v,从而建立文档索引。通常,在单词计数时会忽略掉一些常用词,如“这”“和”“是”等,这些常用词称为停用词。由于每篇文档长度不同,故除以直方图总和将向量归一化成单位长度。对于直方图向量中的每个元素,一般根据每个单词的重要性来赋予相应的权重。通常,数据集(或语料库)中一个单词的重要性与它在文档中出现的次数成正比,而与它在语料库中出现的次数成反比。

7.2 视觉单词

为了将文本挖掘技术应用到图像中,我们首先需要建立视觉等效单词;这通常可以采用2.2节中介绍的SIFT局部描述子做到。它的思想是将描述子空间量化成一些典型实例,并将图像中的每个描述子指派到其中的某个实例中。这些典型实例可以通过分析训练图像集确定,并被视为视觉单词。所有这些视觉单词构成的集合称为视觉词汇,有时也称为视觉码本。对于给定的问题、图像类型,或在通常情况下仅需呈现视觉内容,可以创建特定的词汇。

从一个(很大的训练图像)集提取特征描述子,利用一些聚类算法可以构建出视觉单词。聚类算法中最常用的是K-means,这里也将采用K-means。视觉单词并不高端,只是在给定特征描述子空间中的一组向量集,在采用K-means进行聚类时得到的视觉单词是聚类质心。用视觉单词直方图来表示图像,则该模型便称为BOW模型。

创建词汇
为创建视觉单词词汇,首先需要提取特征描述子。运行下面的代码,可以得到每幅图像提取出的描述子,并将每幅图像的描述子保存在一个文件中:

nbr_images = len(imlist)
featlist = [imlist[i][:-3] + 'sift' for i in range(nbr_images)]for i in range(nbr_images):sift.process_image(imlist[i], featlist[i])

下面的代码创建一个词汇类,以及在训练图像数据集上训练出一个词汇的方法:

from scipy.cluster.vq import *
import vlfeat as siftclass Vocabulary(object):def __init__(self, name):self.name = nameself.voc = []self.idf = []self.trainingdata = []self.nbr_words = 0def train(self, featurefiles, k=100, subsampling=10):"""用含有 k 个单词的 K-means 列出在 featurefiles 中的特征文件训练出一个词汇。对训练数据下采样可以加快训练速度"""nbr_images = len(featurefiles)# 从文件中读取特征descr = []descr.append(sift.read_features_from_file(featurefiles[0])[1])descriptors = descr[0]  # 将所有的特征并在一起,以便后面进行 K-means 聚类for i in arange(1, nbr_images):descr.append(sift.read_features_from_file(featurefiles[i])[1])descriptors = vstack((descriptors, descr[i]))# K-means: 最后一个参数决定运行次数self.voc, distortion = kmeans(descriptors[::subsampling, :], k, 1)self.nbr_words = self.voc.shape[0]# 遍历所有的训练图像,并投影到词汇上imwords = zeros((nbr_images, self.nbr_words))for i in range(nbr_images):imwords[i] = self.project(descr[i])nbr_occurences = sum((imwords > 0) * 1, axis=0)self.idf = log((1.0 * nbr_images) / (1.0 * nbr_occurences + 1))self.trainingdata = featurefilesdef project(self, descriptors):"""将描述子投影到词汇上,以创建单词直方图"""# 图像单词直方图imhist = zeros((self.nbr_words))words, distance = vq(descriptors, self.voc)for w in words:imhist[w] += 1return imhist

Vocabulary 类包含了一个由单词聚类中心 VOC 与每个单词对应的逆向文档频率构成的向量,为了在某些图像集上训练词汇,train() 方法获取包含有 .sift 描后缀的述子文件列表和词汇单词数 k。在 K-means 聚类阶段可以对训练数据下采样,因为如果使用过多特征,会耗费很长时间。

现在在你计算机的某个文件夹中,保存了图像及提取出来的 sift 特征文件,下面的代码会创建一个长为 k ≈ 1000 的词汇表。这里,再次假设 imlist 是一个包含了图像文件名的列表:

import pickle
import vocabularynbr_images = len(imlist)
featlist = [imlist[i][:-3] + 'sift' for i in range(nbr_images)]voc = vocabulary.Vocabulary('ukbenchtest')
voc.train(featlist, 1000, 10)# 保存词汇
with open('vocabulary.pkl', 'wb') as f:pickle.dump(voc, f)print('vocabulary is:', voc.name, voc.nbr_words)

代码最后部分用 pickle 模块保存了整个词汇对象以便后面使用。

7.3 图像索引

在开始搜索之前,我们需要建立图像数据库和图像的视觉单词表示。

7.3.1 建立数据库

在索引图像前,我们需要建立一个数据库。这里,对图像进行索引就是从这些图像中提取描述子,利用词汇将描述子转换成视觉单词,并保存视觉单词及对应图像的单词直方图。从而可以利用图像对数据库进行查询,并返回相似的图像作为搜索结果。

7.3.2 添加图像

有了数据库表单,我们便可以在索引中添加图像。为了实现该功能,我们需要在Indexer 类中添加 add_to_index() 方法。将下面的方法添加到 imagesearch.py 中:

def add_to_index(self, imname, descr):"""获取一幅带有特征描述子的图像,投影到词汇上并添加进数据库"""if self.is_indexed(imname):returnprint('indexing', imname)# 获取图像 idimid = self.get_id(imname)# 获取单词imwords = self.voc.project(descr)nbr_words = imwords.shape[0]# 将每个单词与图像链接起来for i in range(nbr_words):word = imwords[i]# wordid 就是单词本身的数字self.con.execute("insert into imwords(imid, wordid, vocname) values (?, ?, ?)",(imid, word, self.voc.name))# 存储图像的单词直方图# 用 pickle 模块将 NumPy 数组编码成字符串self.con.execute("insert into imhistograms(imid, histogram, vocname) values (?, ?, ?)",(imid, pickle.dumps(imwords), self.voc.name))

该方法获取图像文件名与 Numpy 数组,该数组包含的是在图像找到的描述子。这些描述子投影到词汇上,并插入到 imwords(逐字)和 imhistograms 表单中。我们使用两个辅助函数:is_indxed() 用来检查图像是否已经被索引,get_id() 则对一幅图像文件名给定 id 号。将下面的代码添加进 imagesearch.py:

def is_indexed(self, imname):"""如果图像名字(imname)被索引到,就返回 True"""im = self.con.execute("select rowid from imlist where filename='%s'" % imname).fetchone()return im is not Nonedef get_id(self, imname):"""获取图像 id,如果不存在,就进行添加"""cur = self.con.execute("select rowid from imlist where filename='%s'" % imname)res = cur.fetchone()if res is None:cur = self.con.execute("insert into imlist(filename) values ('%s')" % imname)return cur.lastrowidelse:return res[0]

7.4 在数据库中搜索图像

建立好图像的索引,我们就可以在数据库中搜索相似的图像了。

7.4.1 利用索引获取候选图像

我们可以利用建立起来的索引找到包含特定单词的所有图像,在 Searcher 类中加入 candidates_from_word() 方法:

def candidates_from_word(self, imword):"""获取包含 imword 的图像列表"""im_ids = self.con.execute("select distinct imid from imwords where wordid=%d" % imword).fetchall()return [i[0] for i in im_ids]

上面会给出包含特定单词的所有图像 id 号。

7.4.2 用一幅图像进行查询

利用一幅图像进行查询时,没有必要进行完全的搜索。为了比较单词直方图,Searcher类需要从数据库读入图像的单词直方图。将下面的方法添加到 Searcher 类中:

def get_imhistogram(self, imname):"""返回一幅图像的单词直方图"""im_id = self.con.execute("select rowid from imlist where filename='%s'" % imname).fetchone()if im_id is None:return Nones = self.con.execute("select histogram from imhistograms where rowid='%d'" % im_id[0]).fetchone()# 用 pickle 模块从字符串解码 Numpy 数组return pickle.loads(s[0])

这里,为了在字符串和 NumPy 数组间进行转换,我们再次用到了 pickle 模块,这次使用的是 loads()。

现在,我们可以全部合并到查询方法中:

def query(self, imname):"""查找所有与 imname 匹配的图像列表"""h = self.get_imhistogram(imname)if h is None:return []candidates = self.candidates_from_histogram(h)matchscores = []for imid in candidates:# 获取名字cand_name = self.con.execute("select filename from imlist where rowid=%d" % imid).fetchone()[0]cand_h = self.get_imhistogram(cand_name)if cand_h is None:continue# 用 L2 距离度量相似性cand_dist = sqrt(sum(self.voc.idf * (h - cand_h) ** 2))matchscores.append((cand_dist, imid))# 返回排序后的距离及对应数据库 ids 列表matchscores.sort()return matchscores

该 query() 方法获取图像的文件名,检索其单词直方图及候选图像列表

尝试对图像进行查询

src = imagesearch.Searcher('test.db', voc)
print('try a query...')
print(src.query(imlist[0])[:10])

这会再次打印前 10 个结果,包括候选图像与查询图像间的距离,结果应该和下面类似:

在这里插入图片描述

图1 结果 ### 7.4.3 确定对比基准并绘制结果

为了评价搜索结果的好坏,我们可以计算前 4 个位置中搜索到相似图像数。计算分数的函数,将它添加到 imagesearch.py 中:

def compute_ukbench_score(src, imlist):"""对查询返回的前 4 个结果计算平均相似图像数,并返回结果"""nbr_images = len(imlist)pos = zeros((nbr_images, 4))# 获取每幅查询图像的前 4 个结果for i in range(nbr_images):pos[i] = [w[1] - 1 for w in src.query(imlist[i])[:4]]# 计算分数,并返回平均分数score = array([(pos[i] // 4) == (i // 4) for i in range(nbr_images)]) * 1.0return sum(score) / nbr_images

该函数获得搜索的前 4 个结果,将 query() 返回的索引减去 1,因为数据库索引是从1 开始的,而图像列表的索引是从 0 开始的。然后,利用每 4 幅图像为一组时相似图像文件名是连续的这一事实,我们用整数相除计算得到最终的分数。分数为 4 时结果最理想;没有一个是准确的,分数为 0;仅检索到相同图像时,分数为 1;找到相同的图像并且其他三个中的两个相同时,分数为 3。

显示实际搜索结果的函数如下:

def plot_results(src, res):"""显示在列表 res 中的图像"""plt.figure()nbr_results = len(res)for i in range(nbr_results):imname = src.get_filename(res[i])plt.subplot(1, nbr_results, i + 1)plt.imshow(array(Image.open(imname)))plt.axis('off')plt.show()

7.5 使用几何特性对结果排序

让我们简要地看一种用 BoW 模型改进检索结果的常用方法。BoW 模型的一个主要缺点是在用视觉单词表示图像时不包含图像特征的位置信息,这是为获取速度和可伸缩性而付出的代价。

在这里插入图片描述

图2 在 ukbench 数据集上用一些查询图像进行搜索给出的一些结果。查询图像在最左边,后面是检索到的前 5 幅图像

下面是一个载入所有模型文件并用单应性对靠前的图像进行重排的完整例子:

import pickle
import sift
import imagesearch
import homography# 载入图像列表和词汇
with open('ukbench_imlist.pkl', 'rb') as f:imlist = pickle.load(f)featlist = pickle.load(f)nbr_images = len(imlist)with open('vocabulary.pkl', 'rb') as f:voc = pickle.load(f)src = imagesearch.Searcher('test.db', voc)# 查询图像的索引号和返回的搜索结果数目
q_ind = 50
nbr_results = 20# 常规查询
res_reg = [w[1] for w in src.query(imlist[q_ind])[:nbr_results]]
print('top matches (regular):', res_reg)# 载入查询图像特征
q_locs, q_descr = sift.read_features_from_file(featlist[q_ind])
fp = homography.make_homog(q_locs[:, :2].T)# 用 RANSAC 模型拟合单应性
model = homography.RansacModel()
rank = {}# 载入搜索结果的图像特征
for ndx in res_reg[1:]:locs, descr = sift.read_features_from_file(featlist[ndx])# 获取匹配数matches = sift.match(q_descr, descr)ind = matches.nonzero()[0]ind2 = matches[ind]tp = homography.make_homog(locs[:, :2].T)# 计算单应性,对内点计数。如果没有足够的匹配数则返回空列表try:H, inliers = homography.H_from_ransac(fp[:, ind], tp[:, ind2], model, match_theshold=4)except:inliers = []# 存储内点数rank[ndx] = len(inliers)# 将字典排序,以首先获取最内层的内点数
sorted_rank = sorted(rank.items(), key=lambda t: t[1], reverse=True)
res_geom = [res_reg[0]] + [s[0] for s in sorted_rank]
print('top matches (homography):', res_geom)# 显示靠前的搜索结果
imagesearch.plot_results(src, res_reg[:8])
imagesearch.plot_results(src, res_geom[:8])

首先,载入图像列表、特征列表(分别包含图像文件名和 SIFT 特征文件)及词汇。然后,创建一个 Searcher 对象,执行定期查询,并将结果保存在 res_reg 列表中。然后载入 res_reg 列表中每一幅图像的特征,并和查询图像进行匹配。单应性通过计算匹配数和计数内点数得到。最终,我们可以通过减少内点的数目对包含图像索引和内点数的字典进行排序。打印搜索结果列表到控制台,并可视化检索靠前的图像。

输出结果如下:

在这里插入图片描述

图3 输出结果样例

下图给出了常规查询和对常规查询重新排序后的一些样例结果

在这里插入图片描述

图4 基于几何一致性用单应性对搜索结果进行重排后一些实例搜索结果。在每一个例子中,上一行是没有常规查询的结果,下一行是重排后的结果

7.6 建立演示程序及Web应用

参考教材上的章节,仅作参考,具体开发可以依据个人见解进行个性化。演示代码采用 CherryPy 包,CherryPy 是一个纯 Python 轻量级 Web 服务器,使用面向对象模型。

7.6.2 图像搜索演示程序

首先,我们需要用一些 HTML 标签进行初始化,并用 Pickle 载入数据。另外,还需要有与数据库进行交互的 Searcher 对象词汇。创建一个名为 searchdemo.py 的文件,并添加下面具有两个方法的 Search Demo 类:

import cherrypy
import os
import urllib
import pickle
import random
import imagesearchclass SearchDemo(object):def __init__(self):# 载入图像列表with open('webimlist.txt') as f:self.imlist = f.readlines()self.nbr_images = len(self.imlist)self.ndx = list(range(self.nbr_images))# 载入词汇with open('vocabulary.pkl', 'rb') as f:self.voc = pickle.load(f)# 设置可以显示多少幅图像self.maxres = 15# html 的头部和尾部self.header = """<!doctype html><html><head><title>Image search example</title></head><body>"""self.footer = """</body></html>"""def index(self, query=None):self.src = imagesearch.Searcher('web.db', self.voc)html = self.headerhtml += """<br />Click an image to search. <a href='?query='>Random selection</a> of images.<br /><br />"""if query:# 查询数据库并获取靠前的图像res = self.src.query(query)[:self.maxres]for dist, ndx in res:imname = self.src.get_filename(ndx)html += "<a href='?query=" + urllib.parse.quote(imname) + "'>"html += "<img src='" + imname + "' width='100' />"html += "</a>"else:# 如果没有查询图像,则显示随机选择的图像random.shuffle(self.ndx)for i in self.ndx[:self.maxres]:imname = self.imlist[i].strip()html += "<a href='?query=" + urllib.parse.quote(imname) + "'>"html += "<img src='" + imname + "' width='100' />"html += "</a>"html += self.footerreturn htmlindex.exposed = Truecherrypy.quickstart(SearchDemo(), '/', config=os.path.join(os.path.dirname(__file__), 'service.conf'))

在这里插入图片描述

图5 在 ukbench 数据集上进行搜索的示例。上方是开始页面,显示了一些随机选择的图像;下方是一些查询示例。左上角是查询图像,之后的是搜索到的一些结果靠前的图像

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

halcon try_catch无try不项目

#1&#xff0c;没有用过try的人&#xff0c;肯定是没有真正实战做过项目的。 #2&#xff0c;try_catch又被称为抓异常语句&#xff0c;出现异常的代码会在 exception里进行显示&#xff0c;这个exception就是一个字符串的数组。 #3&#xff0c;为什么要用&#xff0c;halcon的一…

OpenCV结构分析与形状描述符(13)拟合椭圆函数fitEllipseDirect()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆&#xff0c;该椭圆拟合一组2D点。它返回一个内切于该椭圆的旋转矩形。使用了由[91]提出的直接…

将字符串序列中的每个字符串,用字符“0“扩充到x位 Series.str.zfill(x)

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 将字符串序列中的每个字符串sn 如果sn的位数不足x位 则在sn左侧补充0凑齐x位 即在sn左侧补充x-sn个0 Series.str.zfill(x) 选择题 关于以下代码输出结果的说法中正确的是? import pandas …

在国产芯片上实现YOLOv5/v8图像AI识别-【4.4】RK3588网络摄像头推理后推流到RTSP更多内容见视频

本专栏主要是提供一种国产化图像识别的解决方案&#xff0c;专栏中实现了YOLOv5/v8在国产化芯片上的使用部署&#xff0c;并可以实现网页端实时查看。根据自己的具体需求可以直接产品化部署使用。 B站配套视频&#xff1a;https://www.bilibili.com/video/BV1or421T74f 前言…

基于微信小程序点餐、外卖系统的设计与实现 (源码+lw+参考文档+核心代码讲解等)

基于微信小程序点餐、外卖系统的设计与实现(源码lw部署文档讲解等) 项目概述&#xff1a; 这段时间做了一个关于点餐的小程序&#xff0c;也是学习和总结的一部分&#xff0c;希望对大家有所帮助。本课题的主要目标是设计并能够实现一个基于微信小程序点餐系统。项目采用的是…

transforemr网络理解

1.transformer网络中数据的流动过程&#xff1a; 2.transformer中残差的理解&#xff1a; 残差连接&#xff08;Residual Connection&#xff09; 的核心思想就是通过将输入与经过变化的输出相加&#xff0c;来最大限度地保留原始信息。 transforemr中注意力层网络和前馈神经…

GMS地下水数值模拟及溶质(包含反应性溶质)运移模拟技术深度应用

以地下水数值模拟软件GMS操作为主要授课内容&#xff0c;在教学中强调模块化教学&#xff0c;分为前期数据收集与处理&#xff1b;三维地质结构建模&#xff1b;地下水流动模型构建&#xff1b;地下水溶质运移模型构建和反应性溶质运移构建5个模块&#xff1b;采用全流程模式将…

计算机技术专硕,三维数字地球的学习路径?

三维数字地球是一个跨学科领域&#xff0c;涉及地理信息系统&#xff08;GIS&#xff09;、计算机图形学、遥感技术、大数据处理等多个方面。作为计算机技术专硕的学生&#xff0c;可以按照以下学习路径来逐步深入&#xff1a; 1、基础理论学习&#xff1a; 地理信息系统&…

C 408—《数据结构》算法题基础篇—链表(上)

目录 Δ前言 一、链表中特定值结点的删除 0.题目&#xff1a; 1.算法设计思想&#xff1a; 2.C语言描述&#xff1a; 3.算法的时间和空间复杂度&#xff1a; 二、链表链表最小值结点的删除 0.题目 : 1.算法设计思想 : 2.C语言描述 : 3.算法的时间和空间复杂度 : 三、链…

【FPGA数字信号处理】- FIR串行滤波器

理解和掌握 FIR 串行滤波器是踏入数字信号处理领域的重要一步。 那么&#xff0c;什么是 FIR 串行滤波器&#xff1f;它是如何工作的&#xff1f;又有着怎样的神奇之处呢&#xff1f;让我们一起揭开它的神秘面纱。 一、FIR 滤波器简介 FIR 滤波器&#xff0c;全称为有限脉冲…

PointNet++改进策略 :模块改进 | x-Conv | PointCNN, 结合局部结构与全局排列提升模型性能

目录 前言PointCNN实现细节1. X X X-Conv 操作输入输出步骤 2. PointCNN 网络架构层级卷积分类与分割任务 3. 数据增强4. 效率优化 前言 这篇论文介绍了一种名为 PointCNN 的方法&#xff0c;旨在从点云&#xff08;point cloud&#xff09;数据中学习特征。传统卷积神经网络…

【前端】探索webpack3项目build速度优化, 优化个p

文章目录 背景uglifyjs-webpack-pluginwebpack3 压缩混淆js 优化踩坑。结论 背景 webpack3 babel7 uglifyjs-webpack-plugin的项目&#xff0c;build起来是什么体验。 大抵是写了两个月后&#xff0c;发现build时间从120s激增到400s。而这400秒中&#xff0c;有50多秒是Ugli…

江协科技STM32学习- P11 中断系统,EXTI外部中断

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

Pygame中Sprite类实现多帧动画3-3

4 使用自定义类MySprite 使用自定义类MySprite实现多帧动画的步骤是首先创建MySprite类的实例&#xff0c;之后使用相关函数对该实例进行操作。 4.1 创建MySprite类的实例 创建MySprite类的实例的代码如图12所示。 图12 创建MySprite类的实例的代码 其中&#xff0c;变量dr…

Dagger:Android 和 Java 的快速依赖注入框架

在软件开发中&#xff0c;依赖注入&#xff08;DI&#xff09;是一种设计模式&#xff0c;用于实现控制反转&#xff0c;减少代码耦合&#xff0c;提高模块化。Dagger 是一个由 Google 开发的依赖注入库&#xff0c;专门用于 Android 和 Java 应用程序&#xff0c;以其快速和高…

TiDB-从0到1【完结】

来来回回3个月&#xff0c;TiDB-从0到1系列就此完结。 如果各位读者有时间整个系列阅读并根据内容掌握实操的话&#xff0c;最次也有中级TiDB工程师的水准了。 TiDB-从0到1-体系结构 TiDB-从0到1-分布式存储 TiDB-从0到1-分布式事务 TiDB-从0到1-MVCC TiDB-从0到1-部署篇 TiDB…

通信工程学习:什么是IFMP(Ipsilon流管理协议)

IFMP&#xff1a;Ipsilon流管理协议 IFMP&#xff08;Ipsilon Flow Management Protocol&#xff09;&#xff0c;即Ipsilon流量管理协议&#xff0c;是一种用于网络流量管理的协议。它主要用于IP交换机、IP交换网关或IP主机中&#xff0c;通过控制数据传送&#xff0c;将现有网…

Java代码审计篇 | ofcms系统审计思路讲解 - 篇1 | 环境搭建、路由机制

文章目录 Java代码审计篇 | ofcms系统审计思路讲解 - 篇1 | 环境搭建、路由机制1. 前言2. 项目环境搭建3. 项目路由机制3.1. 1&#xff09;先搜索pom.xml文件&#xff0c;看看使用了什么框架3.2. 2&#xff09;确定是否是spring的路由机制3.3. 3&#xff09;确定自写路由机制的…

Chapter 14 计算机网络基本概述

欢迎大家订阅【Vue2Vue3】入门到实践 专栏&#xff0c;开启你的 Vue 学习之旅&#xff01; 文章目录 前言一、网络的基本概念二、集线器、交换机和路由器三、互连网与互联网四、网络的类型五、互连网的组成1. 边缘部分2. 核心部分 六、网络协议 前言 计算机网络是现代信息社会…

【每日一题】LeetCode 104.二叉树的最大深度(树、深度优先搜索、广度优先搜索、二叉树)

【每日一题】LeetCode 104.二叉树的最大深度&#xff08;树、深度优先搜索、广度优先搜索、二叉树&#xff09; 题目描述 给定一个二叉树 root&#xff0c;我们需要计算并返回该二叉树的最大深度。二叉树的最大深度是指从根节点到最远叶子节点的最长路径上的节点数。 思路分…