openCV,C++接口,cv::Mat矩阵数据元素读取

参见: http://blog.youkuaiyun.com/moc062066/article/details/6949826

简单地处理 cv::Mat,直接如下进行即可:

cv::Mat tmat(4, 4, CV_32FC1);
tmat.at<float>(0,0) = 1;
cout<<tmat.at<float>(0,0)<<endl;

示例,使用openCV求矩阵的特征值与特征向量:

#include "cv.h"
#include "highgui.h"
#include <iostream>
using namespace std;

int main(int argc, char** argv) {
    float mat[4][4];
    for(int i=0; i<4; i++) {
        for(int j=0; j<4; j++) {
            mat[i][j] = (float)rand() / RAND_MAX;
        }
    }

    cv::Mat cvmat(4,4,CV_32FC1);
    for(int i=0; i<cvmat.rows; i++) {
        for(int j=0; j<cvmat.cols; j++) {
            cvmat.at<float>(i,j) = mat[i][j];
        }
    }

    for(int i=0; i<cvmat.rows; i++) {
        for(int j=0; j<cvmat.cols; j++) {
            float tmpNum = cvmat.at<float>(i,j);
            cout<<tmpNum<<" ";
        } cout<<endl;
    }

    cv::Mat eigenvalues;
    cv::Mat eigenvectors;
    cv::eigen(cvmat, eigenvalues, eigenvectors);

    cout<<endl;
    for(int i=0; i<eigenvalues.rows; i++) {
        for(int j=0; j<eigenvalues.cols; j++) {
            cout<<eigenvalues.at<float>(i,j)<<endl;
        }
    }

    cout<<endl;
    for(int i=0; i<eigenvectors.rows; i++) {
        for(int j=0; j<eigenvectors.cols; j++) {
            cout<<eigenvectors.at<float>(i,j)<<" ";
        } cout<<endl;
    }

    return 0;
}
<think>我们正在讨论如何将cv::Mat对象保存为文本文件。根据OpenCV的文档,我们可以使用FileStorage类来实现,但通常用于XML/YAML格式。不过,用户要求保存为TXT文件,我们可以使用简单的文件写入操作,将矩阵每个元素写入文本文件,每行一个元素或者按照矩阵的行列结构写入。 但是,OpenCV也提供了cv::imwrite函数用于保存图像,但它通常用于图像格式(如JPEG、PNG等),而不是文本文件。对于文本文件,我们可以使用以下方法: 方法1:使用循环遍历矩阵每个元素,将其写入文件,并控制格式(例如,每行写入矩阵的一行,元素之间用空格或逗号分隔)。 方法2:使用OpenCV的FileStorage类,并指定为JSON或YAML格式,但用户要求是TXT,我们可以将格式设置为简单的文本格式(实际上,FileStorage默认的YAML或XML格式都是文本可读的,但用户可能想要更简单的纯数据格式)。 考虑到用户想要的是TXT文件,我们可能更倾向于方法1,因为我们可以完全控制输出的格式。 步骤: 1. 打开一个文件流。 2. 遍历矩阵的每一行和每一列。 3. 将每个元素写入文件,并在元素之间添加分隔符(如空格、逗号等),每行结束后换行。 注意:矩阵数据类型(如uchar, float, int等)会影响如何写入。 示例代码: 假设我们有一个单通道的浮点矩阵mat,我们可以这样写: ```cpp #include <fstream> #include <opencv2/opencv.hpp> int main() { cv::Mat mat = (cv::Mat_<float>(2,2) << 1.0, 2.0, 3.0, 4.0); // 2x2矩阵 std::ofstream outFile("matrix.txt"); if (!outFile.is_open()) { std::cerr << "无法打开文件进行写入!" << std::endl; return -1; } // 设置输出格式为固定小数,精度为6位 outFile << std::fixed << std::setprecision(6); for (int i = 0; i < mat.rows; i++) { for (int j = 0; j < mat.cols; j++) { outFile << mat.at<float>(i, j); if (j < mat.cols - 1) { outFile << " "; // 用空格分隔同一行的元素 } } outFile << std::endl; // 换行 } outFile.close(); return 0; } ``` 对于多通道矩阵(例如3通道的BGR图像),每个像素位置有3个值。我们可以这样写: ```cpp // 假设mat是一个3通道的矩阵,类型为CV_8UC3 for (int i = 0; i < mat.rows; i++) { for (int j = 0; j < mat.cols; j++) { cv::Vec3b pixel = mat.at<cv::Vec3b>(i, j); outFile << (int)pixel[0] << " " << (int)pixel[1] << " " << (int)pixel[2]; if (j < mat.cols - 1) { outFile << " "; // 可选,因为每像素已经用空格分隔了,这里可以不加,或者用其他方式 } } outFile << std::endl; } ``` 但是,用户可能希望以更紧凑的方式存储,或者按通道存储。这取决于需求。 另外,OpenCV还提供了cv::Formatter类,可以用于格式化输出,但上述方法更直接。 注意:如果矩阵很大,这种方法可能会比较慢,但对于一般大小的矩阵是可行的。 如果用户想要的是科学计算中常用的矩阵格式(例如MATLAB的格式),那么可能需要按照特定的格式写入,比如每行开头和结尾用括号等,但用户要求是TXT,所以通常就是纯数据。 另外,引用[1]中提到可以用cvSave和cvLoad,但这是C接口,在C++中不推荐使用。而且,cvSave保存的是OpenCV自己的二进制格式(类似于XML/YAML),不是纯文本。 因此,我们推荐使用上述循环写入的方法。 如果用户希望使用OpenCV内置的函数,可以考虑使用FileStorage保存为YAML格式,虽然扩展名是.yml,但内容是文本,可读性很好,而且OpenCV可以直接读取。示例: ```cpp cv::FileStorage fs("matrix.yml", cv::FileStorage::WRITE); fs << "mat" << mat; fs.release(); ``` 这样保存的文件是文本文件,但格式是YAML。如果用户坚持要TXT(即没有YAML的标记),那么循环写入是更直接的方法。 综上所述,根据用户需求,我们提供两种方案: 方案1:使用循环写入,生成纯数据TXT文件(不带任何标记)。 方案2:使用FileStorage保存为YAML格式,生成文本文件(带YAML标记,但可读性很好,且OpenCV可读)。 由于用户明确要求TXT,我们假设用户需要的是没有标记的纯数据,因此推荐方案1。 注意:在方案1中,我们需要知道矩阵的类型,因为不同的类型访问方式不同(如at<float>,at<uchar>等)。我们可以根据矩阵的type()来判断类型,然后使用模板函数或者switch语句来处理不同的类型。但是,为了简单起见,我们可以假设用户知道矩阵的类型,并在代码中明确指定。 如果用户需要处理多种类型,可以写一个函数,根据矩阵类型进行不同的处理。 下面是一个处理多种类型的示例框架(仅处理常见类型): ```cpp #include <fstream> #include <iomanip> #include <opencv2/opencv.hpp> void writeMatToTxt(const cv::Mat& mat, const std::string& filename) { std::ofstream outFile(filename); if (!outFile) { std::cerr << "无法打开文件: " << filename << std::endl; return; } outFile << std::fixed << std::setprecision(6); // 对于浮点数,设置精度 for (int i = 0; i < mat.rows; i++) { for (int j = 0; j < mat.cols; j++) { switch (mat.type()) { case CV_8U: // 8位无符号整数 outFile << static_cast<int>(mat.at<uchar>(i, j)); break; case CV_8S: // 8位有符号整数 outFile << static_cast<int>(mat.at<char>(i, j)); break; case CV_16U: // 16位无符号整数 outFile << mat.at<ushort>(i, j); break; case CV_16S: // 16位有符号整数 outFile << mat.at<short>(i, j); break; case CV_32S: // 32位有符号整数 outFile << mat.at<int>(i, j); break; case CV_32F: // 32位浮点数 outFile << mat.at<float>(i, j); break; case CV_64F: // 64位浮点数 outFile << mat.at<double>(i, j); break; default: std::cerr << "不支持的矩阵类型!" << std::endl; return; } if (j < mat.cols - 1) { outFile << " "; } } outFile << std::endl; } outFile.close(); } ``` 对于多通道矩阵,上面的函数只处理了单通道。多通道的情况,每个像素有多个值,我们需要在同一个像素位置输出多个值,并用空格分隔每个通道的值,同时像素之间也要用空格分隔(或者换一种方式,比如每行一个通道?)。这取决于用户的需求。 如果用户需要保存多通道矩阵,我们可以修改函数,例如: ```cpp void writeMatToTxt(const cv::Mat& mat, const std::string& filename) { std::ofstream outFile(filename); if (!outFile) { std::cerr << "无法打开文件: " << filename << std::endl; return; } outFile << std::fixed << std::setprecision(6); for (int i = 0; i < mat.rows; i++) { for (int j = 0; j < mat.cols; j++) { // 处理每个通道 for (int c = 0; c < mat.channels(); c++) { switch (mat.type()) { case CV_8UC3: // 这里我们以CV_8UC3为例,但实际类型是组合的 // 实际上,我们根据深度(即基本数据类型)来处理 default: // 获取深度类型 int depth = mat.depth(); if (depth == CV_8U) { outFile << static_cast<int>(mat.ptr<cv::Vec3b>(i)[j][c]); } else if (depth == CV_32F) { outFile << mat.ptr<cv::Vec3f>(i)[j][c]; } // 其他类型类似 break; } if (c < mat.channels() - 1) { outFile << " "; } } if (j < mat.cols - 1) { outFile << " "; // 像素之间的分隔符(同一行内) } } outFile << std::endl; } outFile.close(); } ``` 但是,由于OpenCV矩阵类型组合很多,处理起来比较繁琐。我们可以使用mat.ptr<T>(i)来获取行指针,然后根据通道数遍历。但T需要根据深度和通道数确定。我们可以使用cv::Mat_来简化,但这里我们提供一个更通用的方法,使用mat.elemSize()和mat.elemSize1()等,但这样代码会复杂。 另一种简单的方法是使用mat.reshape(1)将矩阵转换为单通道(即按行排列,每个通道作为一个独立的行?),但这样会改变矩阵的结构。或者,我们可以将多通道矩阵视为一个二维单通道矩阵,其中每个元素是一个向量,但我们希望将向量展开。 实际上,我们更倾向于使用OpenCV的迭代器,但为了简单,我们假设用户知道矩阵的通道数,并指定一个类型(如Vec3b,Vec3f等)。因此,我们可以使用模板函数。 但是,由于时间有限,我们提供一种针对多通道的通用方法,使用mat.at<cv::Vec<T, n>>(i, j)的方式,但需要知道通道数和类型。我们可以通过mat.depth()和mat.channels()来推断,但这样需要很多switch。 鉴于复杂度,我们建议用户如果矩阵是多通道的,可以分别保存每个通道,或者使用FileStorage保存为YAML格式。 因此,如果用户需要保存多通道矩阵为TXT,我们可以这样写: ```cpp // 假设矩阵是连续存储的,我们可以使用ptr std::ofstream outFile(filename); int depth = mat.depth(); int channels = mat.channels(); for (int i = 0; i < mat.rows; i++) { const uchar* p = mat.ptr<uchar>(i); for (int j = 0; j < mat.cols; j++) { for (int c = 0; c < channels; c++) { switch (depth) { case CV_8U: outFile << static_cast<int>(p[j * channels + c]); break; case CV_8S: outFile << static_cast<int>(reinterpret_cast<const char*>(p)[j * channels + c]); break; case CV_16U: outFile << reinterpret_cast<const ushort*>(p)[j * channels + c]; break; case CV_16S: outFile << reinterpret_cast<const short*>(p)[j * channels + c]; break; case CV_32S: outFile << reinterpret_cast<const int*>(p)[j * channels + c]; break; case CV_32F: outFile << reinterpret_cast<const float*>(p)[j * channels + c]; break; case CV_64F: outFile << reinterpret_cast<const double*>(p)[j * channels + c]; break; default: break; } if (c < channels - 1) { outFile << " "; } } if (j < mat.cols - 1) { outFile << " "; } } outFile << std::endl; } outFile.close(); ``` 这种方法比较通用,但要注意指针的类型转换。另外,对于非连续存储的矩阵OpenCV的ptr会自动处理行边界,所以是安全的。 综上所述,我们提供了两种方法: 1. 对于单通道矩阵,使用简单的循环和at方法。 2. 对于多通道矩阵,使用指针遍历。 但为了代码的通用性,我们可以将多通道的方法同时适用于单通道。 因此,我们提供一个通用的函数,可以处理任意通道和任意基本类型的矩阵(除了某些复杂类型)。 注意:矩阵数据类型必须是基本数据类型(如8U, 32F等),不能是自定义类型或者用户定义的类型。 下面是一个完整的示例函数: ```cpp #include <fstream> #include <iomanip> #include <opencv2/opencv.hpp> bool writeMatToTxt(const cv::Mat& mat, const std::string& filename, int precision = 6) { std::ofstream outFile(filename); if (!outFile.is_open()) { std::cerr << "无法打开文件: " << filename << std::endl; return false; } outFile << std::fixed << std::setprecision(precision); int depth = mat.depth(); int channels = mat.channels(); for (int i = 0; i < mat.rows; i++) { const uchar* p = mat.ptr<uchar>(i); for (int j = 0; j < mat.cols; j++) { // 对于每个像素的每个通道 for (int c = 0; c < channels; c++) { // 计算当前元素在行中的位置 int offset = j * channels * mat.elemSize1() + c * mat.elemSize1(); switch (depth) { case CV_8U: outFile << static_cast<int>(*(p + offset)); break; case CV_8S: outFile << static_cast<int>(*reinterpret_cast<const char*>(p + offset)); break; case CV_16U: outFile << *reinterpret_cast<const unsigned short*>(p + offset); break; case CV_16S: outFile << *reinterpret_cast<const short*>(p + offset); break; case CV_32S: outFile << *reinterpret_cast<const int*>(p + offset); break; case CV_32F: outFile << *reinterpret_cast<const float*>(p + offset); break; case CV_64F: outFile << *reinterpret_cast<const double*>(p + offset); break; default: std::cerr << "不支持的矩阵深度类型!" << std::endl; outFile.close(); return false; } if (c < channels - 1) { outFile << " "; } } if (j < mat.cols - 1) { outFile << " "; } } outFile << std::endl; } outFile.close(); return true; } ``` 这个函数可以处理任意通道和任意基本深度的矩阵。注意,我们使用mat.elemSize1()来获取每个元素每个通道)的字节数,然后计算偏移量。 但是,这种方法比较复杂,而且容易出错。实际上,OpenCV提供了cv::MatConstIterator_来迭代矩阵,但这里为了效率我们使用指针。 注意:这个函数没有经过充分测试,用户在使用时应该注意。 最后,我们提供这个函数作为参考。 如果用户需要更简单的方法,并且矩阵不大,也可以使用FileStorage保存为YAML,然后手动删除标记(不推荐)。 因此,我们推荐使用上述函数将cv::Mat保存为TXT文件。 如果用户需要读取这个TXT文件,那么需要知道矩阵的行列数、通道数和数据类型,因为TXT文件不包含这些头信息。所以,用户需要自己记录。 而使用FileStorage保存的YAML文件则包含这些信息,方便读取。 所以,请用户权衡。 根据引用[2],用户也可以考虑保存为其他格式,但用户要求是TXT,所以我们按照要求给出方案。 总结:我们提供了将cv::Mat保存为TXT文件的方法,即通过循环遍历矩阵每个元素,并写入文本文件,同时处理了不同的数据类型和通道数。 注意:如果矩阵很大,写入可能会比较慢,而且文件也会很大。 相关问题: 1. 如何从TXT文件中读取数据并重建为cv::Mat对象? 2. 除了TXT文件,OpenCV还支持哪些矩阵数据的文本格式? 3. 如何保存和加载OpenCVcv::Mat为二进制文件(非文本)?</think>### 使用 OpenCVcv::Mat 保存为文本文件的方法 在 OpenCV 中将 `cv::Mat` 对象保存为文本文件(.txt)主要有两种方法,下面分别说明操作步骤和示例代码: #### 方法 1:使用 FileStorage 类(推荐) 这是 OpenCV 官方推荐的方式,支持结构化保存和读取,生成的文本文件可读性好。 ```cpp #include <opencv2/opencv.hpp> int main() { // 创建示例矩阵 (3x3 浮点矩阵) cv::Mat mat = (cv::Mat_<float>(3,3) << 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9); // 保存为文本文件 cv::FileStorage fs("matrix.txt", cv::FileStorage::WRITE); fs << "data" << mat; // 使用键名 "data" 存储矩阵 fs.release(); return 0; } ``` **生成的文件内容示例**: ``` %YAML:1.0 --- data: !!opencv-matrix rows: 3 cols: 3 dt: f data: [ 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9 ] ``` #### 方法 2:手动写入纯数据格式 直接遍历矩阵元素写入文件,适合需要自定义格式的场景: ```cpp #include <fstream> #include <opencv2/opencv.hpp> int main() { cv::Mat mat = cv::Mat::eye(3, 3, CV_32F); // 3x3 单位矩阵 std::ofstream outFile("matrix_raw.txt"); for (int i = 0; i < mat.rows; i++) { for (int j = 0; j < mat.cols; j++) { // 根据数据类型访问元素 if (mat.type() == CV_32F) { outFile << mat.at<float>(i, j) << " "; } // 可扩展其他数据类型... } outFile << "\n"; // 每行结束换行 } outFile.close(); return 0; } ``` **生成的文件内容示例**: ``` 1 0 0 0 1 0 0 0 1 ``` ### 关键注意事项 1. **数据类型处理**: - 访问元素时需匹配矩阵数据类型(如 `CV_32F` 用 `at<float>`, `CV_8U` 用 `at<uchar>`) - 多通道矩阵需额外遍历通道维度 2. **性能优化**: ```cpp // 使用指针直接访问行数据(更高效) for (int i = 0; i < mat.rows; i++) { float* rowPtr = mat.ptr<float>(i); for (int j = 0; j < mat.cols; j++) { outFile << rowPtr[j] << " "; } outFile << "\n"; } ``` 3. **文件格式选择**: - `FileStorage` 格式:适合带元数据的结构化存储(推荐)[^1] - 纯数据格式:适合与其他数值计算软件交互 ### 从文本文件读取矩阵 使用 `FileStorage` 读取: ```cpp cv::Mat loadedMat; cv::FileStorage fs("matrix.txt", cv::FileStorage::READ); fs["data"] >> loadedMat; // 使用保存时的键名 fs.release(); ``` ### 相关问题 1. 如何将多通道 cv::Mat (如 RGB 图像) 保存为文本文件? 2. 当处理大型矩阵时,如何优化文本文件的读写性能? 3. 除了文本文件,OpenCV 还支持哪些矩阵持久化格式?各自的优缺点是什么? 4. 如何从自定义格式的文本文件中读取数据并重建 cv::Mat 对象? [^1]: FileStorage 支持 YAML/XML 格式的文本存储,可保留矩阵维度、数据类型等元信息。 [^2]: 对于纯数值数据交换,建议设置精度避免浮点误差:`outFile << std::setprecision(9) << value;`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值