C++ OpenCV 机器学习与深度学习

我将为你详细介绍在 C++ 中使用 OpenCV 的机器学习深度学习功能,重点涵盖 OpenCV 的 ml 模块(传统机器学习)和 dnn 模块(深度学习),并提供实际可运行的代码示例。内容基于 OpenCV 4.x,假设你已配置好开发环境。以下将包括核心概念、关键功能、示例代码和优化建议,适合从基础到高级的应用。


1. 机器学习(ml 模块)

OpenCV 的 ml 模块提供了多种传统机器学习算法(如 SVM、决策树、KNN 等),适合图像分类、目标检测等任务。这些算法通常结合图像特征(如 HOG、SIFT)使用。

1.1 核心功能

  • 支持的算法
  • SVM(支持向量机):用于分类或回归。
  • KNN(K 近邻):简单有效的分类算法。
  • 决策树/随机森林:适合多类分类。
  • ANN(人工神经网络):基础的多层感知器。
  • 数据格式
  • 输入:Mat 矩阵,行表示样本,列表示特征。
  • 标签:Mat 矩阵,存储类别标签或回归值。
  • 用途:图像分类、特征分类(如人脸/非人脸)。

1.2 示例代码:SVM 分类

以下示例展示如何使用 SVM 进行二分类(例如区分两类图像特征)。

include

include

include

using namespace cv;
using namespace cv::ml;
using namespace std;

int main() {
// 构造示例数据(2 类,每类 100 个样本,2D 特征)
Mat samples(200, 2, CV_32F);
Mat labels(200, 1, CV_32S);
randn(samples(Range(0, 100), Range::all()), Scalar(1, 1), Scalar(0.5, 0.5)); // 类 1
randn(samples(Range(100, 200), Range::all()), Scalar(3, 3), Scalar(0.5, 0.5)); // 类 2
labels(Range(0, 100), Range::all()) = 0; // 类 1 标签
labels(Range(100, 200), Range::all()) = 1; // 类 2 标签

// 创建并训练 SVM
Ptr<SVM> svm = SVM::create();
svm->setType(SVM::C_SVC); // 分类 SVM
svm->setKernel(SVM::LINEAR); // 线性核
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
Ptr<TrainData> trainData = TrainData::create(samples, ROW_SAMPLE, labels);
svm->train(trainData);

// 测试新样本
Mat testSample = (Mat_<float>(1, 2) << 1.2, 1.3);
float prediction = svm->predict(testSample);
cout << "测试样本预测类别: " << prediction << endl;

// 可视化分类边界(仅限 2D 特征)
Mat image(400, 400, CV_8UC3, Scalar(255, 255, 255));
for (int y = 0; y < image.rows; y++) {
    for (int x = 0; x < image.cols; x++) {
        Mat sample = (Mat_<float>(1, 2) << x * 5.0 / image.cols, y * 5.0 / image.rows);
        float pred = svm->predict(sample);
        image.at<Vec3b>(y, x) = pred == 0 ? Vec3b(200, 200, 255) : Vec3b(255, 200, 200);
    }
}

// 绘制训练样本
for (int i = 0; i < samples.rows; i++) {
    int x = samples.at<float>(i, 0) * image.cols / 5;
    int y = samples.at<float>(i, 1) * image.rows / 5;
    circle(image, Point(x, y), 3, labels.at<int>(i) == 0 ? Scalar(0, 0, 255) : Scalar(255, 0, 0), -1);
}

imshow("SVM Classification", image);
imwrite("svm_result.jpg", image);
waitKey(0);
destroyAllWindows();
return 0;

}

说明

  • 数据准备samples 存储特征(2D 点),labels 存储类别标签。
  • SVM 配置:使用线性核,适合简单分类任务;可尝试 RBF 核(SVM::RBF)处理非线性数据。
  • 可视化:将分类结果绘制为彩色区域,样本点用圆形标记。
  • 用途:分类手写数字、图像特征等。

1.3 结合图像特征

实际应用中,通常提取图像特征(如 HOG、SIFT)作为 SVM 输入。例如,使用 HOG 描述子分类图像:

include

include

include

include

using namespace cv;
using namespace cv::ml;
using namespace std;

int main() {
// 初始化 HOG 描述子
HOGDescriptor hog(Size(64, 64), Size(16, 16), Size(8, 8), Size(8, 8), 9);

// 加载示例图像(正样本和负样本)
Mat pos_img = imread("positive.jpg", IMREAD_GRAYSCALE);
Mat neg_img = imread("negative.jpg", IMREAD_GRAYSCALE);
resize(pos_img, pos_img, Size(64, 64));
resize(neg_img, neg_img, Size(64, 64));

// 计算 HOG 特征
vector<float> pos_desc, neg_desc;
hog.compute(pos_img, pos_desc);
hog.compute(neg_img, neg_desc);

// 构造训练数据
Mat samples(2, pos_desc.size(), CV_32F);
Mat labels(2, 1, CV_32S);
for (size_t i = 0; i < pos_desc.size(); i++) {
    samples.at<float>(0, i) = pos_desc[i];
    samples.at<float>(1, i) = neg_desc[i];
}
labels.at<int>(0, 0) = 1; // 正样本
labels.at<int>(1, 0) = 0; // 负样本

// 训练 SVM
Ptr<SVM> svm = SVM::create();
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::RBF);
svm->train(TrainData::create(samples, ROW_SAMPLE, labels));

// 测试新图像
Mat test_img = imread("test.jpg", IMREAD_GRAYSCALE);
resize(test_img, test_img, Size(64, 64));
vector<float> test_desc;
hog.compute(test_img, test_desc);
Mat test_sample(1, test_desc.size(), CV_32F, test_desc.data());
float prediction = svm->predict(test_sample);
cout << "测试图像预测类别: " << (prediction == 1 ? "正样本" : "负样本") << endl;

return 0;

}

说明

  • HOG 描述子:适合检测固定大小的对象(如行人)。
  • 训练数据:实际应用需准备大量正负样本。
  • 用途:行人检测、物体分类。

2. 深度学习(dnn 模块)

OpenCV 的 dnn 模块支持加载预训练的深度学习模型(如 TensorFlow、PyTorch、ONNX),用于目标检测、图像分类、分割等任务。

2.1 核心功能

  • 支持框架:TensorFlow、PyTorch、Caffe、ONNX 等。
  • 常见模型
  • 分类:ResNet、MobileNet。
  • 检测:YOLO、SSD。
  • 分割:Mask R-CNN、DeepLab。
  • 关键类cv::dnn::Net 用于加载和推理模型。
  • 用途:实时目标检测、语义分割。

2.2 示例代码:YOLO 目标检测

以下示例使用 YOLOv5(ONNX 格式)进行目标检测:

include

include

include

include

using namespace cv;
using namespace cv::dnn;
using namespace std;

int main() {
// 加载 YOLOv5 模型
string model_path = “yolov5s.onnx”; // 替换为你的 ONNX 模型路径
Net net = readNetFromONNX(model_path);
net.setPreferableBackend(DNN_BACKEND_OPENCV);
net.setPreferableTarget(DNN_TARGET_CPU); // 可改为 DNN_TARGET_CUDA

// 加载类别名称
vector<string> classes;
ifstream ifs("coco.names"); // 替换为你的类别文件
string line;
while (getline(ifs, line)) classes.push_back(line);

// 读取图像
Mat img = imread("image.jpg");
if (img.empty()) {
    cout << "无法加载图像!" << endl;
    return -1;
}

// 准备输入
Mat blob = blobFromImage(img, 1.0 / 255, Size(640, 640), Scalar(0, 0, 0), true, false);
net.setInput(blob);

// 前向推理
Mat output = net.forward();

// 处理输出(YOLOv5 输出格式)
float confThreshold = 0.5;
float nmsThreshold = 0.4;
vector<int> classIds;
vector<float> confidences;
vector<Rect> boxes;
int rows = output.size[1];
float* data = (float*)output.data;

for (int i = 0; i < rows; i++) {
    float confidence = data[4];
    if (confidence > confThreshold) {
        float* classes_scores = data + 5;
        Mat scores(1, classes.size(), CV_32F, classes_scores);
        Point classIdPoint;
        double max_class_score;
        minMaxLoc(scores, 0, &max_class_score, 0, &classIdPoint);
        if (max_class_score > confThreshold) {
            float x = data[0], y = data[1], w = data[2], h = data[3];
            int left = static_cast<int>((x - w / 2) * img.cols / 640);
            int top = static_cast<int>((y - h / 2) * img.rows / 640);
            int width = static_cast<int>(w * img.cols / 640);
            int height = static_cast<int>(h * img.rows / 640);
            boxes.push_back(Rect(left, top, width, height));
            confidences.push_back(confidence);
            classIds.push_back(classIdPoint.x);
        }
    }
    data += output.size[2];
}

// 非极大值抑制
vector<int> indices;
NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices);

// 绘制结果
for (int idx : indices) {
    Rect box = boxes[idx];
    rectangle(img, box, Scalar(0, 255, 0), 2);
    string label = format("%s: %.2f", classes[classIds[idx]].c_str(), confidences[idx]);
    putText(img, label, Point(box.x, box.y - 10), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0), 2);
}

// 显示和保存
imshow("YOLO Detection", img);
imwrite("yolo_output.jpg", img);
waitKey(0);
destroyAllWindows();
return 0;

}

说明

  • 模型准备:需要 YOLOv5 的 ONNX 模型和 COCO 类别文件(coco.names)。
  • 输入预处理blobFromImage 将图像标准化为模型输入。
  • 输出解析:YOLO 输出包含边界框坐标、置信度和类别分数,需手动解析。
  • NMS:非极大值抑制去除冗余框。
  • 硬件加速:可切换到 DNN_BACKEND_CUDA 加速推理(需编译支持 CUDA)。

2.3 视频中的深度学习

将 YOLO 应用于视频流:

include

include

include

include

using namespace cv;
using namespace cv::dnn;
using namespace std;

int main() {
// 加载 YOLOv5 模型
string model_path = “yolov5s.onnx”;
Net net = readNetFromONNX(model_path);
net.setPreferableBackend(DNN_BACKEND_OPENCV);
net.setPreferableTarget(DNN_TARGET_CPU);

// 加载类别
vector<string> classes;
ifstream ifs("coco.names");
string line;
while (getline(ifs, line)) classes.push_back(line);

// 打开视频
VideoCapture cap(0); // 使用摄像头
if (!cap.isOpened()) {
    cout << "无法打开摄像头!" << endl;
    return -1;
}

Mat frame;
while (true) {
    cap >> frame;
    if (frame.empty()) break;

    // 准备输入
    Mat blob = blobFromImage(frame, 1.0 / 255, Size(640, 640), Scalar(0, 0, 0), true, false);
    net.setInput(blob);

    // 前向推理
    Mat output = net.forward();

    // 处理输出
    float confThreshold = 0.5;
    float nmsThreshold = 0.4;
    vector<int> classIds;
    vector<float> confidences;
    vector<Rect> boxes;
    int rows = output.size[1];
    float* data = (float*)output.data;

    for (int i = 0; i < rows; i++) {
        float confidence = data[4];
        if (confidence > confThreshold) {
            float* classes_scores = data + 5;
            Mat scores(1, classes.size(), CV_32F, classes_scores);
            Point classIdPoint;
            double max_class_score;
            minMaxLoc(scores, 0, &max_class_score, 0, &classIdPoint);
            if (max_class_score > confThreshold) {
                float x = data[0], y = data[1], w = data[2], h = data[3];
                int left = static_cast<int>((x - w / 2) * frame.cols / 640);
                int top = static_cast<int>((y - h / 2) * frame.rows / 640);
                int width = static_cast<int>(w * frame.cols / 640);
                int height = static_cast<int>(h * frame.rows / 640);
                boxes.push_back(Rect(left, top, width, height));
                confidences.push_back(confidence);
                classIds.push_back(classIdPoint.x);
            }
        }
        data += output.size[2];
    }

    // 非极大值抑制
    vector<int> indices;
    NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices);

    // 绘制结果
    for (int idx : indices) {
        Rect box = boxes[idx];
        rectangle(frame, box, Scalar(0, 255, 0), 2);
        string label = format("%s: %.2f", classes[classIds[idx]].c_str(), confidences[idx]);
        putText(frame, label, Point(box.x, box.y - 10), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0), 2);
    }

    // 显示
    imshow("YOLO Video", frame);
    if (waitKey(30) == 27) break;
}

cap.release();
destroyAllWindows();
return 0;

}

说明

  • 实时性:视频处理需优化推理速度,建议降低分辨率或使用轻量模型(如 YOLOv5s)。
  • 输出保存:可结合 VideoWriter 保存检测结果。

3. 优化技巧

  • 机器学习
  • 特征选择:使用 HOG、SIFT 或深度学习提取的特征提高分类精度。
  • 数据增强:通过旋转、翻转等增加训练数据多样性。
  • 交叉验证:使用 TrainData::shufflesplit 进行模型验证。
  • 深度学习
  • 硬件加速:使用 DNN_BACKEND_CUDADNN_TARGET_CUDA(需 CUDA 支持的 OpenCV)。
  • 模型优化:选择轻量模型(如 MobileNet SSD)或量化模型以提高速度。
  • 批量处理:对视频帧批量推理以减少开销。
  • 性能提升
  • 降低输入分辨率(如 320×320 而不是 640×640)。
  • 使用多线程或 OpenCV 的 parallel_for_
  • 跳帧处理以减少计算量。

4. 注意事项

  • 环境配置
  • 确保安装 opencv-contrib 以支持 xfeatures2d(如 SIFT)。
  • 深度学习需准备模型文件和类别文件。
  • 模型格式
  • ONNX 格式通用性强,推荐使用。
  • 确保模型输入尺寸与 blobFromImage 一致。
  • 数据准备
  • 机器学习需大量标注数据。
  • 深度学习需预训练模型和正确的预处理步骤。

5. 进阶学习建议

  • 模型训练:使用 Python(PyTorch/TensorFlow)训练模型,导出 ONNX 格式给 OpenCV 使用。
  • 高级任务
  • 分割:尝试 DeepLabv3 或 Mask R-CNN。
  • 跟踪:结合深度学习检测与 OpenCV 的 Tracker 类。
  • 混合方法:将传统特征(HOG/SIFT)与深度学习特征结合,提升鲁棒性。
  • 参考文档
  • OpenCV ML:https://docs.opencv.org/4.x/d0/df8/classcv_1_1ml_1_1SVM.html
  • OpenCV DNN:https://docs.opencv.org/4.x/d6/d0f/classcv_1_1dnn_1_1Net.html

如果你需要针对特定任务(如人脸识别、语义分割)或更复杂的实现(如多目标跟踪),请告诉我,我可以提供更详细的代码和指导!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注