当前位置: 首页 > news >正文

【python】OpenCV—Tracking(10.5)—dlib

在这里插入图片描述

文章目录

  • 1、功能描述
  • 2、代码实现
  • 3、效果展示
  • 4、完整代码
  • 5、涉及到的库函数
    • dlib.correlation_tracker()
  • 6、参考

1、功能描述

基于 dlib 库,实现指定类别的目标检测和单目标跟踪

2、代码实现

caffe 模型

https://github.com/MediosZ/MobileNet-SSD/tree/master/mobilenet

或者

链接: https://pan.baidu.com/s/1fiBz6tEQmcXdw_dtaUuAVw?pwd=pw5n
提取码: pw5n

在这里插入图片描述

输入 1x3x300x300

输出的类别数为 21

在这里插入图片描述

在这里插入图片描述

导入必要的包

from imutils.video import FPS
import numpy as np
import argparse
import imutils
import dlib
import cv2

注意 dlib 的安装

conda 或者 pip 安装,如果 build 失败的话,可以试试下载 whl 安装

https://github.com/Silufer/dlib-python/tree/main

python -V 查看 python 版本,然后找到对应版本的 whl ,pip install xxx.whl


构造参数解析并解析参数

ap = argparse.ArgumentParser()
ap.add_argument("-p", "--prototxt", required=True,help="path to Caffe 'deploy' prototxt file")
ap.add_argument("-m", "--model", required=True,help="path to Caffe pre-trained model")
ap.add_argument("-v", "--video", required=True,help="path to input video file")
ap.add_argument("-l", "--label", required=True,help="class label we are interested in detecting + tracking")
ap.add_argument("-o", "--output", type=str,help="path to optional output video file")
ap.add_argument("-c", "--confidence", type=float, default=0.2,help="minimum probability to filter weak detections")
args = vars(ap.parse_args())

涉及到 caffe 模型的 prototxt,caffemodel,输入视频,类别标签,输出视频,检测框的置信度配置

moblienet SSD 支持的类别类型如下

CLASSES = ["background", "aeroplane", "bicycle", "bird", "boat","bottle", "bus", "car", "cat", "chair", "cow", "diningtable","dog", "horse", "motorbike", "person", "pottedplant", "sheep","sofa", "train", "tvmonitor"]

加载模型,读取视频,初始化跟踪器

print("[INFO] loading model...")
net = cv2.dnn.readNetFromCaffe(args["prototxt"], args["model"])# 初始化视频流、dlib 相关跟踪器、输出视频写入器和预测的类标签
print("[INFO] starting video stream...")
vs = cv2.VideoCapture(args["video"])
tracker = None
writer = None
label = ""
# 启动每秒帧数估计器
fps = FPS().start()

循环读取视频帧

# 循环播放视频文件流中的帧
while True:# 从视频文件中获取下一帧(grabbed, frame) = vs.read()# 检查我们是否已经到达视频文件的末尾if frame is None:break# 调整帧大小以加快处理速度,然后将帧从 BGR 转换为 RGB 排序(dlib 需要 RGB 排序)frame = imutils.resize(frame, width=600)rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)# 如果我们应该将视频写入磁盘,请初始化写入器if args["output"] is not None and writer is None:fourcc = cv2.VideoWriter_fourcc(*"MJPG")writer = cv2.VideoWriter(args["output"], fourcc, 30,(frame.shape[1], frame.shape[0]), True)

resize 图片至宽为 600,转化为 RGB 输入模式,设置输出视频相关配置

    # 如果我们的相关对象跟踪器是None,我们首先需要应用一个对象检测器来为跟踪器提供实际跟踪的东西if tracker is None:# 获得帧尺寸并将帧转换为 blob(h, w) = frame.shape[:2]blob = cv2.dnn.blobFromImage(frame, 0.007843, (w, h), 127.5)# blob传入网络并获得检测结果net.setInput(blob)detections = net.forward()# 确保至少有一个检测结果if len(detections) > 0:# 找到概率最大的检测索引——为方便起见,我们只跟踪我们以最大概率找到的第一个对象;# 未来的示例将演示如何检测和提取*特定*对象i = np.argmax(detections[0, 0, :, 2])# 获取与对象关联的概率及其类标签conf = detections[0, 0, i, 2]label = CLASSES[int(detections[0, 0, i, 1])]# filter out weak detections by requiring a minimum# confidenceif conf > args["confidence"] and label == args["label"]:# compute the (x, y)-coordinates of the bounding box# for the objectbox = detections[0, 0, i, 3:7] * np.array([w, h, w, h])(startX, startY, endX, endY) = box.astype("int")# construct a dlib rectangle object from the bounding# box coordinates and then start the dlib correlation# trackertracker = dlib.correlation_tracker()rect = dlib.rectangle(startX, startY, endX, endY)tracker.start_track(rgb, rect)# draw the bounding box and text for the objectcv2.rectangle(frame, (startX, startY), (endX, endY),(0, 255, 0), 2)cv2.putText(frame, label, (startX, startY - 15),cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 255, 0), 2)

第一帧的时候,调用目标检测模型,获取检测结果 detections

如果检测到了目标,预测的分数大于配置的阈值,且预测的类别和配置的类别一致

初始化跟踪器 tracker,可视化检测结果


否则,我们已经执行了检测,所以让我们跟踪对象

    else:# 更新跟踪器并抓取被跟踪对象的位置tracker.update(rgb)pos = tracker.get_position()# 解包位置对象startX = int(pos.left())startY = int(pos.top())endX = int(pos.right())endY = int(pos.bottom())# 从相关对象跟踪器中绘制边界框cv2.rectangle(frame, (startX, startY), (endX, endY),(0, 255, 0), 2)cv2.putText(frame, label, (startX, startY - 15),cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 255, 0), 2)

后续帧采用跟踪算法,update 更新目标坐标后,通过 get_position 获取新的坐标,并可视化


    # 检查我们是否应该将帧写入磁盘if writer is not None:writer.write(frame)# 显示输出帧cv2.imshow("Frame", frame)key = cv2.waitKey(1) & 0xFF# 如果按下了“q”键,则退出循环if key == ord("q"):break# 更新FPS计数器fps.update()

保存和可视化结果,按 q 键退出视频流


# 我们的 fps 计数器停止并且 FPS 信息显示在终端中
fps.stop()
print("[INFO] elapsed time: {:.2f}".format(fps.elapsed()))
print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))
# 然后,如果我们正在写入输出视频,我们释放视频编写器
if writer is not None:writer.release()
# 最后,我们关闭所有 OpenCV 窗口并释放视频流
cv2.destroyAllWindows()
vs.release()

完成信息统计,释放资源

3、效果展示

train_result

cat_result

4、完整代码

# 导入必要的包
from imutils.video import FPS
import numpy as np
import argparse
import imutils
import dlib
import cv2# 构造参数解析并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-p", "--prototxt", required=True,help="path to Caffe 'deploy' prototxt file")
ap.add_argument("-m", "--model", required=True,help="path to Caffe pre-trained model")
ap.add_argument("-v", "--video", required=True,help="path to input video file")
ap.add_argument("-l", "--label", required=True,help="class label we are interested in detecting + tracking")
ap.add_argument("-o", "--output", type=str,help="path to optional output video file")
ap.add_argument("-c", "--confidence", type=float, default=0.2,help="minimum probability to filter weak detections")
args = vars(ap.parse_args())# 初始化MobileNet SSD训练好的类标签列表
CLASSES = ["background", "aeroplane", "bicycle", "bird", "boat","bottle", "bus", "car", "cat", "chair", "cow", "diningtable","dog", "horse", "motorbike", "person", "pottedplant", "sheep","sofa", "train", "tvmonitor"]
# 从磁盘加载我们的序列化模型
print("[INFO] loading model...")
net = cv2.dnn.readNetFromCaffe(args["prototxt"], args["model"])# 初始化视频流、dlib 相关跟踪器、输出视频写入器和预测的类标签
print("[INFO] starting video stream...")
vs = cv2.VideoCapture(args["video"])
tracker = None
writer = None
label = ""
# 启动每秒帧数估计器
fps = FPS().start()# 循环播放视频文件流中的帧
while True:# 从视频文件中获取下一帧(grabbed, frame) = vs.read()# 检查我们是否已经到达视频文件的末尾if frame is None:break# 调整帧大小以加快处理速度,然后将帧从 BGR 转换为 RGB 排序(dlib 需要 RGB 排序)frame = imutils.resize(frame, width=600)rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)# 如果我们应该将视频写入磁盘,请初始化写入器if args["output"] is not None and writer is None:fourcc = cv2.VideoWriter_fourcc(*"MJPG")writer = cv2.VideoWriter(args["output"], fourcc, 30,(frame.shape[1], frame.shape[0]), True)# 如果我们的相关对象跟踪器是None,我们首先需要应用一个对象检测器来为跟踪器提供实际跟踪的东西if tracker is None:# 获得帧尺寸并将帧转换为 blob(h, w) = frame.shape[:2]blob = cv2.dnn.blobFromImage(frame, 0.007843, (w, h), 127.5)# blob传入网络并获得检测结果net.setInput(blob)detections = net.forward()# 确保至少有一个检测结果if len(detections) > 0:# 找到概率最大的检测索引——为方便起见,我们只跟踪我们以最大概率找到的第一个对象;# 未来的示例将演示如何检测和提取*特定*对象i = np.argmax(detections[0, 0, :, 2])# 获取与对象关联的概率及其类标签conf = detections[0, 0, i, 2]label = CLASSES[int(detections[0, 0, i, 1])]# filter out weak detections by requiring a minimum# confidenceif conf > args["confidence"] and label == args["label"]:# compute the (x, y)-coordinates of the bounding box# for the objectbox = detections[0, 0, i, 3:7] * np.array([w, h, w, h])(startX, startY, endX, endY) = box.astype("int")# construct a dlib rectangle object from the bounding# box coordinates and then start the dlib correlation# trackertracker = dlib.correlation_tracker()rect = dlib.rectangle(startX, startY, endX, endY)tracker.start_track(rgb, rect)# draw the bounding box and text for the objectcv2.rectangle(frame, (startX, startY), (endX, endY),(0, 255, 0), 2)cv2.putText(frame, label, (startX, startY - 15),cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 255, 0), 2)# 否则,我们已经执行了检测,所以让我们跟踪对象else:# 更新跟踪器并抓取被跟踪对象的位置tracker.update(rgb)pos = tracker.get_position()# 解包位置对象startX = int(pos.left())startY = int(pos.top())endX = int(pos.right())endY = int(pos.bottom())# 从相关对象跟踪器中绘制边界框cv2.rectangle(frame, (startX, startY), (endX, endY),(0, 255, 0), 2)cv2.putText(frame, label, (startX, startY - 15),cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 255, 0), 2)# 检查我们是否应该将帧写入磁盘if writer is not None:writer.write(frame)# 显示输出帧cv2.imshow("Frame", frame)key = cv2.waitKey(1) & 0xFF# 如果按下了“q”键,则退出循环if key == ord("q"):break# 更新FPS计数器fps.update()# 我们的 fps 计数器停止并且 FPS 信息显示在终端中
fps.stop()
print("[INFO] elapsed time: {:.2f}".format(fps.elapsed()))
print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))
# 然后,如果我们正在写入输出视频,我们释放视频编写器
if writer is not None:writer.release()
# 最后,我们关闭所有 OpenCV 窗口并释放视频流
cv2.destroyAllWindows()
vs.release()

测试脚本1

python .\track.py -p .\mobilenet_ssd\MobileNetSSD_deploy.prototxt -m .\mobilenet_ssd\MobileNetSSD_deploy.caffemodel -v .\cat.mp4 -l cat -o cat_result.mp4

测试脚本2

python .\track.py -p .\mobilenet_ssd\MobileNetSSD_deploy.prototxt -m .\mobilenet_ssd\MobileNetSSD_deploy.caffemodel -v .\train.mp4 -l aeroplane -o train_result.mp4

5、涉及到的库函数

dlib.correlation_tracker()

dlib.correlation_tracker 是 Dlib 库中的一个功能,用于实现目标跟踪(Object Tracking)。

dlib.correlation_tracker 基于判别式相关滤波器(Discriminative Correlation Filter, DCF)的方法,这种方法通过训练一个滤波器来区分目标对象和背景,从而实现高效的跟踪。

使用 dlib.correlation_tracker 跟踪目标通常涉及以下几个步骤:

  • 初始化跟踪器:首先,你需要创建一个 correlation_tracker 对象。这通常是在你已知目标对象在第一帧中的位置时进行的。
  • 设置目标区域:你需要指定一个矩形区域(通常通过左上角和右下角的坐标或者通过中心点和尺寸)来标识目标对象在第一帧中的位置。
  • 更新跟踪器:对于后续的视频帧,你需要将新的帧传递给跟踪器,并让它更新目标的位置。这个过程会不断重复,直到视频结束或者跟踪失败。
  • 获取跟踪结果:每次更新后,你可以从跟踪器中获取当前帧中目标对象的位置。

以下是一个简单的示例,展示了如何使用 dlib.correlation_tracker 进行目标跟踪:

import dlib
import cv2# 加载视频
cap = cv2.VideoCapture('video.mp4')# 读取第一帧
ret, frame = cap.read()# 选择目标区域(这里需要手动选择或者通过某种方法自动选择)
rect = dlib.rectangle(50, 50, 200, 200)  # 示例矩形,需要替换为实际的目标位置# 创建跟踪器
tracker = dlib.correlation_tracker()
tracker.start_track(frame, rect)while cap.isOpened():ret, frame = cap.read()if not ret:break# 更新跟踪器tracker.update(frame)# 获取跟踪结果rect = tracker.get_position()# 在帧上绘制跟踪结果cv2.rectangle(frame, (rect.left(), rect.top()), (rect.right(), rect.bottom()), (0, 255, 0), 2)# 显示结果cv2.imshow('Tracking', frame)# 按下 'q' 键退出if cv2.waitKey(1) & 0xFF == ord('q'):break# 释放资源
cap.release()
cv2.destroyAllWindows()

注意事项

  • 目标初始化:目标在第一帧中的位置对于跟踪器的性能至关重要。如果初始化不准确,跟踪可能会失败。
  • 视频质量:视频的质量(如分辨率、帧率、光照条件等)也会影响跟踪器的性能。
  • 遮挡和快速移动:当目标被遮挡或者快速移动时,跟踪器可能会遇到困难。虽然 dlib.correlation_tracker 已经在很多场景下表现良好,但在这些情况下可能需要更复杂的策略。

通过 dlib.correlation_tracker,你可以实现高效且相对准确的目标跟踪,适用于各种计算机视觉应用,如视频监控、人机交互等。

6、参考

  • 目标跟踪(4)使用dlib进行对象跟踪
  • dlib–win系统所有版本文件下载地址whl文件

相关文章:

【python】OpenCV—Tracking(10.5)—dlib

文章目录 1、功能描述2、代码实现3、效果展示4、完整代码5、涉及到的库函数dlib.correlation_tracker() 6、参考 1、功能描述 基于 dlib 库,实现指定类别的目标检测和单目标跟踪 2、代码实现 caffe 模型 https://github.com/MediosZ/MobileNet-SSD/tree/master/…...

音视频入门基础:MPEG2-TS专题(9)——FFmpeg源码中,解码TS Header的实现

一、引言 FFmpeg源码对MPEG2-TS传输流/TS文件解复用时,在通过read_packet函数读取出一个transport packet后,会调用handle_packet函数来处理该transport packet: static int handle_packets(MpegTSContext *ts, int64_t nb_packets) { //..…...

解决“磁盘已插上,但Windows系统无法识别“问题

电脑上有2块硬盘,一块是500GB的固态硬盘,另一块是1000GB的机械硬盘,按下开机键,发现500G的固态硬盘识别了,但1000GB的机械硬盘却无法识别。后面为了描述方便,将"500GB的固态硬盘"称为X盘&#xf…...

论文笔记-WWW2024-ClickPrompt

论文笔记-WWW2024-ClickPrompt: CTR Models are Strong Prompt Generators for Adapting Language Models to CTR Prediction ClickPrompt: CTR模型是大模型适配CTR预测任务的强大提示生成器摘要1.引言2.预备知识2.1传统CTR预测2.2基于PLM的CTR预测 3.方法3.1概述3.2模态转换3.…...

53 基于单片机的8路抢答器加记分

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 首先有三个按键 分别为开始 暂停 复位,然后八个选手按键,开机显示四条杠,然后按一号选手按键,数码管显示30,这…...

【java数据结构】二叉树OJ题

【java数据结构】二叉树OJ题 一、检查两颗树是否相同二、另一颗树的子树三、翻转二叉树四、对称二叉树五、判断一颗二叉树是否是平衡二叉树六、给定一个二叉树, 找到该树中两个指定节点的最近公共祖先七、根据一棵树的前序遍历与中序遍历构造二叉树练习:八、二叉树前…...

IIC和SPI的时序图

SCL的变化快慢决定了通信速率,当SCL为低电平的时候,无论SDA是1还是0都不识别: ACK应答:当从设备为低电平的时候识别为从设备有应答: 谁接收,谁应答: 起始位和停止位: IIC的时序图&am…...

MySQL数据库表的操作

1、总述 今天我跟大家分享MySQL数据库中表的创建,查看,修改,删除。 2、创建表 create table table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎; 说明&#xff1…...

.net core 创建linux服务,并实现服务的自我更新

目录 创建服务创建另一个服务,用于执行更新操作给你的用户配置一些systemctl命令权限 创建服务 /etc/systemd/system下新建服务配置文件:yourapp.service,内容如下: [Unit] Descriptionyourapp Afternetwork.target[Service] Ty…...

springboot338it职业生涯规划系统--论文pf(论文+源码)_kaic

毕 业 设 计(论 文) 题目:it职业生涯规划系统的设计与实现 摘 要 互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以…...

oracle将select作为字段查询

在Oracle中,如果你想将一个SELECT语句作为字段的值,你可以使用子查询或者使用WITH子句(也称为公用表表达式CTE)。以下是两种方法的示例: 方法1:使用子查询 语法如下: SELECTcolumn1,(SELECT …...

Java数据结构和算法相关面试题

天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…...

网络安全风险评估

项目背景 随着信息化技术的快速发展,特别是面向社会、政府机构、企业等业务系统的投入使用,各组织机构对网络和信息系统安全防护都提出了新的要求。为满足安全需求,需对组织机构的网络和信息系统的安全进行一次系统全面的评估,以…...

ADAM优化算法与学习率调度器:深度学习中的关键工具

深度学习模型的训练效果离不开优化算法和学习率的选择。ADAM(Adaptive Moment Estimation)作为深度学习领域中广泛应用的优化算法之一,以其高效性和鲁棒性成为许多任务的默认选择。而学习率调度器则是优化算法的“助推器”,帮助训…...

岛屿数量C++11新特性

每日一题 200. 岛屿数量 class Solution {//使用深度的优先搜索来搜索岛屿图//遍历整个图片 当char数组的值为1时开始从这个点开始往外扩散搜索//注意处理边界 图不是正方形 public:int ans;int d[4][2] {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};int N;int M;void dfs(vector<…...

Git 快速入门:全面了解与安装步骤

Git 快速入门&#xff1a;全面了解与安装步骤 一、关于Git 1.1 简介 Git 是一个开源的分布式版本控制系统&#xff0c;由 Linus Torvalds 于 2005 年创建&#xff0c;最初是为了更好地管理 Linux 内核开发而设计。 Git用于跟踪计算机文件的变化&#xff0c;特别是源代码文件…...

基于域自适应的双光融合

目录 引言DAF-Net编码器-解码器分支编码器部分融合层解码器部分 域自适应层概述多核最大均值差异&#xff08;MK-MMD&#xff09;第一阶段&#xff1a;编码器-解码器分支训练训练过程损失函数 第二阶段&#xff1a;融合层训练训练过程损失函数 实验与结果总结 文章声明&#xf…...

迭代器模式 (Iterator Pattern)

文章目录 迭代器模式 (Iterator Pattern)原理优点缺点示例代码场景描述1. 定义迭代器接口2. 定义集合接口3. 实现具体集合类4. 客户端代码输出结果 UML 类图使用场景优化与扩展小结 迭代器模式 (Iterator Pattern) 迭代器模式是一种 行为型设计模式&#xff0c;用于顺序访问集…...

039集——渐变色之:CAD中画彩虹()(CAD—C#二次开发入门)

&#xff08;来左边儿 跟我一起画个龙&#xff0c;在你右边儿 画一道彩虹 ~~~~~~~~~~~ &#xff09; 效果如下&#xff1a; namespace AcTools {public class Class1{public Wform.Timer timer;//定时器需建在类下面public static DateTime startTime;[CommandM…...

如何将 GitHub 私有仓库(private)转换为公共仓库(public)

文章目录 如何将 GitHub 私有仓库转换为公共仓库步骤 1: 登录 GitHub步骤 2: 导航到目标仓库步骤 3: 访问仓库设置步骤 4: 更改仓库可见性步骤 5: 确认更改步骤 6: 验证更改注意事项 如何将 GitHub 私有仓库转换为公共仓库 在软件开发领域&#xff0c;GitHub 是一个广受欢迎的…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

基于Java+VUE+MariaDB实现(Web)仿小米商城

仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意&#xff1a;运行前…...

MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用

文章目录 一、背景知识&#xff1a;什么是 B-Tree 和 BTree&#xff1f; B-Tree&#xff08;平衡多路查找树&#xff09; BTree&#xff08;B-Tree 的变种&#xff09; 二、结构对比&#xff1a;一张图看懂 三、为什么 MySQL InnoDB 选择 BTree&#xff1f; 1. 范围查询更快 2…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发&#xff0c;旨在打造一个互动性强的购物平台&#xff0c;让用户在购物的同时&#xff0c;能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机&#xff0c;实现旋转、抽拉等动作&#xff0c;增…...