OpenCV4X学习- cv::Mat

cv::Mat对象共享相同

cv::Mat对象可以共享相同的数据,这通过矩阵指针指向同一个地址来实现。

clone() 方法

功能:

clone() 方法创建一个与原矩阵完全相同的新矩阵,包括矩阵头和矩阵数据。新矩阵与原矩阵在内存上是完全独立的,对新矩阵的任何修改都不会影响原矩阵,反之亦然。

bash 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    cv::Mat originalMat = cv::Mat::ones(3, 3, CV_32F);
    cv::Mat clonedMat = originalMat.clone();

    clonedMat.at<float>(0, 0) = 2.0f;

    std::cout << "Original Mat:" << std::endl;
    for (int i = 0; i < originalMat.rows; ++i) {
        for (int j = 0; j < originalMat.cols; ++j) {
            std::cout << originalMat.at<float>(i, j) << " ";
        }
        std::cout << std::endl;
    }

    std::cout << "Cloned Mat:" << std::endl;
    for (int i = 0; i < clonedMat.rows; ++i) {
        for (int j = 0; j < clonedMat.cols; ++j) {
            std::cout << clonedMat.at<float>(i, j) << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}

copyTo 方法

功能:

copyTo 方法将源矩阵的数据复制到目标矩阵中。目标矩阵可以是预先分配好内存的 cv::Mat 对象,且其大小和数据类型需与源矩阵兼容。如果目标矩阵的大小和数据类型与源矩阵不一致,copyTo 方法会抛出异常。

bash 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    cv::Mat originalMat = cv::Mat::ones(3, 3, CV_32F);
    cv::Mat newMat(3, 3, CV_32F);

    originalMat.copyTo(newMat);

    newMat.at<float>(0, 0) = 2.0f;

    std::cout << "Original Mat:" << std::endl;
    for (int i = 0; i < originalMat.rows; ++i) {
        for (int j = 0; j < originalMat.cols; ++j) {
            std::cout << originalMat.at<float>(i, j) << " ";
        }
        std::cout << std::endl;
    }

    std::cout << "New Mat after copyTo:" << std::endl;
    for (int i = 0; i < newMat.rows; ++i) {
        for (int j = 0; j < newMat.cols; ++j) {
            std::cout << newMat.at<float>(i, j) << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}

数据共享

bash 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <opencv2/opencv.hpp>
#include <QDebug>

void shareMatDataExample() {
    // 创建一个初始的cv::Mat对象
    cv::Mat originalMat = cv::Mat::ones(3, 3, CV_32F);

    // 创建其他cv::Mat对象,它们将共享originalMat的数据
    cv::Mat sharedMat1 = originalMat;
    cv::Mat sharedMat2 = originalMat.clone(); // clone方法会复制数据,这里为了对比展示,下面再修改为共享
    cv::Mat sharedMat3;
    originalMat.copyTo(sharedMat3); // copyTo方法也会复制数据,同样为对比展示,后面修改为共享

    // 修改sharedMat1的数据,这会影响originalMat,因为它们共享数据
    sharedMat1.at<float>(0, 0) = 2.0f;

    qDebug() << "Original Mat:";
    for (int i = 0; i < originalMat.rows; ++i) {
        for (int j = 0; j < originalMat.cols; ++j) {
            qDebug() << originalMat.at<float>(i, j);
        }
    }

    qDebug() << "Shared Mat 1:";
    for (int i = 0; i < sharedMat1.rows; ++i) {
        for (int j = 0; j < sharedMat1.cols; ++j) {
            qDebug() << sharedMat1.at<float>(i, j);
        }
    }

    qDebug() << "Shared Mat 2:";
    for (int i = 0; i < sharedMat2.rows; ++i) {
        for (int j = 0; j < sharedMat2.cols; ++j) {
            qDebug() << sharedMat2.at<float>(i, j);
        }
    }
    // 让sharedMat2共享originalMat的数据
    sharedMat2 = originalMat;
    // 修改sharedMat2的数据,这会影响originalMat和sharedMat1
    sharedMat2.at<float>(1, 1) = 3.0f;

    qDebug() << "Original Mat after modifying Shared Mat 2:";
    for (int i = 0; i < originalMat.rows; ++i) {
        for (int j = 0; j < originalMat.cols; ++j) {
            qDebug() << originalMat.at<float>(i, j);
        }
    }

    qDebug() << "Shared Mat 2:";
    for (int i = 0; i < sharedMat2.rows; ++i) {
        for (int j = 0; j < sharedMat2.cols; ++j) {
            qDebug() << sharedMat2.at<float>(i, j);
        }
    }

    qDebug() << "Shared Mat 3:";
    for (int i = 0; i < sharedMat3.rows; ++i) {
        for (int j = 0; j < sharedMat3.cols; ++j) {
            qDebug() << sharedMat3.at<float>(i, j);
        }
    }

    // 让sharedMat3共享originalMat的数据
    sharedMat3 = originalMat;
    // 修改sharedMat3的数据,这会影响originalMat、sharedMat1和sharedMat2
    sharedMat3.at<float>(2, 2) = 4.0f;

    qDebug() << "Original Mat after modifying Shared Mat 3:";
    for (int i = 0; i < originalMat.rows; ++i) {
        for (int j = 0; j < originalMat.cols; ++j) {
            qDebug() << originalMat.at<float>(i, j);
        }
    }

    qDebug() << "Shared Mat 3:";
    for (int i = 0; i < sharedMat3.rows; ++i) {
        for (int j = 0; j < sharedMat3.cols; ++j) {
            qDebug() << sharedMat3.at<float>(i, j);
        }
    }
}

// MainWindow类的构造函数
// 功能:初始化MainWindow对象,设置用户界面并调用主测试函数
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    // 设置用户界面
    ui->setupUi(this);
    shareMatDataExample();
}

// MainWindow类的析构函数
// 功能:释放ui对象所占用的资源
MainWindow::~MainWindow()
{
    delete ui;
}

cv::Mat 创建方法

构造法:使用cv::Mat的构造函数来初始化矩阵,指定矩阵的行数、列数和数据类型。

直接赋值法:通过直接将一个已有的cv::Mat对象赋值给另一个cv::Mat对象,实现数据共享。

数组法:先定义一个数组,然后使用cv::Mat的构造函数将数组数据复制到矩阵中。

create 函数法:先创建一个空的cv::Mat对象,然后使用create函数来分配内存并指定矩阵的大小和数据类型。

定义特殊矩阵:使用cv::Mat提供的静态函数创建特殊矩阵,如全零矩阵、全一矩阵和单位矩阵。

bash 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <opencv2/opencv.hpp>
#include <QDebug>

void initializeMatExamples() {
    // 构造法
    cv::Mat mat1(3, 3, CV_32F);
    for (int i = 0; i < mat1.rows; ++i) {
        for (int j = 0; j < mat1.cols; ++j) {
            mat1.at<float>(i, j) = i * mat1.cols + j;
        }
    }
    qDebug() << "构造法初始化的矩阵mat1:";
    for (int i = 0; i < mat1.rows; ++i) {
        for (int j = 0; j < mat1.cols; ++j) {
            qDebug() << mat1.at<float>(i, j);
        }
    }

    // 直接赋值法
    cv::Mat mat2 = mat1;
    qDebug() << "直接赋值法初始化的矩阵mat2(与mat1共享数据):";
    for (int i = 0; i < mat2.rows; ++i) {
        for (int j = 0; j < mat2.cols; ++j) {
            qDebug() << mat2.at<float>(i, j);
        }
    }

    // 数组法
    float data[] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f};
    cv::Mat mat3(3, 3, CV_32F, data);
    qDebug() << "数组法初始化的矩阵mat3:";
    for (int i = 0; i < mat3.rows; ++i) {
        for (int j = 0; j < mat3.cols; ++j) {
            qDebug() << mat3.at<float>(i, j);
        }
    }

    // create函数法
    cv::Mat mat4;
    mat4.create(3, 3, CV_32F);
    for (int i = 0; i < mat4.rows; ++i) {
        for (int j = 0; j < mat4.cols; ++j) {
            mat4.at<float>(i, j) = i * mat4.cols + j + 10.0f;
        }
    }
    qDebug() << "create函数法初始化的矩阵mat4:";
    for (int i = 0; i < mat4.rows; ++i) {
        for (int j = 0; j < mat4.cols; ++j) {
            qDebug() << mat4.at<float>(i, j);
        }
    }

    // 定义特殊矩阵
    cv::Mat mat5 = cv::Mat::zeros(3, 3, CV_32F);
    qDebug() << "全零矩阵mat5:";
    for (int i = 0; i < mat5.rows; ++i) {
        for (int j = 0; j < mat5.cols; ++j) {
            qDebug() << mat5.at<float>(i, j);
        }
    }

    cv::Mat mat6 = cv::Mat::ones(3, 3, CV_32F);
    qDebug() << "全一矩阵mat6:";
    for (int i = 0; i < mat6.rows; ++i) {
        for (int j = 0; j < mat6.cols; ++j) {
            qDebug() << mat6.at<float>(i, j);
        }
    }

    cv::Mat mat7 = cv::Mat::eye(3, 3, CV_32F);
    qDebug() << "单位矩阵mat7:";
    for (int i = 0; i < mat7.rows; ++i) {
        for (int j = 0; j < mat7.cols; ++j) {
            qDebug() << mat7.at<float>(i, j);
        }
    }
}


// MainWindow类的构造函数
// 功能:初始化MainWindow对象,设置用户界面并调用主测试函数
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    // 设置用户界面
    ui->setupUi(this);
    initializeMatExamples();
}

// MainWindow类的析构函数
// 功能:释放ui对象所占用的资源
MainWindow::~MainWindow()
{
    delete ui;
}

创建和初始化数组

bash 复制代码
#include <opencv2/opencv.hpp>
#include <QDebug>

void createAndInitArray() {
    // 创建一个3x3的单精度浮点数矩阵并初始化
    cv::Mat mat1(3, 3, CV_32F);
    for (int i = 0; i < mat1.rows; ++i) {
        for (int j = 0; j < mat1.cols; ++j) {
            mat1.at<float>(i, j) = i * mat1.cols + j;
        }
    }
    qDebug() << "创建并初始化的矩阵mat1:";
    for (int i = 0; i < mat1.rows; ++i) {
        for (int j = 0; j < mat1.cols; ++j) {
            qDebug() << mat1.at<float>(i, j);
        }
    }

    // 创建一个全零的3x3双精度浮点数矩阵
    cv::Mat mat2 = cv::Mat::zeros(3, 3, CV_64F);
    qDebug() << "全零矩阵mat2:";
    for (int i = 0; i < mat2.rows; ++i) {
        for (int j = 0; j < mat2.cols; ++j) {
            qDebug() << mat2.at<double>(i, j);
        }
    }

    // 创建一个全一的2x2整数矩阵
    cv::Mat mat3 = cv::Mat::ones(2, 2, CV_32S);
    qDebug() << "全一矩阵mat3:";
    for (int i = 0; i < mat3.rows; ++i) {
        for (int j = 0; j < mat3.cols; ++j) {
            qDebug() << mat3.at<int>(i, j);
        }
    }

    // 创建一个单位矩阵
    cv::Mat mat4 = cv::Mat::eye(3, 3, CV_32F);
    qDebug() << "单位矩阵mat4:";
    for (int i = 0; i < mat4.rows; ++i) {
        for (int j = 0; j < mat4.cols; ++j) {
            qDebug() << mat4.at<float>(i, j);
        }
    }
}

访问和修改数组元素

bash 复制代码
void accessAndModifyArray() {
    cv::Mat mat = cv::Mat::ones(3, 3, CV_32F);
    // 访问并修改特定位置的元素
    mat.at<float>(1, 1) = 5.0f;
    qDebug() << "修改后的矩阵:";
    for (int i = 0; i < mat.rows; ++i) {
        for (int j = 0; j < mat.cols; ++j) {
            qDebug() << mat.at<float>(i, j);
        }
    }

    // 遍历并修改所有元素
    for (int i = 0; i < mat.rows; ++i) {
        for (int j = 0; j < mat.cols; ++j) {
            mat.at<float>(i, j) = mat.at<float>(i, j) * 2;
        }
    }
    qDebug() << "再次修改后的矩阵:";
    for (int i = 0; i < mat.rows; ++i) {
        for (int j = 0; j < mat.cols; ++j) {
            qDebug() << mat.at<float>(i, j);
        }
    }
}

数组运算

bash 复制代码
void arrayOperations() {
    cv::Mat mat1 = cv::Mat::ones(3, 3, CV_32F);
    cv::Mat mat2 = cv::Mat::ones(3, 3, CV_32F);

    // 矩阵加法
    cv::Mat sumMat;
    cv::add(mat1, mat2, sumMat);
    qDebug() << "矩阵相加的结果:";
    for (int i = 0; i < sumMat.rows; ++i) {
        for (int j = 0; j < sumMat.cols; ++j) {
            qDebug() << sumMat.at<float>(i, j);
        }
    }

    // 矩阵乘法(点乘,对应元素相乘)
    cv::Mat multiplyMat;
    cv::multiply(mat1, mat2, multiplyMat);
    qDebug() << "矩阵点乘的结果:";
    for (int i = 0; i < multiplyMat.rows; ++i) {
        for (int j = 0; j < multiplyMat.cols; ++j) {
            qDebug() << multiplyMat.at<float>(i, j);
        }
    }

    // 矩阵乘法(矩阵相乘)
    cv::Mat mat3 = cv::Mat::ones(3, 2, CV_32F);
    cv::Mat mat4 = cv::Mat::ones(2, 3, CV_32F);
    cv::Mat matrixMultiplyMat;
    cv::gemm(mat3, mat4, 1.0, cv::Mat(), 0.0, matrixMultiplyMat);
    qDebug() << "矩阵相乘的结果:";
    for (int i = 0; i < matrixMultiplyMat.rows; ++i) {
        for (int j = 0; j < matrixMultiplyMat.cols; ++j) {
            qDebug() << matrixMultiplyMat.at<float>(i, j);
        }
    }
}

数组的合并与拆分

bash 复制代码
void splitAndMergeArrays() {
    cv::Mat mat1 = cv::Mat::ones(3, 3, CV_8U);
    cv::Mat mat2 = cv::Mat::ones(3, 3, CV_8U) * 2;
    cv::Mat mat3 = cv::Mat::ones(3, 3, CV_8U) * 3;

    std::vector<cv::Mat> channels;
    channels.push_back(mat1);
    channels.push_back(mat2);
    channels.push_back(mat3);

    // 合并数组
    cv::Mat mergedMat;
    cv::merge(channels, mergedMat);
    qDebug() << "合并后的矩阵维度:" << mergedMat.channels();

    // 拆分数组
    std::vector<cv::Mat> splitChannels;
    cv::split(mergedMat, splitChannels);
    qDebug() << "拆分后的第一个矩阵:";
    for (int i = 0; i < splitChannels[0].rows; ++i) {
        for (int j = 0; j < splitChannels[0].cols; ++j) {
            qDebug() << (int)splitChannels[0].at<uchar>(i, j);
        }
    }
}

文件读写

XML文件

bash 复制代码
#include <opencv2/opencv.hpp>
#include <QDebug>

void writeXMLFile() {
    cv::FileStorage fs("data.xml", cv::FileStorage::WRITE);

    // 写入基本数据类型
    fs << "frameCount" << 5;
    fs << "cameraMatrix" << (cv::Mat_<double>(3, 3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1);
    fs << "distCoeffs" << (cv::Mat_<double>(5, 1) << 0.1, 0.2, -0.3, 0.4, 0.5);

    // 写入序列(类似于数组)
    fs << "features" << "[";
    for (int i = 0; i < 3; ++i) {
        cv::Point2f pt((float)(i * 10), (float)(i * 20));
        fs << "{:" << "x" << pt.x << "y" << pt.y << "}";
    }
    fs << "]";

    fs.release();
}

void readXMLFile() {
    cv::FileStorage fs("data.xml", cv::FileStorage::READ);
    if (!fs.isOpened()) {
        qDebug() << "无法打开XML文件";
        return;
    }

    int frameCount;
    cv::Mat cameraMatrix, distCoeffs;
    fs["frameCount"] >> frameCount;
    fs["cameraMatrix"] >> cameraMatrix;
    fs["distCoeffs"] >> distCoeffs;

    qDebug() << "frameCount: " << frameCount;
    qDebug() << "cameraMatrix: " << cameraMatrix;
    qDebug() << "distCoeffs: " << distCoeffs;

    cv::FileNode features = fs["features"];
    if (features.type() == cv::FileNode::SEQ) {
        for (cv::FileNodeIterator it = features.begin(); it != features.end(); ++it) {
            cv::Point2f pt;
            (*it)["x"] >> pt.x;
            (*it)["y"] >> pt.y;
            qDebug() << "Feature point: (" << pt.x << ", " << pt.y << ")";
        }
    }

    fs.release();
}

写入:

cv::FileStorage类用于打开文件进行写入操作,第一个参数是文件名,第二个参数cv::FileStorage::WRITE表示写入模式。

使用<<运算符写入基本数据类型,如整数、矩阵等。对于序列,先写入"["开始序列,然后逐个写入元素,最后写入"]"结束序列。

写入完成后,调用fs.release()关闭文件。

读取:

以cv::FileStorage::READ模式打开文件。

使用>>运算符从文件中读取数据到相应的变量中。对于序列,通过cv::FileNode和cv::FileNodeIterator遍历序列中的元素

YAML文件

bash 复制代码
void writeYAMLFile() {
    cv::FileStorage fs("data.yaml", cv::FileStorage::WRITE);

    // 写入基本数据类型
    fs << "imageWidth" << 640;
    fs << "imageHeight" << 480;
    fs << "calibrationDate" << "2024-01-01";

    // 写入映射(类似于字典)
    fs << "calibration" << "{"
       << "cameraMatrix" << (cv::Mat_<double>(3, 3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1)
       << "distCoeffs" << (cv::Mat_<double>(5, 1) << 0.1, 0.2, -0.3, 0.4, 0.5)
       << "}";

    fs.release();
}

void readYAMLFile() {
    cv::FileStorage fs("data.yaml", cv::FileStorage::READ);
    if (!fs.isOpened()) {
        qDebug() << "无法打开YAML文件";
        return;
    }

    int imageWidth, imageHeight;
    std::string calibrationDate;
    fs["imageWidth"] >> imageWidth;
    fs["imageHeight"] >> imageHeight;
    fs["calibrationDate"] >> calibrationDate;

    qDebug() << "imageWidth: " << imageWidth;
    qDebug() << "imageHeight: " << imageHeight;
    qDebug() << "calibrationDate: " << calibrationDate.c_str();

    cv::Mat cameraMatrix, distCoeffs;
    cv::FileNode calibration = fs["calibration"];
    calibration["cameraMatrix"] >> cameraMatrix;
    calibration["distCoeffs"] >> distCoeffs;

    qDebug() << "cameraMatrix: " << cameraMatrix;
    qDebug() << "distCoeffs: " << distCoeffs;

    fs.release();
}

写入:

同样使用cv::FileStorage打开文件进行写入,这次文件扩展名为.yaml。

写入基本数据类型和映射。映射使用{"}包围,内部通过<<依次写入键值对。

读取:

以读取模式打开 YAML 文件。

从文件中读取数据到变量,对于映射,先获取对应的cv::FileNode,再从该节点中读取具体的数据。

重载QDebug

bash 复制代码
// 重载QDebug的<<运算符以支持cv::Mat输出
QDebug operator<<(QDebug debug, const cv::Mat& mat) {
    debug.nospace() << "cv::Mat(" << mat.rows << "x" << mat.cols << ", "
                    << (mat.type() == CV_8U? "CV_8U" :
                            mat.type() == CV_8S? "CV_8S" :
                            mat.type() == CV_16U? "CV_16U" :
                            mat.type() == CV_16S? "CV_16S" :
                            mat.type() == CV_32S? "CV_32S" :
                            mat.type() == CV_32F? "CV_32F" :
                            mat.type() == CV_64F? "CV_64F" : "unknown type")
                    << ") {";
    for (int i = 0; i < mat.rows; ++i) {
        if (i > 0) {
            debug << "\n ";
        }
        for (int j = 0; j < mat.cols; ++j) {
            if (mat.type() == CV_8U) {
                debug << (int)mat.at<uchar>(i, j);
            } else if (mat.type() == CV_8S) {
                debug << (int)mat.at<char>(i, j);
            } else if (mat.type() == CV_16U) {
                debug << mat.at<ushort>(i, j);
            } else if (mat.type() == CV_16S) {
                debug << mat.at<short>(i, j);
            } else if (mat.type() == CV_32S) {
                debug << mat.at<int>(i, j);
            } else if (mat.type() == CV_32F) {
                debug << mat.at<float>(i, j);
            } else if (mat.type() == CV_64F) {
                debug << mat.at<double>(i, j);
            }
            if (j < mat.cols - 1) {
                debug << ", ";
            }
        }
    }
    debug << "}";
    return debug.space();
}
相关推荐
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码4 天前
嵌入式学习路线
学习
毛小茛4 天前
计算机系统概论——校验码
学习
babe小鑫4 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms4 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下4 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。4 天前
2026.2.25监控学习
学习
im_AMBER4 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J4 天前
从“Hello World“ 开始 C++
c语言·c++·学习