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

opencv入门指南

OpenCV 入门指南 (C/C++):开启计算机视觉之旅 👁️‍🗨️

OpenCV (Open Source Computer Vision Library) 是一个开源的计算机视觉和机器学习软件库。它包含数千种优化的算法,为各种计算机视觉应用提供了丰富的工具集。无论你是想进行图像处理、物体检测、人脸识别,还是进行更复杂的视频分析,OpenCV 都是一个强大且灵活的选择。本指南将带你使用 C/C++ 语言入门 OpenCV。


📖 OpenCV 是什么?为什么选择它?

简单来说,OpenCV 是一套可以让你用编程方式“看”和“理解”图像与视频的工具箱

为什么选择 OpenCV?

  1. 功能强大且全面:提供了从基本的图像读取、显示、保存,到复杂的图像处理(滤波、边缘检测、形态学操作)、特征提取、物体检测、视频分析、相机标定、3D重建,乃至机器学习模块。
  2. 跨平台:支持 Windows, Linux, macOS, Android, iOS 等主流操作系统。
  3. 高性能:许多算法都针对速度进行了优化,并且可以利用多核处理器。对于实时应用,性能至关重要。
  4. C/C++ 接口:虽然 OpenCV 也支持 Python、Java 等语言,但其核心是用 C++ 编写的,提供了高效的 C/C++ API。对于追求性能的应用,C++ 是首选。
  5. 庞大的社区和丰富的文档:遇到问题时,很容易找到解决方案和学习资源。
  6. 商业友好型许可 (BSD):允许你在商业产品中免费使用。

🛠️ 环境搭建:准备好你的第一个 OpenCV 程序

在开始编码之前,你需要先安装 OpenCV。安装过程因操作系统而异。

基本步骤:

  1. 下载 OpenCV:

    • 访问 OpenCV 官方网站 ( https://opencv.org/releases/ ) 下载最新的稳定版本。通常你会下载源码包。
  2. 编译与安装:

    • Windows:
      • 你可以下载预编译的库 (pre-built libraries),解压后配置 Visual Studio 的包含目录 (Include Directories)、库目录 (Library Directories) 和链接器输入 (Linker Input)。
      • 或者使用 CMake 配合 Visual Studio (或其他编译器如 MinGW) 从源码编译。这能让你更好地控制编译选项。
    • Linux (Ubuntu/Debian 为例):
      • 可以通过包管理器安装:sudo apt update && sudo apt install libopencv-dev python3-opencv (这通常会安装一个较稳定但可能不是最新的版本)。
      • 推荐从源码编译安装,使用 CMake。这能确保你使用最新版本并可以自定义模块。你需要先安装必要的依赖项 (如 build-essential, cmake, git, libgtk2.0-dev, pkg-config, libavcodec-dev, libavformat-dev, libswscale-dev 等)。
    • macOS:
      • 可以使用 Homebrew:brew install opencv
      • 同样也可以从源码编译。
  3. 配置你的 IDE/编译器:

    • 你需要告诉你的编译器在哪里找到 OpenCV 的头文件 (.hpp, .h) 和库文件 (.lib, .dll on Windows; .so, .a on Linux/macOS)。

    • Visual Studio:

      • 项目属性 -> VC++ 目录 -> 包含目录:添加 path_to_opencv/build/include
      • 项目属性 -> VC++ 目录 -> 库目录:添加 path_to_opencv/build/your_compiler_arch/your_vc_version/lib (例如 x64/vc16/lib)。
      • 项目属性 -> 链接器 -> 输入 -> 附加依赖项:添加需要的 .lib 文件名 (例如 opencv_worldXYZ.lib,其中 XYZ 是版本号)。调试版通常带 ‘d’ 后缀,如 opencv_worldXYZd.lib
    • CMake (推荐):
      CMake 是一个跨平台的构建系统,非常适合 C++ 项目。创建一个 CMakeLists.txt 文件来管理你的项目编译:

      cmake_minimum_required(VERSION 3.10)
      project(MyOpenCVApp)set(CMAKE_CXX_STANDARD 11) # 或更高版本# 找到 OpenCV 包
      find_package(OpenCV REQUIRED)# 包含 OpenCV 头文件目录
      include_directories(${OpenCV_INCLUDE_DIRS})# 添加你的源文件
      add_executable(MyOpenCVApp main.cpp)# 链接 OpenCV 库
      target_link_libraries(MyOpenCVApp ${OpenCV_LIBS})
      

      然后使用 CMake 生成构建文件 (例如 Makefile 或 Visual Studio 项目),再进行编译。

      mkdir build
      cd build
      cmake ..
      make # 或者在 Visual Studio 中打开生成的 .sln 文件编译
      

环境变量 (尤其在 Windows 上):
确保包含 OpenCV bin 目录 (例如 path_to_opencv/build/your_compiler_arch/your_vc_version/bin) 的路径已添加到系统的 PATH 环境变量中,这样程序运行时才能找到所需的 .dll 文件。


🖼️ 核心概念与第一个程序:加载、显示和保存图像

OpenCV 中最核心的数据结构之一是 cv::Mat。它用于表示 n 维稠密数组,可以存储图像、特征向量、矩阵等。你可以把它想象成一个灵活的画布或数据容器。

我们的第一个程序:读取一张图片,显示它,然后保存它。

假设你有一张名为 input.jpg 的图片和你的 C++ 源文件 (例如 main.cpp) 在同一个目录下。

// main.cpp
#include <opencv2/core.hpp>     // 核心功能,如 cv::Mat
#include <opencv2/imgcodecs.hpp> // 图像读取和写入 (imread, imwrite)
#include <opencv2/highgui.hpp>  // GUI 功能 (imshow, waitKey, namedWindow)
#include <iostream>             // 用于控制台输出int main() {// 1. 读取图像// cv::imread(filename, flags)//  - filename: 图像文件的路径//  - flags://    - cv::IMREAD_COLOR: 以彩色模式加载图像 (默认)//    - cv::IMREAD_GRAYSCALE: 以灰度模式加载图像//    - cv::IMREAD_UNCHANGED: 加载包含 Alpha 通道的图像std::string imagePath = "input.jpg"; // 替换为你的图片路径cv::Mat image = cv::imread(imagePath, cv::IMREAD_COLOR);// 2. 检查图像是否成功加载if (image.empty()) {std::cerr << "错误:无法加载图像 " << imagePath << std::endl;return -1; // 返回错误码}// 3. 显示图像// cv::namedWindow(windowName, flags) - 创建一个窗口 (可选,但推荐)//  - windowName: 窗口的标题//  - flags://    - cv::WINDOW_AUTOSIZE: 窗口大小根据图像自动调整 (默认)//    - cv::WINDOW_NORMAL: 窗口可以被用户调整大小std::string windowName = "我的第一个 OpenCV 图像";cv::namedWindow(windowName, cv::WINDOW_AUTOSIZE);// cv::imshow(windowName, imageMatrix) - 在指定窗口中显示图像cv::imshow(windowName, image);// 4. 等待按键// cv::waitKey(delay) - 等待指定的毫秒数,如果 delay <= 0,则无限等待直到用户按键// 返回值为按键的 ASCII 码,如果没有按键且超时则返回 -1std::cout << "图像已显示。按任意键关闭窗口并保存图像..." << std::endl;cv::waitKey(0); // 等待用户按键// 5. 保存图像// cv::imwrite(filename, imageMatrix)std::string outputImagePath = "output.png"; // 可以保存为不同格式,如 .png, .bmp 等bool success = cv::imwrite(outputImagePath, image);if (success) {std::cout << "图像已成功保存到 " << outputImagePath << std::endl;} else {std::cerr << "错误:无法保存图像到 " << outputImagePath << std::endl;}// 6. 关闭所有 OpenCV 创建的窗口 (可选,程序结束时会自动关闭)cv::destroyAllWindows();return 0; // 程序成功结束
}

代码解释:

  1. 包含头文件
    • opencv2/core.hpp: 包含了 cv::Mat 等核心数据结构的定义。
    • opencv2/imgcodecs.hpp: 包含了图像编解码函数,如 cv::imread() (读取) 和 cv::imwrite() (保存)。
    • opencv2/highgui.hpp: 包含了高级 GUI 函数,如 cv::namedWindow() (创建窗口)、cv::imshow() (显示图像) 和 cv::waitKey() (等待按键)。
  2. cv::Mat image = cv::imread(imagePath, cv::IMREAD_COLOR);
    • 创建一个 cv::Mat 对象 image
    • cv::imread() 函数尝试从 imagePath 指定的路径加载图像。
    • cv::IMREAD_COLOR 表示以彩色模式加载。
  3. if (image.empty())
    • 这是一个非常重要的错误检查。如果 cv::imread() 找不到文件或文件格式不支持,它会返回一个空的 cv::Mat 对象。.empty() 方法可以检查 cv::Mat 是否为空。
  4. cv::namedWindow(windowName, cv::WINDOW_AUTOSIZE);
    • 创建一个名为 “我的第一个 OpenCV 图像” 的窗口。cv::WINDOW_AUTOSIZE 使窗口大小自动适应图像。
  5. cv::imshow(windowName, image);
    • 在之前创建的窗口中显示 image
  6. cv::waitKey(0);
    • 这是使图像窗口保持可见的关键。cv::waitKey() 等待用户按键。参数 0 表示无限期等待。如果没有这个函数,窗口会一闪而过。
  7. cv::imwrite(outputImagePath, image);
    • image 对象中的图像数据保存到名为 output.png 的文件中。OpenCV 会根据文件扩展名自动选择编码格式。
  8. cv::destroyAllWindows();
    • 关闭所有由 OpenCV 创建的窗口。虽然程序结束时通常会自动清理,但这是一种良好的编程习惯。

编译和运行:

  • 使用 CMake:
    # 假设你的 CMakeLists.txt 和 main.cpp 在同一目录
    mkdir build
    cd build
    cmake ..
    make        # 或者 cmake --build .  (在 Windows 上,如果生成的是 Visual Studio 项目,则用 VS 打开并编译)
    ./MyOpenCVApp # 运行程序
    
  • 直接使用 g++ (Linux/macOS 示例,需要正确配置 pkg-config 或手动指定包含和库路径):
    g++ main.cpp -o MyOpenCVApp `pkg-config --cflags --libs opencv4` # opencv4 可能需要根据你的版本调整
    ./MyOpenCVApp
    
    (对于 pkg-config opencv4 不可用的情况,你需要手动指定 -I-L 标志以及 -l 链接库,例如:g++ main.cpp -o MyOpenCVApp -I/usr/local/include/opencv4 -L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_highgui)

🎨 基本图像操作

掌握了图像的读写和显示后,我们来看看一些基本的图像操作。

1. 获取图像属性

cv::Mat 对象包含很多关于图像的信息:

#include <opencv2/opencv.hpp> // 包含所有常用模块
#include <iostream>int main() {cv::Mat image = cv::imread("input.jpg");if (image.empty()) {std::cerr << "无法加载图像!" << std::endl;return -1;}// 获取图像尺寸int rows = image.rows; // 高度 (像素)int cols = image.cols; // 宽度 (像素)std::cout << "图像尺寸: " << cols << "x" << rows << std::endl;// 获取图像通道数int channels = image.channels();std::cout << "图像通道数: " << channels << std::endl;// 彩色图像通常有 3 个通道 (BGR),灰度图像有 1 个通道// 获取图像深度 (每个通道中像素值的类型)// CV_8U (8位无符号整数), CV_16S (16位有符号整数), CV_32F (32位浮点数) 等int depth = image.depth();std::cout << "图像深度类型: ";switch (depth) {case CV_8U:  std::cout << "CV_8U (8-bit unsigned)"; break;case CV_8S:  std::cout << "CV_8S (8-bit signed)"; break;case CV_16U: std::cout << "CV_16U (16-bit unsigned)"; break;case CV_16S: std::cout << "CV_16S (16-bit signed)"; break;case CV_32S: std::cout << "CV_32S (32-bit signed)"; break;case CV_32F: std::cout << "CV_32F (32-bit float)"; break;case CV_64F: std::cout << "CV_64F (64-bit float)"; break;default:     std::cout << "未知"; break;}std::cout << std::endl;// 获取图像类型 (深度 + 通道数的组合)// 例如 CV_8UC3 表示 8位无符号整数,3通道int type = image.type();std::cout << "图像类型 (如 CV_8UC3): " << type << std::endl;// 你可以用 image.type() == CV_8UC3 来检查cv::imshow("属性演示", image);cv::waitKey(0);cv::destroyAllWindows();return 0;
}

2. 颜色空间转换

最常见的转换是将彩色图像转换为灰度图像。

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp> // 图像处理模块,包含颜色转换int main() {cv::Mat colorImage = cv::imread("input.jpg");if (colorImage.empty()) {std::cerr << "无法加载彩色图像!" << std::endl;return -1;}cv::Mat grayImage;// cv::cvtColor(sourceImage, destinationImage, conversionCode)cv::cvtColor(colorImage, grayImage, cv::COLOR_BGR2GRAY);// 注意:OpenCV 默认以 BGR 顺序加载彩色图像,而不是 RGBcv::imshow("彩色图像", colorImage);cv::imshow("灰度图像", grayImage);cv::waitKey(0);cv::imwrite("gray_output.jpg", grayImage);std::cout << "灰度图像已保存为 gray_output.jpg" << std::endl;cv::destroyAllWindows();return 0;
}

其他常见的颜色空间转换包括 COLOR_BGR2HSV, COLOR_BGR2Lab 等。

3. 图像缩放 (Resizing)

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>int main() {cv::Mat originalImage = cv::imread("input.jpg");if (originalImage.empty()) {std::cerr << "无法加载图像!" << std::endl;return -1;}cv::Mat resizedImage;// 方法1: 指定目标尺寸cv::Size newSize(300, 200); // 宽 300, 高 200// cv::resize(source, destination, dsize, fx, fy, interpolation)// dsize: 目标尺寸// fx, fy: x 和 y 方向的缩放因子 (如果 dsize 非零,则忽略)// interpolation: 插值方法//   - cv::INTER_LINEAR: 线性插值 (常用)//   - cv::INTER_NEAREST: 最近邻插值//   - cv::INTER_CUBIC: 双三次插值 (效果好但慢)cv::resize(originalImage, resizedImage, newSize, 0, 0, cv::INTER_LINEAR);cv::imshow("原始图像", originalImage);cv::imshow("缩放图像 (指定尺寸)", resizedImage);cv::waitKey(0);// 方法2: 指定缩放因子cv::Mat scaledImage;double scaleX = 0.5; // 缩小到50%宽度double scaleY = 0.5; // 缩小到50%高度cv::resize(originalImage, scaledImage, cv::Size(), scaleX, scaleY, cv::INTER_LINEAR);cv::imshow("缩放图像 (指定因子)", scaledImage);cv::waitKey(0);cv::destroyAllWindows();return 0;
}

4. 图像模糊 (Blurring/Smoothing)

模糊可以用于降噪或作为某些算法的预处理步骤。高斯模糊是最常用的模糊方法之一。

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>int main() {cv::Mat image = cv::imread("input.jpg");if (image.empty()) {std::cerr << "无法加载图像!" << std::endl;return -1;}cv::Mat blurredImage;// cv::GaussianBlur(source, destination, ksize, sigmaX, sigmaY, borderType)// ksize: 高斯核大小,必须是正奇数,如 cv::Size(5, 5)// sigmaX: X方向的高斯核标准差// sigmaY: Y方向的高斯核标准差 (如果为0,则从 sigmaX 计算;如果都为0,则从 ksize 计算)cv::GaussianBlur(image, blurredImage, cv::Size(15, 15), 0);cv::imshow("原始图像", image);cv::imshow("高斯模糊", blurredImage);cv::waitKey(0);cv::destroyAllWindows();return 0;
}

5. 边缘检测 (Edge Detection)

Canny 边缘检测是一种流行的边缘检测算法。

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>int main() {cv::Mat image = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE); // Canny 通常在灰度图上操作if (image.empty()) {std::cerr << "无法加载图像!" << std::endl;return -1;}cv::Mat edges;// cv::Canny(source, destination, threshold1, threshold2, apertureSize, L2gradient)// threshold1: 第一个阈值 (低阈值)// threshold2: 第二个阈值 (高阈值)// apertureSize: Sobel 算子的孔径大小 (通常为 3)// L2gradient: 是否使用更精确的 L2 范数计算图像梯度幅值 (默认 false)cv::Canny(image, edges, 50, 150);cv::imshow("原始灰度图", image);cv::imshow("Canny 边缘", edges);cv::waitKey(0);cv::destroyAllWindows();return 0;
}

📹 处理视频

OpenCV 同样可以轻松处理视频,无论是从文件读取还是从摄像头实时捕获。

1. 从文件读取视频

#include <opencv2/opencv.hpp>
#include <iostream>int main() {// 打开视频文件cv::VideoCapture cap("my_video.mp4"); // 替换为你的视频文件路径// 检查视频是否成功打开if (!cap.isOpened()) {std::cerr << "错误:无法打开视频文件!" << std::endl;return -1;}cv::Mat frame;std::string windowName = "视频播放";cv::namedWindow(windowName, cv::WINDOW_AUTOSIZE);while (true) {// 读取一帧// cap.read(frame) 或 cap >> frame;bool success = cap.read(frame);// 如果读取失败 (例如视频结束),则退出循环if (!success) {std::cout << "视频播放完毕或读取帧失败。" << std::endl;break;}// 在这里可以对 frame 进行处理,例如转灰度、边缘检测等// cv::cvtColor(frame, frame, cv::COLOR_BGR2GRAY);// 显示帧cv::imshow(windowName, frame);// 等待 25毫秒,如果按下 'q' 键或 ESC 键则退出// 视频的帧率通常是 25-30 fps,所以等待时间是 1000/fpschar key = (char)cv::waitKey(25);if (key == 'q' || key == 27) { // 27 是 ESC 键的 ASCII 码break;}}// 释放 VideoCapture 对象cap.release();// 关闭所有窗口cv::destroyAllWindows();return 0;
}

2. 从摄像头捕获视频

#include <opencv2/opencv.hpp>
#include <iostream>int main() {// 打开默认摄像头 (通常索引为 0)// 如果有多个摄像头,可以尝试 1, 2, ...cv::VideoCapture cap(0);if (!cap.isOpened()) {std::cerr << "错误:无法打开摄像头!" << std::endl;return -1;}// 可以设置摄像头的属性,例如帧的宽度和高度// cap.set(cv::CAP_PROP_FRAME_WIDTH, 640);// cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480);cv::Mat frame;std::string windowName = "摄像头实时画面";cv::namedWindow(windowName, cv::WINDOW_AUTOSIZE);std::cout << "按 'q' 或 ESC 键退出..." << std::endl;while (true) {cap >> frame; // 或者 cap.read(frame);if (frame.empty()) {std::cerr << "错误:捕获到空帧!" << std::endl;break;}// 对帧进行处理 (可选)// 例如,水平翻转图像,因为摄像头画面通常是镜像的cv::Mat flippedFrame;cv::flip(frame, flippedFrame, 1); // 1 表示水平翻转, 0 表示垂直, -1 表示都翻转cv::imshow(windowName, flippedFrame); // 显示翻转后的帧char key = (char)cv::waitKey(1); // 等待 1ms,几乎是实时if (key == 'q' || key == 27) {break;}}cap.release();cv::destroyAllWindows();return 0;
}

🖌️ 在图像上绘制图形和文本

OpenCV 提供了在图像上绘制点、线、矩形、圆形、文本等功能,这对于标注、调试和结果可视化非常有用。

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp> // 绘图函数主要在这里int main() {// 创建一个黑色背景图像cv::Mat canvas = cv::Mat::zeros(cv::Size(500, 500), CV_8UC3); // 500x500, 3通道 (BGR)// 1. 绘制直线// cv::line(image, pt1, pt2, color, thickness, lineType, shift)cv::Point pt1(50, 50);cv::Point pt2(450, 50);cv::Scalar lineColor(0, 255, 0); // BGR 格式:绿色int thickness = 2;cv::line(canvas, pt1, pt2, lineColor, thickness);// 2. 绘制矩形// cv::rectangle(image, pt1, pt2, color, thickness, lineType, shift)// pt1: 左上角点, pt2: 右下角点// 或者 cv::rectangle(image, Rect, color, thickness, ...)cv::Rect rect(100, 100, 200, 150); // x, y, width, heightcv::Scalar rectColor(255, 0, 0);   // 蓝色cv::rectangle(canvas, rect, rectColor, thickness);// 绘制填充矩形cv::rectangle(canvas, cv::Point(120,120), cv::Point(180,180), cv::Scalar(255,255,0), cv::FILLED);// 3. 绘制圆形// cv::circle(image, center, radius, color, thickness, lineType, shift)cv::Point center(350, 350);int radius = 50;cv::Scalar circleColor(0, 0, 255); // 红色cv::circle(canvas, center, radius, circleColor, thickness);// 绘制填充圆形cv::circle(canvas, cv::Point(350,150), 30, cv::Scalar(0,255,255), cv::FILLED);// 4. 绘制椭圆// cv::ellipse(image, center, axes, angle, startAngle, endAngle, color, thickness, ...)cv::Point ellipseCenter(150, 400);cv::Size axes(70, 30); // 长轴和短轴长度double angle = 45; // 旋转角度double startAngle = 0;double endAngle = 360; // 完整的椭圆cv::Scalar ellipseColor(255, 0, 255); // 紫色cv::ellipse(canvas, ellipseCenter, axes, angle, startAngle, endAngle, ellipseColor, thickness);// 5. 放置文本// cv::putText(image, text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin)std::string text = "OpenCV Rocks!";cv::Point textOrg(50, 480); // 文本左下角起始点int fontFace = cv::FONT_HERSHEY_SIMPLEX;double fontScale = 1.0;cv::Scalar textColor(255, 255, 255); // 白色cv::putText(canvas, text, textOrg, fontFace, fontScale, textColor, thickness);cv::imshow("绘图演示", canvas);cv::waitKey(0);cv::destroyAllWindows();return 0;
}

🚀 接下来学什么?

恭喜你!你已经迈出了 OpenCV (C/C++) 学习的第一步。这只是冰山一角,OpenCV 的世界非常广阔。

建议的学习路径:

  1. 深入 cv::Mat:理解其内存管理、ROI (Region of Interest)、拷贝和克隆的区别。
  2. 像素级操作:学习如何直接访问和修改图像的像素值 (使用 .at<type>(row, col) 或指针)。
  3. 更多图像处理技术
    • 形态学操作 (腐蚀、膨胀、开运算、闭运算)
    • 直方图计算与均衡化
    • 图像阈值化 (二值化)
    • 模板匹配
    • 霍夫变换 (检测直线和圆)
  4. 特征提取与匹配:SIFT, SURF, ORB, AKAZE 等特征点检测与描述。
  5. 物体检测:Haar 级联分类器 (用于人脸检测等),以及更现代的基于深度学习的方法 (OpenCV 的 DNN 模块可以加载预训练模型)。
  6. 机器学习模块 cv::ml:支持向量机 (SVM)、K最近邻 (KNN) 等。
  7. 相机标定与3D重建calib3d 模块。
  8. 探索 OpenCV 的 contrib 模块:包含许多实验性或较新的功能。

重要资源:

  • OpenCV 官方文档和教程: https://docs.opencv.org/ (这是你最好的朋友!)
  • OpenCV Q&A 论坛: https://forum.opencv.org/
  • 大量的在线教程和博客 (搜索特定功能,如 “OpenCV C++ tutorial blur image”)

实践是学习编程的最佳方式。尝试修改示例代码,用自己的图像和视频进行实验,并挑战一些小的计算机视觉项目。

祝你在计算机视觉的探索之旅中一切顺利!🚀


相关文章:

  • UI自动化测试中,一个完整的断言应所需要考虑的问题
  • Linux基础开发工具大全
  • IEC 60601-2-16:2025 标准解析
  • muduo库Poller模块详解
  • B2C 商城转型指南:传统企业如何用 ZKmall模板商城实现电商化
  • 在多个SpringBoot程序中./相对路径下隐患、文件覆盖问题
  • 【C/C++】C++中引用类型私有成员的设计与应用
  • Git - 2( 12000 字详解 )
  • 【leetcode】144. 二叉树的前序遍历
  • SpringBoot--Bean管理详解
  • 双轨雷达波测流系统:开启水文监测新时代
  • math toolkit for real-time development读书笔记一-三角函数快速计算(1)
  • 频域中的反射-信号完整性分析
  • 如何阅读、学习 Tcc (Tiny C Compiler) 源代码?如何解析 Tcc 源代码?
  • 【踩坑】修复Cloudflare的Origin Rules端口重定向不生效
  • 【C#】 lock 关键字
  • RabbitMQ 扇形交换器工作原理详解
  • Ubuntu24.04 安装 5080显卡驱动以及cuda
  • python Excel操作,将一个工作表中的sheet页复制到另一个工作表中(包括单元格的内容、样式、格式等)
  • 进阶-数据结构部分:1、数据结构入门
  • 天算星座二期首批卫星成功发射,将助力6G空天信息基础设施建设
  • 体坛联播|热刺追平单赛季输球纪录,世俱杯或创收20亿美元
  • 中方是否计划解除或调整稀土出口管制?外交部回应
  • 张国清将赴俄罗斯举行中俄“长江—伏尔加河”地方合作理事会第五次会议和“东北—远东”政府间合作委员会双方主席会晤
  • 美国将与阿联酋合作建立海外最大的人工智能数据中心
  • 王东杰评《国家与学术》︱不“国”不“故”的“国学”