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

SIFT算法详细原理与应用

SIFT算法详细原理与应用

1 SIFT算法由来

1.1 什么是 SIFT?

SIFT,全称为 Scale-Invariant Feature Transform(尺度不变特征变换),是一种用于图像特征检测和描述的经典算法。它通过提取图像中的局部关键点,并为每个关键点生成具有尺度和旋转不变性的描述子,使其能够在不同的图像中进行特征匹配。SIFT 算法尤其适合处理视角变化、尺度变换、部分遮挡和光照变化的问题,因此被广泛应用于计算机视觉领域。通过sift算法可生成图像的特征向量(128维),对图像特征向量可以理解为图像的细节纹理特征,它具有光照,角度,尺寸大小不变性,一个特征向量可能为某个人脸图像的局部一个特征纹理,比如为人脸图像中鼻子的特征,不同人脸图像的鼻子特征是不同的,故,通过收集人脸图像的所有特征向量,可以进行人脸识别,图像匹配等场景。

1.2 SIFT 的发展历程

SIFT 由计算机科学家 David G. Lowe 于 1999 年首次提出,并在 2004 年发表的论文《Distinctive Image Features from Scale-Invariant Keypoints》中进一步完善。其革命性的设计使得 SIFT 成为了特征提取领域的重要里程碑。
虽然 SIFT 曾因专利保护限制了开源使用,但随着专利过期(美国专利于 2020 年到期),SIFT 再次成为开源社区的重要工具,并在许多实际项目中被广泛应用。

此外,SIFT 的思想也启发了许多后续算法的诞生,例如 SURF(Speeded-Up Robust Features)和 ORB(Oriented FAST and Rotated BRIEF),进一步推动了特征提取技术的发展。

2 SIFT算法详细原理

  • 根据高斯函数生成高斯金字塔
    生成高斯金字塔的作用可以理解为 通过相机调整距离物体的距离拍摄的物理,实际上对于物体本身,不管相机距离物体的距离远近,总有一些特征是不变的,比如物体的边缘及轮廓等。
    高斯金字塔底层的图像可以理解为相机近距离拍摄物体的图像,顶层可以理解为相机远距离拍摄物体的图像。故sift具有图像尺寸不变性及旋转不变性(再生成种子时会进行角度旋转变换及归一化处理),而角点检测算法Harris只具有旋转不变性
    在这里插入图片描述
    通过模拟不同距离的图像,尽可能的找到图像的所有特征点

  • 根据高斯金字塔生成差分金字塔(DOG)
    在这里插入图片描述
    通过对高斯金字塔同一组中相邻的图像进行相减,得到DOG差分图像

  • 根据DOG差分图像找到关键点(极值点)
    在这里插入图片描述
    为了寻找尺度空间的极值点,每一个采样点要和它所有的相邻点比较,看其是否比它的图像域和尺度域的相邻点大或者小。如图所示,中间的检测点和它同尺度的8个相邻点和上下相邻尺度对应的9×2个点共26个点比较,以确保在尺度空间和二维图像空间都检测到极值点。 一个点如果在DOG尺度空间本层以及上下两层的26个领域中是最大或最小值时,就认为该点是图像在该尺度下的一个特征点,如图所示。

  • 关键点过滤
    真正的关键点都是灰度值在,x,y方向都有明显变化的,即:一个定义不好的高斯差分算子的极值在横跨边缘的地方有较大的主曲率,而在垂直边缘的方向有较小的主曲率。

  • 根据找到的关键点计算出对应的描述子

    • 计算关键点周围领域的直方图(包括变化尺度,角度(角度可以分为8个方向)),得出当前关键点的主方向(变换尺度最大的为主方向)
      在这里插入图片描述
      通过对每个关键点领域做直方图计算,可以得到该关键点的主方向及大小,由此每个关键点有三个基本的属性:(关键点位置,主方向(与x轴的角度),主方向的大小)
    • 对关键点周围领域进行旋转,将主方向旋转成与x方向一致的方向,以确保旋转不变性
      在这里插入图片描述
    • 将关键点领域分为4x4的 16个子区域,每个子区域分别计算他们的直方图,然后生成由右图组成的16个种子,每个种子包含八个方向的变化尺度。
      在这里插入图片描述
    • 生成描述子
      根据关键点生成的16个种子的八维向量,组成一个4x4x8-128维的向量。再将特征向量进行归一化(就是使的特征向量的值1,成为单位向量)。即每一个关键点就能得到一个128维的向量,也成为一个关键点的描述子。
  • 两个图像描述子的匹配
    通过sift算法分别生成两个图像的各自的多个描述子,分别计算两个图像描述子向量之间的距离,计算两个向量的距离有多种
    计算两个向量的距离有多种方式

    • 曼哈顿距离欧氏距离
    • 欧氏距离
    • 切比雪夫距离
    • 闵可夫斯基距离
    • 标准化欧式距离
    • 余弦距离
    • 汉明距离
    • 杰卡德距离
    • 马氏距离
      具体详见:9个机器学习算法常见距离计算公式

详见:
经典算法研究(1):SIFT算法1
SIFT算法详解
SIFT 全面解析:原理、实现与应用
经典的图像匹配算法----SIFT

3 SIFT算法应用

详见:OpenCV入门学习笔记之Harris角点检测与SIFT特征匹配算法

3.1 cv2中sift算法的使用

""" SIFT,全称为 Scale-Invariant Feature Transform(尺度不变特征变换)"""
src_img = cv2.imread('resources/sift_2.jpg')img = copy.deepcopy(src_img)
gray1 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)gray = copy.deepcopy(gray1)# SIFT检测器
sift = cv2.SIFT_create()# 找出图像中的关键点
kp = sift.detect(gray, None)# 在图中画出关键点
sift_src_img = cv2.drawKeypoints(img, kp, img, color=(255, 0, 0), flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)sift_gray_img = cv2.drawKeypoints(gray, kp, gray, color=(255, 0, 0),flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)# 计算关键点对应的SIFT特征向量
# kp, des = sift.compute(gray, kp)# 调整子图间距
plt.subplot(1, 2, 1), plt.imshow(src_img), plt.title('Origin')
plt.subplot(1, 2, 2), plt.imshow(sift_src_img), plt.title('Sift_Origin')
# plt.subplot(2, 2, 3), plt.imshow(gray, cmap='gray'), plt.title('Gray')
# plt.subplot(2, 2, 4), plt.imshow(sift_gray_img, cmap='gray'), plt.title('Sift_Gray')
plt.show()

在这里插入图片描述
其中右图中的每个关键点是由位置,方向,方向大小组成,通过断点调试可以看到这些信息均包含在kp数组里
在这里插入图片描述

3.2 cv2中sift算法进行图像拼接

如下图有不同角度拍摄的书面,两个图片有共同部分,也有不同部分,现在要对这两个图片进行拼接,共同组成一个完整的图片。

请添加图片描述

左图
请添加图片描述

右图

拼接的过程:
1, 通过sift求出两个图片的特征向量
2,通过knn匹配算法找到两个图片的多对(四队以上)相互匹配的关键点坐标
3,通过四对关键点坐标对B图像进行投射变换
4,A图像与变换后的B图像进行拼接得到原图
下面是完整的

import copy
import time
from datetime import datetime
from typing import Anyimport cv2
import numpy as np
import matplotlib
import matplotlib.pyplot as plt#  modify backend to TkAgg
matplotlib.use('TkAgg')# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题#  modify backend to TkAgg
matplotlib.use('TkAgg')# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题class SIFT():def __init__(self):passdef sift_keypoints_draw(self):""" SIFT,全称为 Scale-Invariant Feature Transform(尺度不变特征变换)"""src_img = cv2.imread('resources/sift_2.jpg')img = copy.deepcopy(src_img)gray1 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)gray = copy.deepcopy(gray1)# SIFT检测器sift = cv2.SIFT_create()# 找出图像中的关键点kp = sift.detect(gray, None)# 在图中画出关键点sift_src_img = cv2.drawKeypoints(img, kp, img, color=(255, 0, 0), flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)sift_gray_img = cv2.drawKeypoints(gray, kp, gray, color=(255, 0, 0),flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)# 计算关键点对应的SIFT特征向量# kp, des = sift.compute(gray, kp)# 调整子图间距plt.subplot(1, 2, 1), plt.imshow(src_img), plt.title('Origin')plt.subplot(1, 2, 2), plt.imshow(sift_src_img), plt.title('Sift_Origin')# plt.subplot(2, 2, 3), plt.imshow(gray, cmap='gray'), plt.title('Gray')# plt.subplot(2, 2, 4), plt.imshow(sift_gray_img, cmap='gray'), plt.title('Sift_Gray')plt.show()def image_sift(self, src_image_a, src_image_b):# 对两个图像通过sift 分别求出各自的特征向量,根据特征向量求出两个图像的所有相同的部分的点坐标信息,并返回""" SIFT,全称为 Scale-Invariant Feature Transform(尺度不变特征变换)"""gray_a = cv2.cvtColor(src_image_a, cv2.COLOR_BGR2GRAY)gray_b = cv2.cvtColor(src_image_b, cv2.COLOR_BGR2GRAY)# generate kps and feature vectorsift = cv2.SIFT_create()(kps_a, features_a) = sift.detectAndCompute(gray_a, None)(kps_b, features_b) = sift.detectAndCompute(gray_b, None)n_kps_a = np.float32([kp.pt for kp in kps_a])n_kps_b = np.float32([kp.pt for kp in kps_b])# feature vector matchratio = 0.95n = 2matcher = cv2.BFMatcher()# 获取两个图像之间所有的匹配点(通过knn算法 进行匹配 对于A图像的每个匹配点,都在B中找到两个最近的匹配点)raw_matches = matcher.knnMatch(features_a, features_b, n)good_matches = []# 通过第一匹配点与第二匹配点的距离差异大小,过滤掉可能为不真实的匹配点for match in raw_matches:if match[0].distance < ratio * match[1].distance:good_matches.append(match[0])if len(good_matches) < 4:print("numbers of match points is less than 4")# 求透视矩阵(用RANSAC方法)# 关于重投影阈值的取值,1:可以根据应用场景来取"""应用场景	                    推荐阈值范围	说明高精度匹配(如标定板、AR 标记)	1.0~3.0	    适用于特征点位置非常准确的场景图像拼接(SIFT/SURF/ORB 匹配)	3.0~5.0	    平衡噪声和精度动态场景或低质量图像	            5.0~10.0	容忍更大的匹配误差"""src_pts = np.float32([n_kps_b[match.trainIdx] for match in good_matches])dst_pts = np.float32([n_kps_a[match.queryIdx] for match in good_matches])return src_pts, dst_ptsdef image_perspective_transformation(self, src_image_a, src_image_b, src_pts, dst_pts):# 图像透视变换reproj_threshold = 4.0H, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, reproj_threshold)distance = 700# 对图像B进行透视变换,变换成与A图像相同角度的平面图像,以便于与图像A进行拼接pert_src_image_b = cv2.warpPerspective(src_image_b, H, (src_image_b.shape[1] + distance, src_image_b.shape[0]))# 图像变形和拼接h1, w1 = src_image_a.shape[:2]h2, w2 = src_image_b.shape[:2]# 计算拼接后图像的尺寸corners1 = np.float32([[0, 0], [0, h1], [w1, h1], [w1, 0]]).reshape(-1, 1, 2)corners2 = np.float32([[0, 0], [0, h2], [w2, h2], [w2, 0]]).reshape(-1, 1, 2)warped_corners2 = cv2.perspectiveTransform(corners2, H)all_corners = np.concatenate((corners1, warped_corners2), axis=0)[xmin, ymin] = np.int32(all_corners.min(axis=0).ravel() - 0.5)[xmax, ymax] = np.int32(all_corners.max(axis=0).ravel() + 0.5)# 计算平移变换translation_dist = [-xmin, -ymin]H_translation = np.array([[1, 0, translation_dist[0]],[0, 1, translation_dist[1]],[0, 0, 1]])# 应用变换,图像拼接result = cv2.warpPerspective(src_image_b, H_translation.dot(H), (xmax - xmin, ymax - ymin))result[translation_dist[1]:h1 + translation_dist[1],translation_dist[0]:w1 + translation_dist[0]] = src_image_areturn resultdef image_mosaicking(self):# 图像拼接src_image_a = cv2.imread('resources/sift_1.jpg')src_image_b = cv2.imread('resources/sift_2.jpg')src_pts, dst_pts = self.image_sift(src_image_a, src_image_b)dst_image_b = self.image_perspective_transformation(src_image_a, src_image_b, src_pts, dst_pts)plt.subplot(1, 3, 1), plt.imshow(src_image_a), plt.title('src_image_a')plt.subplot(1, 3, 2), plt.imshow(src_image_b), plt.title('src_image_b')plt.subplot(1, 3, 3), plt.imshow(dst_image_b), plt.title('Origin_perspective')plt.show()if __name__ == '__main__':sift = SIFT()sift.image_mosaicking()

下面是拼接后的效果图:

在这里插入图片描述

相关文章:

SIFT算法详细原理与应用

SIFT算法详细原理与应用 1 SIFT算法由来 1.1 什么是 SIFT&#xff1f; SIFT&#xff0c;全称为 Scale-Invariant Feature Transform&#xff08;尺度不变特征变换&#xff09;&#xff0c;是一种用于图像特征检测和描述的经典算法。它通过提取图像中的局部关键点&#xff0c;…...

AlphaDrive:通过强化学习和推理释放自动驾驶中 VLM 的力量

AlphaDrive: Unleashing the Power of VLMs in Autonomous Driving via Reinforcement Learning and Reasoning 25年3月来自华中科技大学和地平线的论文 OpenAI 的 o1 和 DeepSeek R1 在数学和科学等复杂领域达到甚至超越了人类专家水平&#xff0c;其中强化学习&#xff08;R…...

【八股消消乐】如何解决SQL线上死锁事故

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本专栏《八股消消乐》旨在记录个人所背的八股文&#xff0c;包括Java/Go开发、Vue开发、系统架构、大模型开发、具身智能、机器学习、深度学习、力扣算法等相关知识点&#xff…...

如何使用 HTML、CSS 和 JavaScript 随机更改图片颜色

原文&#xff1a;如何使用 HTML、CSS 和 JavaScript 随机更改图片颜色 | w3cschool笔记 &#xff08;请勿标记为付费&#xff01;&#xff01;&#xff01;&#xff01;&#xff09; 在网页开发中&#xff0c;为图片添加动态效果可以显著提升用户体验。今天&#xff0c;我将向…...

html如何在一张图片上的某一个区域做到点击事件

在HTML中&#xff0c;可以通过<map>和<area>标签来实现对图片的某个区域添加点击事件。这种方法通常用于创建图像地图&#xff08;Image Map&#xff09;&#xff0c;允许用户点击图片的不同区域触发不同的事件。 以下是实现步骤和代码示例&#xff1a; 1. 准备图…...

Java数据校验:确保数据完整性和正确性

在软件开发中&#xff0c;数据校验是确保应用程序数据完整性和正确性的关键步骤。Java 提供了多种方式来实现数据校验&#xff0c;从简单的条件检查到复杂的框架支持。在这篇博客中&#xff0c;我们将探讨 Java 中数据校验的重要性、常用的校验注解以及如何整合校验框架来提高代…...

Java-IO流之序列化与反序列化详解

Java-IO流之序列化与反序列化详解 一、序列化与反序列化概述1.1 基本概念1.2 核心接口与类1.3 应用场景 二、Java序列化的基本实现2.1 实现Serializable接口2.2 使用ObjectOutputStream进行序列化2.3 使用ObjectInputStream进行反序列化 三、序列化的高级特性3.1 serialVersion…...

机器学习14-迁移学习

迁移学习学习笔记 一、迁移学习概述 迁移学习是机器学习中的一个重要领域&#xff0c;它旨在解决当目标任务的训练数据有限时&#xff0c;如何利用与目标任务相关但不完全相同的源任务数据来提高学习性能的问题。在现实世界中&#xff0c;获取大量高质量的标注数据往往成本高…...

CAN通信收发测试(USB2CAN模块测试实验)

1.搭建测试环境 电脑&#xff1a;安装 USB 驱动&#xff0c;安装原厂调试工具&#xff0c;安装cangaroo&#xff08;参考安装包的入门教程即可&#xff09; USB驱动路径&#xff1a;~\CAN分析仪资料20230701_Linux\硬件驱动程序 原厂调试工具路径&#xff1a;~\CAN分析仪资料2…...

小白初学SpringBoot记录

1.对于通过json返回用户信息时&#xff0c;需要忽略password字段操作&#xff1a; 1.1 pom配置jackson细节&#xff1a; <dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>…...

OSCP备战-BSides-Vancouver-2018-Workshop靶机详细步骤

一、靶机介绍 靶机地址&#xff1a;https://www.vulnhub.com/entry/bsides-vancouver-2018-workshop%2C231/ 靶机难度&#xff1a;中级&#xff08;CTF&#xff09; 靶机发布日期&#xff1a;2018年3月21日 靶机描述&#xff1a; Boot2root挑战旨在创建一个安全的环境&…...

PDF转Markdown/JSON软件MinerU最新1.3.12版整合包下载

MinerU发布至今我已经更新多版整合包了&#xff0c;5天前MinerU发布了第一个正式版1.0.1&#xff0c;并且看到在18小时之前有更新模型文件&#xff0c;我就做了个最新版的一键启动整合包。 2025年02月21日更新v1.1.0版整合包 2025年02月27日更新v1.2.0版整合包 2025-06-05 更…...

Android第十三次面试总结基础

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

【深入学习Linux】System V共享内存

目录 前言 一、共享内存是什么&#xff1f; 共享内存实现原理 共享内存细节理解 二、接口认识 1.shmget函数——申请共享内存 2.ftok函数——生成key值 再次理解ftok和shmget 1&#xff09;key与shmid的区别与联系 2&#xff09;再理解key 3&#xff09;通过指令查看/释放系统中…...

编程基础:执行流

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录 执行流同步&#xff1a;顺序执行&#xff0c;只有一个执行流异步&#xff1a;新开后台(次)执行流&#xff0c;后台执行流要确保不能影响主执行流。共有两个执行流。 阻塞&#xff1a;任务阻塞执行流&#xff0c;导致…...

理解非结构化文档:将 Reducto 解析与 Elasticsearch 结合使用

作者&#xff1a;来自 Elastic Adel Wu 演示如何将 Reducto 的文档处理与 Elasticsearch 集成以实现语义搜索。 Elasticsearch 与业界领先的生成式 AI 工具和提供商有原生集成。欢迎观看我们的网络研讨会&#xff0c;了解如何超越 RAG 基础&#xff0c;或使用 Elastic 向量数据…...

算法训练第十天

232. 用栈实现队列 代码&#xff1a; class MyQueue(object):def __init__(self):self.arr1 []self.arr2 []def push(self, x):""":type x: int:rtype: None"""self.arr1.append(x)def pop(self):""":rtype: int""…...

2种官方方法关闭Windows防火墙

2种官方方法关闭Windows防火墙 引言一、防火墙:你电脑的"智能安检员"二、这些场景,可能需要"临时撤防"三、极速关闭方案方法一:通过系统设置(Win10/11专属通道)方法二:通过传统控制面板(全系统通用:Win7-11全系)四、 必读安全警告(关闭前请三思!…...

[面试精选] 0094. 二叉树的中序遍历

文章目录 1. 题目链接2. 题目描述3. 题目示例4. 解题思路5. 题解代码6. 复杂度分析 1. 题目链接 94. 二叉树的中序遍历 - 力扣&#xff08;LeetCode&#xff09; 2. 题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 3. 题目示例 示例 1 : 输入&…...

股指期货期权交易规则是什么?

本文主要介绍股指期货期权交易规则是什么&#xff1f;股指期货期权是以股指期货合约为标的物的期权交易&#xff0c;其规则结合了期货与期权的特点。 股指期货期权交易规则是什么&#xff1f; 一、基础交易规则 交易时间 交易日9:30-11:30&#xff0c;13:00-15:00&#xff0…...

学习笔记(23): 机器学习之数据预处理Pandas和转换成张量格式[1]

学习笔记(23): 机器学习之数据预处理Pandas和转换成张量格式[1] 学习机器学习&#xff0c;需要学习如何预处理原始数据&#xff0c;这里用到pandas&#xff0c;将原始数据转换为张量格式的数据。 1、安装pandas pip install pandas 2、写入和读取数据 >>创建一个人工…...

2025年6月6日第一轮

2025年6月6日 The rapid in Chiese industdy is developnig e,and it is From be in a enjoy a deep is developing The drone industry in China is developing The drone industy in china develops rapidly and is in a leading position in in the world. The dro…...

记一次运行spark报错

提交spark任务运次报错 06/03 18:27:50 INFO Client: Setting up container launch context for our AM 25/06/03 18:27:50 INFO Client: Setting up the launch environment for our AM container 25/06/03 18:27:50 INFO Client: Preparing resources for our AM container …...

12-Oracle 23ai Vector 使用ONNX模型生成向量嵌入

一、Oracle 23ai Vector Embeddings 核心概念​ 向量嵌入&#xff08;Vector Embeddings&#xff09;​​ -- 将非结构化数据&#xff08;文本/图像&#xff09;转换为数值向量 - - 捕获数据的语义含义而非原始内容 - 示例&#xff1a;"数据库" → [0.24, -0.78, 0.5…...

2. 库的操作

2.1 创建数据库 语法&#xff1a; CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [, create_specification] ...] create_specification: [DEFAULT] CHARACTER SET charset_name # 字符集: 存储编码 [DEFAULT] COLLATE collation_name # 校验集: 比较/选择/读…...

pytorch 与 张量的处理

系列文章目录 文章目录 系列文章目录一、Tensor 的裁剪二、Tensor 的索引与数据筛选torch.wheretorch.indicestorch.gathertorch.masked_selecttorch.taketorch.nonzero&#xff08;省略&#xff09; 三、Tensor 的组合与拼接torch.cattorch.stack 四、Tensor的切片chunksplit …...

layer norm和 rms norm 对比

Layer norm # Layer Norm 公式 mean x.mean(dim-1, keepdimTrue) var x.var(dim-1, keepdimTrue) output (x - mean) / sqrt(var eps) * gamma beta特点&#xff1a; 减去均值&#xff08;去中心化&#xff09;除以标准差&#xff08;标准化&#xff09;包含可学习参数 …...

Java高级 | 【实验六】Springboot文件上传和下载

隶属文章&#xff1a;Java高级 | &#xff08;二十二&#xff09;Java常用类库-CSDN博客 系列文章&#xff1a;Java高级 | 【实验一】Springboot安装及测试 |最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 Java高级 | 【实验三】Springboot 静…...

RKNN开发环境搭建1-基于Ubuntu 18.04系统使用Docker安装rknn-toolkit2

目录 写在最前面Docker 方式安装rknn-toolkit2写在最前面 瑞芯微在RKNN的环境搭建方面的资料很多,但是在搭建过程中发现很多问题教程中并未提及,对初学者不友好。所以博主做了这个系列的文章,从开始搭建环境到对于RKNN Model Zoo的示例进行实践,希望能对初学者有帮助。坚持…...

qt使用笔记二:main.cpp详解

Qt中main.cpp文件详解 main.cpp是Qt应用程序的入口文件&#xff0c;包含程序的启动逻辑。下面我将详细解析其结构和功能。 基本结构 一个典型的Qt main.cpp 文件结构如下&#xff1a; #include <QApplication> // 或者 QGuiApplication/QCoreApplication #include &…...