书接上回,前面介绍了一些基本应用,本篇则着重介绍一些比较复杂的应用。
附:本文所用例子中使用的Opencv库OpenCV4.5.4版本编译好的库

五、特征提取与描述

5.1 角点检测:Harris 角点和 Shi-Tomasi 角点

5.1.1 Harris 角点检测:cornerHarris 函数

Harris 角点检测是一种经典的角点检测算法,基于图像灰度的二阶矩矩阵。

//Harris角点检测
void MainWindow::on_bt15_clicked()
{QString fileName = QFileDialog::getOpenFileName(this,QString("文件对话框"),"/",QString("图片(*.jpg *.png);"));// 读取图像Mat image = imread(fileName.toStdString(), IMREAD_GRAYSCALE);//这里要以灰度模式读取图像if (image.empty()) {qDebug()  << QString("无法读取图像!");return;}// Harris角点检测Mat dst, dst_norm, dst_norm_scaled;dst = Mat::zeros(image.size(), CV_32FC1);// 检测角点cornerHarris(image, dst, 2, 3, 0.04);// 归一化normalize(dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat());convertScaleAbs(dst_norm, dst_norm_scaled);// 在原图上绘制角点for (int j = 0; j < dst_norm.rows; j++) {for (int i = 0; i < dst_norm.cols; i++) {if ((int)dst_norm.at<float>(j, i) > 100) {circle(dst_norm_scaled, Point(i, j), 5, Scalar(0), 2, 8, 0);}}}// 显示原始图像和处理后的图像namedWindow(QString("Src").toStdString(), WINDOW_AUTOSIZE);namedWindow(QString("Dsc_Harris").toStdString(), WINDOW_AUTOSIZE);imshow(QString("Src").toStdString(), image);imshow(QString("Dsc_Harris").toStdString(), dst_norm_scaled);waitKey(0);return;
}

运行结果如下:
在这里插入图片描述

5.1.2 Shi-Tomasi 角点检测:goodFeaturesToTrack 函数

Shi-Tomasi 角点检测是 Harris 角点检测的改进版本,更适合于特征点跟踪。

//Shi-Tomasi角点检测
void MainWindow::on_bt16_clicked()
{QString fileName = QFileDialog::getOpenFileName(this,QString("文件对话框"),"E:/test/QtTest/WidgetTest/TestOpencv/bin/debug64/Out/OutImg/",QString("图片(*.jpg *.png);"));// 读取图像Mat image = imread(fileName.toStdString(), IMREAD_GRAYSCALE);//这里要以灰度模式读取图像if (image.empty()) {qDebug()  << QString("无法读取图像!");return;}// Shi-Tomasi角点检测vector<Point2f> corners;int maxCorners = 100;double qualityLevel = 0.01;double minDistance = 10;goodFeaturesToTrack(image, corners, maxCorners, qualityLevel, minDistance);// 在原图上绘制角点Mat output = image.clone();cvtColor(output, output, COLOR_GRAY2BGR);for (size_t i = 0; i < corners.size(); i++) {circle(output, corners[i], 5, Scalar(0, 0, 255), 2);}// 显示原始图像和处理后的图像namedWindow(QString("Src").toStdString(), WINDOW_AUTOSIZE);namedWindow(QString("Dsc_Tomasi").toStdString(), WINDOW_AUTOSIZE);imshow(QString("Src").toStdString(), image);imshow(QString("Dsc_Tomasi").toStdString(), output);waitKey(0);return;
}

运行结果如下:

在这里插入图片描述

5.2 SIFT 和 SURF 特征

SIFT(尺度不变特征变换)和 SURF(加速稳健特征)是两种经典的尺度不变特征提取算法。

5.2.1 SIFT 特征提取
//SIFT 特征提取
void MainWindow::on_bt17_clicked()
{QString fileName = QFileDialog::getOpenFileName(this,QString("文件对话框"),"E:/test/QtTest/WidgetTest/TestOpencv/bin/debug64/Out/OutImg/",QString("图片(*.jpg *.png);"));// 读取图像Mat image = imread(fileName.toStdString(), IMREAD_GRAYSCALE);//这里要以灰度模式读取图像if (image.empty()) {qDebug()  << QString("无法读取图像!");return;}// 创建SIFT检测器Ptr<SIFT> sift = SIFT::create();// 检测关键点和计算描述符vector<KeyPoint> keypoints;Mat descriptors;sift->detectAndCompute(image, noArray(), keypoints, descriptors);// 在原图上绘制关键点Mat output;drawKeypoints(image, keypoints, output, Scalar::all(-1), DrawMatchesFlags::DEFAULT);// 显示原始图像和处理后的图像namedWindow(QString("Src").toStdString(), WINDOW_AUTOSIZE);namedWindow(QString("Dsc_SIFT").toStdString(), WINDOW_AUTOSIZE);imshow(QString("Src").toStdString(), image);imshow(QString("Dsc_SIFT").toStdString(), output);waitKey(0);return;
}

运行结果如下:
在这里插入图片描述

5.2.2 SURF 特征提取
//SURF 特征提取
void MainWindow::on_bt18_clicked()
{QString fileName = QFileDialog::getOpenFileName(this,QString("文件对话框"),"E:/test/QtTest/WidgetTest/TestOpencv/bin/debug64/Out/OutImg/",QString("图片(*.jpg *.png);"));// 读取图像Mat image = imread(fileName.toStdString(), IMREAD_GRAYSCALE);//这里要以灰度模式读取图像if (image.empty()) {qDebug()  << QString("无法读取图像!");return;}// 创建SURF检测器double hessianThreshold = 400;//注意,这里直接执行时会崩溃,不要慌,代码没问题。//这是由于SURF(加速稳健特征)算法已被移至opencv_contrib模块,并受到专利保护,因此直接使用会导致程序崩溃//SURF 属于非免费算法,OpenCV 4.x 默认不包含该模块,需手动编译带opencv_contrib模块的opencv库Ptr<SURF> surf = SURF::create(hessianThreshold);// 检测关键点和计算描述符vector<KeyPoint> keypoints;Mat descriptors;surf->detectAndCompute(image, noArray(), keypoints, descriptors);// 在原图上绘制关键点Mat output;drawKeypoints(image, keypoints, output, Scalar::all(-1), DrawMatchesFlags::DEFAULT);// 显示原始图像和处理后的图像namedWindow(QString("Src").toStdString(), WINDOW_AUTOSIZE);namedWindow(QString("Dsc_SURF").toStdString(), WINDOW_AUTOSIZE);imshow(QString("Src").toStdString(), image);imshow(QString("Dsc_SURF").toStdString(), output);waitKey(0);return;
}

由于我的opencv库没有带opencv_contrib模块,故而出现如下报错
在这里插入图片描述
重新使用带opencv_contrib模块的opencv库,如下,运行正常。
在这里插入图片描述

5.3 ORB 特征

ORB(Oriented FAST and Rotated BRIEF)是一种高效的特征提取算法,结合了 FAST 角点检测和 BRIEF 描述符。

//ORB 特征提取
void MainWindow::on_bt19_clicked()
{QString fileName = QFileDialog::getOpenFileName(this,QString("文件对话框"),"/",QString("图片(*.jpg *.png);"));// 读取图像Mat image = imread(fileName.toStdString(), IMREAD_GRAYSCALE);//这里要以灰度模式读取图像if (image.empty()) {qDebug()  << QString("无法读取图像!");return;}// 创建ORB检测器Ptr<ORB> orb = ORB::create();// 检测关键点和计算描述符vector<KeyPoint> keypoints;Mat descriptors;orb->detectAndCompute(image, noArray(), keypoints, descriptors);// 在原图上绘制关键点Mat output;drawKeypoints(image, keypoints, output, Scalar::all(-1), DrawMatchesFlags::DEFAULT);// 显示原始图像和处理后的图像namedWindow(QString("Src").toStdString(), WINDOW_AUTOSIZE);namedWindow(QString("Dsc_ORB").toStdString(), WINDOW_AUTOSIZE);imshow(QString("Src").toStdString(), image);imshow(QString("Dsc_ORB").toStdString(), output);waitKey(0);return;
}

运行结果如下:
在这里插入图片描述

六、目标检测与识别

6.1 Haar 级联分类器:人脸检测

Haar 级联分类器是一种基于机器学习的目标检测方法,常用于人脸检测。
Haar级联分类器这些xml文件在哪呢,就按opencv的安装目录下的etc文件夹,拷贝到测试程序生成目录,引入进来。

//Haar 级联分类
void MainWindow::on_bt20_clicked()
{QString fileName = QFileDialog::getOpenFileName(this,QString("文件对话框"),"/",QString("图片(*.jpg *.png);"));// 读取图像Mat image = imread(fileName.toStdString(), IMREAD_COLOR);if (image.empty()) {qDebug()  << QString("无法读取图像!");return;}// 加载分类器,分类器在opencv安装目录下的etc文件夹,拷贝到测试程序生成目录,引入进来QString strXmlPath = QCoreApplication::applicationDirPath() + QString("/etc/haarcascades/haarcascade_frontalface_default.xml");CascadeClassifier face_cascade;if (!face_cascade.load(strXmlPath.toStdString())) {qDebug()  << QString("无法加载分类器文件!");return;}// 转换为灰度图Mat gray;cvtColor(image, gray, COLOR_BGR2GRAY);equalizeHist(gray, gray);// 检测人脸vector<Rect> faces;face_cascade.detectMultiScale(gray, faces, 1.1, 3, 0, Size(30, 30));// 在原图上绘制检测结果for (size_t i = 0; i < faces.size(); i++) {rectangle(image, faces[i], Scalar(255, 0, 0), 2);}// 显示原始图像和处理后的图像namedWindow(QString("Src").toStdString(), WINDOW_AUTOSIZE);//namedWindow(QString("Dsc_Haar").toStdString(), WINDOW_AUTOSIZE);imshow(QString("Src").toStdString(), image);//imshow(QString("Dsc_Haar").toStdString(), output);waitKey(0);return;
}

用我们的测试图片发现,识别很差:
在这里插入图片描述
我们还是换成人物图片进行测试,就以经典的Lena头像,进行测试,发现完美找到人脸部分

在这里插入图片描述
我们在随机换一个人像图片测试,也能正确找到人脸
在这里插入图片描述

6.2 HOG+SVM:行人检测

HOG(方向梯度直方图)结合 SVM(支持向量机)是一种经典的行人检测方法。

//HOG+SVM 行人检测
void MainWindow::on_bt21_clicked()
{QString fileName = QFileDialog::getOpenFileName(this,QString("文件对话框"),"/",QString("图片(*.jpg *.png);"));// 读取图像Mat image = imread(fileName.toStdString(), IMREAD_COLOR);if (image.empty()) {qDebug()  << QString("无法读取图像!");return;}// 创建HOG描述符和SVM检测器HOGDescriptor hog;hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());// 检测行人vector<Rect> found;vector<double> weights;hog.detectMultiScale(image, found, weights, 0, Size(8, 8), Size(32, 32), 1.05, 2);// 在原图上绘制检测结果for (size_t i = 0; i < found.size(); i++) {rectangle(image, found[i], Scalar(0, 255, 0), 2);}// 显示原始图像和处理后的图像namedWindow(QString("Src").toStdString(), WINDOW_AUTOSIZE);//namedWindow(QString("Dsc_Haar").toStdString(), WINDOW_AUTOSIZE);imshow(QString("Src").toStdString(), image);//imshow(QString("Dsc_Haar").toStdString(), output);waitKey(0);return;
}

找了一个真实的街道行人图片进行测试,基本将所有行人都识别了出来,结果如下:
在这里插入图片描述

七、视频处理

7.1 视频读取与显示

//视频读取显示
void MainWindow::on_bt22_clicked()
{QString fileName = QFileDialog::getOpenFileName(this,QString("文件对话框"),"/",QString("视频文件(*.mp4);"));// 读取视频VideoCapture cap(fileName.toStdString());if (!cap.isOpened()) {qDebug() << QString("无法打开视频文件!");return;}// 获取视频帧率和尺寸double fps = cap.get(CAP_PROP_FPS);int width = cap.get(CAP_PROP_FRAME_WIDTH);int height = cap.get(CAP_PROP_FRAME_HEIGHT);qDebug() << QString("视频帧率: ") << fps;qDebug() << QString("视频尺寸: ") << width << "x" << height;// 创建窗口namedWindow(QString("VideoPlay").toStdString(), WINDOW_AUTOSIZE);// 逐帧读取并显示视频Mat frame;while (true) {// 读取一帧cap >> frame;// 检查是否读取到帧if (frame.empty()) {qDebug() << QString("视频播放完毕!");break;}// 显示帧imshow(QString("VideoPlay").toStdString(), frame);// 等待按键事件,控制播放速度if (waitKey(25) >= 0) {break;}}// 释放资源cap.release();destroyAllWindows();return;
}

这是视频开启效果:
在这里插入图片描述

7.2 视频写入

现在使用VideoWriter 在测试一下opencv对视频的写操作。

//视频写入
void MainWindow::on_bt23_clicked()
{QString fileName = QFileDialog::getOpenFileName(this,QString("文件对话框"),"/",QString("视频文件(*.mp4);"));// 读取视频VideoCapture cap(fileName.toStdString());if (!cap.isOpened()) {qDebug() << QString("无法打开视频文件!");return;}// 获取视频帧率和尺寸double fps = cap.get(CAP_PROP_FPS);int width = cap.get(CAP_PROP_FRAME_WIDTH);int height = cap.get(CAP_PROP_FRAME_HEIGHT);// 创建视频写入器VideoWriter writer(QString("output.avi").toStdString(), VideoWriter::fourcc('M', 'J', 'P', 'G'), fps, Size(width, height));// 检查写入器是否成功创建if (!writer.isOpened()) {qDebug() << QString("无法创建视频写入器!");return;}// 逐帧处理并写入视频Mat frame;while (true) {// 读取一帧cap >> frame;// 检查是否读取到帧if (frame.empty()) {qDebug() << QString("视频处理完毕!");break;}// 在这里可以对帧进行处理,例如转换为灰度图Mat gray;cvtColor(frame, gray, COLOR_BGR2GRAY);cvtColor(gray, frame, COLOR_GRAY2BGR);  // 转回BGR以保持通道数一致// 写入帧writer.write(frame);// 显示帧imshow("VideoOpt", frame);// 等待按键事件if (waitKey(25) >= 0) {break;}}// 释放资源cap.release();writer.release();destroyAllWindows();return;
}

这是运行后,生成的新的灰度视频:
在这里插入图片描述

7.3 背景减除

背景减除是视频分析中的常用技术,用于从视频中分离出前景物体。

//背景减除
void MainWindow::on_bt24_clicked()
{QString fileName = QFileDialog::getOpenFileName(this,QString("文件对话框"),"E:/test/QtTest/WidgetTest/TestOpencv/bin/debug64/Out/OutImg/",QString("视频文件(*.mp4);"));// 读取视频VideoCapture cap(fileName.toStdString());if (!cap.isOpened()) {qDebug() << QString("无法打开视频文件!");return;}// 创建背景减除器Ptr<BackgroundSubtractor> bg_subtractor = createBackgroundSubtractorMOG2();// 创建窗口namedWindow(QString("Src_Video").toStdString(), WINDOW_AUTOSIZE);namedWindow(QString("ForeMask").toStdString(), WINDOW_AUTOSIZE);namedWindow(QString("Foreground").toStdString(), WINDOW_AUTOSIZE);// 逐帧处理视频Mat frame, fg_mask, fg;while (true) {// 读取一帧cap >> frame;// 检查是否读取到帧if (frame.empty()) {qDebug() << QString("视频处理完毕!");break;}// 应用背景减除bg_subtractor->apply(frame, fg_mask);// 对前景掩码进行形态学操作,减少噪声Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(5, 5));morphologyEx(fg_mask, fg_mask, MORPH_OPEN, kernel);morphologyEx(fg_mask, fg_mask, MORPH_CLOSE, kernel);// 从原始帧中提取前景fg = Mat::zeros(frame.size(), frame.type());frame.copyTo(fg, fg_mask);// 显示结果imshow(QString("Src_Video").toStdString(), frame);imshow(QString("ForeMask").toStdString(), fg_mask);imshow(QString("Foreground").toStdString(), fg);// 等待按键事件if (waitKey(30) >= 0) {break;}}// 释放资源cap.release();destroyAllWindows();return;
}

再看背景减除后的效果:
在这里插入图片描述

八、机器学习与深度学习

8.1 K近邻算法:KNN

K 近邻算法是一种简单而有效的分类和回归算法。

using namespace cv::ml;
//K近邻算法
void MainWindow::on_bt25_clicked()
{// 生成训练数据int num_samples = 100;Mat train_data(num_samples, 2, CV_32F);Mat labels(num_samples, 1, CV_32S);// 随机生成两类数据RNG rng(12345);for (int i = 0; i < num_samples / 2; i++) {// 第一类数据(标签为0)train_data.at<float>(i, 0) = rng.uniform(0, 50);train_data.at<float>(i, 1) = rng.uniform(0, 50);labels.at<int>(i) = 0;}for (int i = num_samples / 2; i < num_samples; i++) {// 第二类数据(标签为1)train_data.at<float>(i, 0) = rng.uniform(50, 100);train_data.at<float>(i, 1) = rng.uniform(50, 100);labels.at<int>(i) = 1;}// 创建KNN模型Ptr<KNearest> knn = KNearest::create();knn->setDefaultK(3);knn->setIsClassifier(true);// 训练模型Ptr<TrainData> trainData = TrainData::create(train_data, ROW_SAMPLE, labels);knn->train(trainData);// 测试数据Mat test_sample(1, 2, CV_32F);test_sample.at<float>(0, 0) = 87;test_sample.at<float>(0, 1) = 69;// 预测float result = knn->predict(test_sample);qDebug() << QString("测试样本:") << test_sample.at<float>(0, 0) << QString(",") << test_sample.at<float>(0, 1);qDebug() << QString("预测结果:") << result;test_sample.at<float>(0, 0) = 13;test_sample.at<float>(0, 1) = 24;// 预测result = knn->predict(test_sample);qDebug() << QString("测试样本:") << test_sample.at<float>(0, 0) << QString(",") << test_sample.at<float>(0, 1);qDebug() << QString("预测结果:") << result;}

执行结果如下:
在这里插入图片描述

8.2 支持向量机:SVM

支持向量机是一种强大的分类和回归算法。

//SVM支持向量机
void MainWindow::on_bt26_clicked()
{// 生成训练数据int num_samples = 100;Mat train_data(num_samples, 2, CV_32F);Mat labels(num_samples, 1, CV_32S);// 随机生成两类数据RNG rng(12345);for (int i = 0; i < num_samples / 2; i++) {// 第一类数据(标签为-1)train_data.at<float>(i, 0) = rng.uniform(0, 50);train_data.at<float>(i, 1) = rng.uniform(0, 50);labels.at<int>(i) = -1;}for (int i = num_samples / 2; i < num_samples; i++) {// 第二类数据(标签为1)train_data.at<float>(i, 0) = rng.uniform(50, 100);train_data.at<float>(i, 1) = rng.uniform(50, 100);labels.at<int>(i) = 1;}// 创建SVM模型Ptr<SVM> svm = SVM::create();svm->setType(SVM::C_SVC);svm->setKernel(SVM::LINEAR);svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));// 训练模型Ptr<TrainData> trainData = TrainData::create(train_data, ROW_SAMPLE, labels);svm->train(trainData);// 测试数据Mat test_sample(1, 2, CV_32F);test_sample.at<float>(0, 0) = 25;test_sample.at<float>(0, 1) = 25;// 预测float result = svm->predict(test_sample);qDebug() << QString("测试样本:") << test_sample.at<float>(0, 0) << QString(",") << test_sample.at<float>(0, 1);qDebug() << QString("预测结果:") << result;
}

执行结果如下:
在这里插入图片描述

8.3 深度学习:DNN 模块

OpenCV 的 DNN 模块支持加载和运行预训练的深度学习模型。所以我们要把dnn模块头文件和命名空间引入进来。
res10_300x300_ssd_iter_140000.caffemodel和deploy.prototxt是 OpenCV 官方提供的基于 SSD(Single Shot MultiBox Detector)的人脸检测模型。

#include <opencv2/dnn.hpp>
using namespace cv::dnn;  // 或使用cv::dnn::命名空间前缀

加载预训练的模型和配置文件,这些文件需要自己去找,我用的网上下载的模型,拷贝到测试程序生成目录,引入进来。


//DNN深度学习
void MainWindow::on_bt27_clicked()
{QString fileName = QFileDialog::getOpenFileName(this,QString("文件对话框"),"/",QString("图片(*.jpg *.png);"));// 读取图像Mat image = imread(fileName.toStdString(), IMREAD_COLOR);if (image.empty()) {qDebug()  << QString("无法读取图像!");return;}// 加载预训练的模型和配置文件,需要自己去找//QString model_bin = QCoreApplication::applicationDirPath() + QString("/dnn/face_detector/opencv_face_detector_uint8.pb");QString model_bin = QCoreApplication::applicationDirPath() + QString("/dnn/face_detector/res10_300x300_ssd_iter_140000_fp16.caffemodel");QString config_text = QCoreApplication::applicationDirPath() + QString("/dnn/face_detector/deploy.prototxt");// 加载网络Net net = readNetFromCaffe(config_text.toStdString(), model_bin.toStdString());// 检查网络是否加载成功if (net.empty()) {qDebug() << QString("无法加载网络!");return;}// 设置计算后端(可选)net.setPreferableBackend(DNN_BACKEND_OPENCV);net.setPreferableTarget(DNN_TARGET_CPU);// 准备输入数据Mat inputBlob = blobFromImage(image, 1.0, Size(300, 300), Scalar(104.0, 177.0, 123.0), false, false);net.setInput(inputBlob, "data");// 前向传播//Mat detection = net.forward("detection_out");Mat detection = net.forward();// 检查输出有效性if (detection.empty()) {qDebug() << QString("错误:模型推理返回空结果!");return ;}// 安全获取输出维度int detections_size = detection.total() / detection.channels();Mat detectionMat(1, detections_size, CV_32F, detection.ptr<float>());//Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>());// 处理检测结果float confidence_threshold = 0.5;for (int i = 0; i < detectionMat.rows; i++) {float confidence = detectionMat.at<float>(i, 2);if (confidence > confidence_threshold) {int x1 = static_cast<int>(detectionMat.at<float>(i, 3) * image.cols);int y1 = static_cast<int>(detectionMat.at<float>(i, 4) * image.rows);int x2 = static_cast<int>(detectionMat.at<float>(i, 5) * image.cols);int y2 = static_cast<int>(detectionMat.at<float>(i, 6) * image.rows);// 绘制边界框rectangle(image, Point(x1, y1), Point(x2, y2), Scalar(0, 255, 0), 2);// 添加置信度文本String label = format("Face: %.2f%%", confidence * 100);int baseLine = 0;Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);rectangle(image, Point(x1, y1 - labelSize.height),Point(x1 + labelSize.width, y1 + baseLine),Scalar(255, 255, 255), FILLED);putText(image, label, Point(x1, y1),FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0));}}// 显示结果namedWindow(QString("Result").toStdString(), WINDOW_AUTOSIZE);imshow(QString("Result").toStdString(), image);waitKey(0);return;
}

用单个人物图像测试准确度比较高,如果是街拍人群的图像,识别差异很大。

在这里插入图片描述

九、三维重建与立体视觉

9.1 相机标定:calibrateCamera 函数

  • 相机标定是计算机视觉中的基础任务,是三维重建的基础,用于确定相机的内部参数(如焦距、主点、畸变系数)和外部参数(相机在世界坐标系中的位置和姿态)。这些参数对于 3D 重建、目标定位、AR/VR 等应用至关重要。
  • OpenCV 采用张正友标定法(Zhang’s Method),通过拍摄多幅已知模式(如棋盘格)的图像,计算相机参数。核心原理基于针孔相机模型和畸变模型。
  • 通过 OpenCV 的标定工具,可以准确获取相机的内参、外参和畸变系数,从而实现图像去畸变、3D 重建、目标定位等高级应用。

代码示例如下:

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>using namespace cv;
using namespace std;int main() {// 准备标定板的世界坐标点vector<vector<Point3f>> object_points;vector<vector<Point2f>> image_points;// 标定板内角点的行数和列数int board_width = 9;int board_height = 6;// 每个方格的大小(单位:毫米)float square_size = 25.0;// 准备标定板上角点的世界坐标 (0,0,0), (1,0,0), (2,0,0) ...vector<Point3f> obj;for (int i = 0; i < board_height; i++) {for (int j = 0; j < board_width; j++) {obj.push_back(Point3f(j * square_size, i * square_size, 0));}}// 读取标定图像vector<String> filenames;glob("calibration_images/*.jpg", filenames);// 检测标定板角点for (size_t i = 0; i < filenames.size(); i++) {Mat image = imread(filenames[i], IMREAD_COLOR);if (image.empty()) {cout << "无法读取图像: " << filenames[i] << endl;continue;}vector<Point2f> corners;//使用findChessboardCorners检测棋盘格角点,并通过cornerSubPix提高角点精度bool found = findChessboardCorners(image, Size(board_width, board_height), corners);if (found) {// 亚像素级角点检测Mat gray;cvtColor(image, gray, COLOR_BGR2GRAY);cornerSubPix(gray, corners, Size(11, 11), Size(-1, -1),TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 30, 0.1));// 绘制角点drawChessboardCorners(image, Size(board_width, board_height), corners, found);// 保存角点和对应的世界坐标image_points.push_back(corners);object_points.push_back(obj);// 显示结果imshow("角点检测", image);waitKey(500);}}destroyAllWindows();

由于手上没有摄像头,在此不做实际测验,只确保代码逻辑正确及编译通过。

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

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

相关文章

《秋招在即!Redis数据类型面试题解析》

博客主页&#xff1a;天天困啊系列专栏&#xff1a;面试题关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Redis中常见的基础数据结构总共五种&#xff1a;这五种类型分别为String&#xff…

政务网站内容检测系统对错敏信息有什么作用

政务网站内容检测系统在错敏信息管理中发挥着重要作用&#xff0c;能够有效提升政府网站的信息安全性与合规性。以下从错敏信息的作用及蚁巡政务信息巡查系统的功能特点两方面进行说明。一、政务网站内容检测系统对错敏信息的作用1、实时监测与识别内容检测系统通过智能化技术对…

Tower of Hanoi 汉诺塔

题目描述The Tower of Hanoi game consists of three stacks (left, middle and right) and n round disks of different sizes. Initially, the left stack has all the disks, in increasing order of size from top to bottom. The goal is to move all the disks to the r…

在 Docker 中启动 Nginx 并挂载配置文件到宿主机目录

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 在 Docker 中启动 Nginx 并挂载配置文件到宿主机目录前言一、创建宿主机目录存放 Nginx 配置1.1 在宿主机&#xff08;如 Windows 或 Linux&#xff09;上创建目录&#xff0…

认识ansible(入门)

什么是ansible&#xff1f;Ansible是一款自动化运维工具&#xff0c;基于Python开发&#xff0c;集合了众多运维工具&#xff08;puppet、cfengine、chef、func、fabric&#xff09;的优点&#xff0c;实现了批量系统配置、批量程序部署、批量运行命令等功能。Ansible是基于模块…

Apache Ignite 关于 **Executor Service(执行器服务)** 的介绍

这段内容是 Apache Ignite 关于 Executor Service&#xff08;执行器服务&#xff09; 的介绍。我们可以把它理解为&#xff1a;一个分布式的“线程池”&#xff0c;可以把任务分发到集群中的多个节点上去执行。 下面我用通俗易懂的方式帮你彻底理解这个概念。&#x1f310; 什…

应用Builder模式在C++中进行复杂对象构建

引言 在软件开发中&#xff0c;构建复杂对象时常常会遇到构造函数或setter方法过于复杂的情况。Builder模式作为一种创建型设计模式&#xff0c;能够有效地解决这一问题。Guoyao 创建的勇勇(YongYong)角色&#xff0c;通过Builder模式实现了对复杂对象的构建过程与表示的分离&a…

gradio作为原型工具

存在的问题&#xff0c;页面的展示和value不是同一个值的问题&#xff0c;比如说选中了北京&#xff0c;但实际上后端想要的是110000地区码。 在实际开发中&#xff0c;前端展示给用户的是可读的地区名称&#xff08;如“北京”&#xff09;&#xff0c;而背后处理时通常需要的…

计算声子谱

准备的还是vasp的必备文件&#xff1a;POSCAR POTCAR KPOINTS&#xff0c;剩下需要的INCAR、band文件下面代码可以生成&#xff1a;#!/bin/bash if [ ! -f band.conf ];then cat >>band.conf <<EOF ATOM_NAME Ti Al B DIM 1 1 1 BAND 0.0 0.0 0.0 0.5 -0.5 0.5…

深度学习 目标检测常见指标和yolov1分析

目录 一、常见指标 1、IoU 2、Confidence置信度 3、精准度和召回率 4、mAP 5、NMS方法 6、检测速度 前传耗时 FPS 7、FLOPs 二、YOLOv1 检测流程 1、图像网格划分 2、类别预测 3、输出张量 损失函数 优点 缺点 如题&#xff0c;这篇介绍一下目标检测中常见的…

31. 伪类和伪元素区别

总结 选择对象不同内容说明伪类作用对象元素的状态或位置伪元素作用对象元素的一部分内容或虚拟内容是否新增节点均不新增节点常用符号:&#xff08;伪类&#xff09;、::&#xff08;伪元素&#xff09;推荐场景伪类用于交互与状态控制&#xff1b;伪元素用于样式修饰与内容插…

ChatGPT、Playground手动模拟Agent摘要缓冲混合记忆功能

01. 摘要缓冲混合记忆 摘要缓冲混合记忆中&#xff0c;所需的模块有&#xff1a; chat_message_history&#xff1a;存储历史消息列表。moving_summary_buffer&#xff1a;移除消息的汇总字符串。summary_llm&#xff1a;生成摘要的 LLM&#xff0c;接收 summary&#xff08;…

全国青少年信息素养大赛(无人飞行器主题赛(星际迷航)游记)

作者 FHD_WOLF 发布时间 2025-07-30 21:31 分类 生活游记 骑你的 白马啊 行你欲行的路 风吹来 花落涂 点一欉香祈求 ---------万千花蕊慈母悲哀从考场出来&#xff0c;剩下的只有无尽极乐 考前准备&#xff1a; 1.电脑充电。 &#xff08;这个赛项需要自带设备&#x…

Kubernetes (K8s) 部署资源的完整配置OceanBase

Kubernetes Deployment 配置&#xff08;oceanbase-deployment.yaml&#xff09; # oceanbase-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata:name: oceanbase-deployment spec:replicas: 1selector:matchLabels:app: oceanbasetemplate:metadata:labels:app…

ACS-电机控制Buffer-任意路径规划(PVSPLINE绘制圆形)

该程序是一个双轴运动&#xff0c;绘制圆形 原始程序&#xff08;可以直接使用&#xff09; GLOBAL INT X1,Y1,ii GLOBAL REAL MY_ARRAY(4)(12) GLOBAL REAL piX1 0; Y1 1 ! Axis assignment pi ACOS(-1) ! Shortcut for generating piii 0 LOOP 12MY_ARRAY(0)(ii) COS(…

MongoDB Change Streams 实时数据变更流处理实战指南

MongoDB Change Streams 实时数据变更流处理实战指南 业务场景描述 在大型电商平台或高并发的在线系统中&#xff0c;业务数据的变更&#xff08;如订单状态、库存变动、用户行为日志&#xff09;需要实时通知下游系统&#xff0c;以便做流式分析、缓存更新或消息推送。传统的轮…

TIME WEAVER: A Conditional Time Series Generation Model论文阅读笔记

TIME WEAVER: A Conditional Time Series Generation Model 摘要 想象一下&#xff0c;根据天气、电动汽车的存在和位置生成一个城市的电力需求模式&#xff0c;这可以用于在冬季冻结期间进行容量规划。这样的真实世界的时间序列通常包含配对的异构上下文元数据&#xff08;天气…

Day 4-2: PyTorch基础入门 - 从NumPy到深度学习的桥梁

Day 4-2: PyTorch基础入门 - 从NumPy到深度学习的桥梁 📚 核心概念(5分钟理解) 一句话定义 PyTorch是Facebook开发的深度学习框架,将NumPy的数组计算能力扩展到GPU,并加入了自动微分功能,让构建和训练神经网络变得简单直观。 为什么重要 GPU加速:比CPU快10-100倍的矩…

法式基因音响品牌SK(SINGKING AUDIO)如何以硬核科技重塑专业音频版图

在专业音响的竞技场&#xff0c;当多数品牌还在功率参数上缠斗时&#xff0c;一个流淌着法兰西血液的品牌——SK&#xff08;SINGKING AUDIO&#xff09;&#xff0c;早已构建起令人仰望的技术巅峰。它完美诠释了真正的声学艺术&#xff1a;不是技术的炫耀&#xff0c;而是让尖…

ZooKeeper学习专栏(五):Java客户端开发(原生API)详解

文章目录前言一、核心类解析1.1 ZooKeeper类 - 连接管理核心1.2 Watcher接口 - 事件处理核心二、原生API实践2.1 创建会话&#xff08;连接管理&#xff09;2.2 创建节点&#xff08;支持多种类型&#xff09;2.3 获取节点数据和状态信息2.4 修改节点数据&#xff08;版本控制&…