OpenCV2-Mat类、图像加载与保存

2023-10-08 12:44
文章标签 加载 图像 保存 opencv2 mat

本文主要是介绍OpenCV2-Mat类、图像加载与保存,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

OpenCV2-Mat类、图像加载与保存

    • 1.Mat类介绍
    • 2.数据类型与取值范围
    • 3.Mat类构造与赋值
    • 4.Mat矩阵运算
    • 5.Mat属性与元素的遍历
      • 方法1 pt<>
      • 方法2 迭代器方法
      • 方法3 at<>
      • 方法4 data成员
    • 6.图像的读取、显示、保存


1.Mat类介绍

Mat类分为矩阵头和指向存储数据的矩阵指针两部分。

矩阵头:包含矩阵的尺寸、存储方法、地址和引用计数等,矩阵头的大小是一个常数。

在OpenCV中复制和传递图像时,只是复制了矩阵头和指向存储数据的指针

Mat a; // 矩阵头
a = imread("lena.jpg"); // 矩阵指针指向像素数据
Mat b = a; // 复制矩阵头和数据指针

C++中使用引用计数管理矩阵数据。

查看Mat类继承关系图:声明一个存放指定类型的Mat变量

Mat A = Mat_<double>(3, 3);

2.数据类型与取值范围

CV_8U - 8-bit unsigned integers ( 0..255 )
CV_8S - 8-bit signed integers ( -128..127 )
CV_16U - 16-bit unsigned integers ( 0..65535 )
CV_16S - 16-bit signed integers ( -32768..32767 )
CV_32S - 32-bit signed integers ( -2147483648..2147483647 )
CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )
CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )

仅有数据类型还是不够的,还需要定义图像数据的通道数(Channel)。C1、C2、C3、C4分别表示单通道、双通道、3通道、4通道。

由于每一种数据类型都存在多个通道的情况,所以将数据类型与通道数结合便得到了OpenCV中对图像数据类型的完整定义。例如CV_8UC1 表示8位单通道数据表示8位灰度图。

创建一个声明通道数和数据类型的Mat类:

Mat a(640, 480, CV_8UC3); // 3通道用于存放彩色图像
Mat a(3, 3, CV_8UC1); // 单通道用于存放灰度图像
Mat a(3, 3, CV_8U); // 单通道矩阵,C1标识可忽略

3.Mat类构造与赋值

// 1.默认构造函数
Mat::Mat();// 2.根据矩阵尺寸和类型构造
Mat::Mat(int rows, int cols, int type);// 3.用Size结构构造
Mat::Mat(Size size(), int type); 
Mat a(Size(480, 640), CV_8UC1); // 640x480单通道矩阵 

注意使用Size结构时,行和列顺序相反。

// 4.拷贝构造 浅拷贝
Mat::Mat(const Mat& m);// 5.利用已有的矩阵的子内容构造 浅拷贝 共享数据
Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all());
Mat b(a, Range(2,5), Range(2,5)); // 2-5行,2-5列拷贝,
Mat c(a, Range(2,5)); // 2-5行,所有列拷贝

赋值:

// 1.构造时赋值
Mat::Mat(int rows, int cols, int type, const Scalar& s);
// 将每个元素要赋值的变量放到Scalar结构中,如Scalar(0,0,255),将会给每个像素的3个通道分别赋值为0、0、255
Mat a(2,2,CV_8UC3,Scalar(0,0,255)); // 3通道矩阵,每个像素都是0、0、255
Mat b(2,2,CV_8UC2,Scalar(0,255));   // 2通道矩阵,每个像素都是0、255
Mat c(2,2,CV_8UC1,Scalar(255));     // 单通道矩阵,每个像素都是255
// 2.枚举赋值 数组赋值
Mat a = (Mat_<int>(3,3) << 1,2,3,4,5,6,7,8,9);
Mat b = Mat(2,2,CV_32FC2, a);
// a[0][0]:1,2
// a[0][1]:3,4
// a[1][0]:5,6
// a[1][1]:7,8// 3.循环赋值
Mat c = Mat_<int>(3,3);
for(int i = 0; i < c.rows; ++i)
{for(int j = 0; j < c.cols; ++j){c.at<int>(i, j) = i+j;}
}

Mat类中提供了可以快速赋值的方法,可以初始化指定的矩阵。例如生成单位矩阵、对角矩阵、所有元素都为0或者1的矩阵等。

// 3.类方法赋值
Mat a = Mat::eye(3,3,CV_8UC1); // 单位矩阵,如果不是方阵,则在主对角位置放1
Mat b = (Mat_<int>(1,3) << 1,2,3);
Mat c = Mat::diag(b); // 对角矩阵,参数必须是向量,用来存放对角元素的值
Mat d = Mat::ones(3,3,CV_8UC1); // 全为1的矩阵,参数含义同eye
Mat e = Mat::zeros(4,2,CV_8UC3); // 全为0的矩阵,参数含义同eye

4.Mat矩阵运算

1.两个Mat类变量进行加减运算,必须保证数据类型相同,

2.Mat类变量与常数进行乘除时,结果的数据类型保留Mat类变量的数据类型,Mat类变量的每一个元素都要与常数乘除。

3.两个矩阵的内积和对应位的乘法:

Mat a = (Mat_<int>(3,3) << 1,2,3,4,5,6,7,8,9);
Mat b = (Mat_<int>(3,3) << 1,2,3,4,5,6,7,8,9);
Mat j = a*b; // 不可以,必须是float、double类型
int k = a.dot(b);
Mat m = a.mul(b);

*矩阵乘积运算:数据类型必须是CV_32FC1、CV_32FC2、CV_64FC1、CV_64FC2

dot点积:把矩阵看成一维进行点积运算,结果永远是double类型。

mul对应位相乘。可以是任意类型。注意乘积结果的溢出。

5.Mat属性与元素的遍历

Mat类常用属性:

属性作用
cols矩阵的列数
rows矩阵的行数
step以字节为单位的矩阵的有效宽度
elemSize()每个元素的字节数
total()矩阵中元素的个数
channels()矩阵的通道数
Mat a(3, 4, CV_32FC3);cout << a.rows << endl;		  // 3
cout << a.cols << endl;		  // 4
cout << a.step << endl;		  // 48
cout << a.elemSize() << endl; // 12
cout << a.total() << endl;    // 12
cout << a.channels() << endl; // 3

共3x4个元素,每个元素的类型是float类型,由于是3通道,所以每个元素有3个float类型,所以每个元素占据4x3个字节数,也即是elemSize()返回12。每行有4列,也即4个元素,所以step为4x12共48字节。(step/cols可以求出每个元素所占据的字节数,再与channels()属性结合,可以知道每个通道的字节数。)

// 矩阵元素的类型 CV_16SC3
int type() const;// return true if Mat::total() is 0 or if Mat::data is NULL
bool empty() const;

方法1 pt<>

通过.ptr<>函数得到一行的指针,并用[]操作符访问某一列的像素值

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/core/utils/logger.hpp>using namespace cv;
using namespace std;void travel_1(Mat& image, int div = 64)
{int nr = image.rows;int nc = image.cols * image.channels(); // 每行总元素数,也即遍历的列数for (int row = 0; row < nr; ++row){// 通过ptr<>函数得到一行的指针uchar* data = image.ptr<uchar>(row);for (int col = 0; col < nc; ++col){// 使用[]操作符访问某一列的像素值data[col] = data[col] / div * div + div / 2;// *data++ = *data / div * div + div / 2;}}
}int main()
{cout << "OpenCV Version: " << CV_VERSION << endl;utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);Mat lena_c = imread("lena_c.bmp", IMREAD_UNCHANGED);imshow("lena_c", lena_c);travel_1(lena_c);imshow("lena_c changed", lena_c);int k = waitKey(0); // Wait for a keystroke in the windowreturn 0;
}

方法2 迭代器方法

void travel_2(Mat& image, int div = 64)
{// get iteratorscv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();for (; it != itend; ++it){(*it)[0] = (*it)[0] / div * div + div / 2; // B channel(*it)[1] = (*it)[1] / div * div + div / 2; // G channel(*it)[2] = (*it)[2] / div * div + div / 2; // R channel}
}

方法3 at<>

void travel_3(Mat& image, int div = 64)
{int nr = image.rows;int nc = image.cols;for (int row = 0; row < nr; row++){for (int col = 0; col < nc; col++){image.at<cv::Vec3b>(row, col)[0] = image.at<cv::Vec3b>(row, col)[0] / div * div + div / 2; // B channelimage.at<cv::Vec3b>(row, col)[1] = image.at<cv::Vec3b>(row, col)[1] / div * div + div / 2; // G channelimage.at<cv::Vec3b>(row, col)[2] = image.at<cv::Vec3b>(row, col)[2] / div * div + div / 2; // R channel}}
}

方法4 data成员

void travel_4(Mat& image, int div = 64)
{int nr = image.rows;int nc = image.cols;for (int row = 0; row < nr; row++){for (int col = 0; col < nc; col++){image.data[row * nc * 3 + col * 3 + 0] = image.data[row * nc * 3 + col * 3 + 0] / div * div + div / 2; // B channelimage.data[row * nc * 3 + col * 3 + 1] = image.data[row * nc * 3 + col * 3 + 1] / div * div + div / 2; // G channelimage.data[row * nc * 3 + col * 3 + 2] = image.data[row * nc * 3 + col * 3 + 2] / div * div + div / 2; // R channel}}
}// 简单优化
void travel_4(Mat& image, int div = 64)
{int nr = image.rows;int nc = image.cols;for (int row = 0; row < nr; row++){for (int col = 0; col < nc; col++){int idx = (row * nc + col) * image.channels();image.data[idx + 0] = image.data[idx + 0] / div * div + div / 2; // B channelimage.data[idx + 1] = image.data[idx + 1] / div * div + div / 2; // G channelimage.data[idx + 2] = image.data[idx + 2] / div * div + div / 2; // R channel}}
}

6.图像的读取、显示、保存

读取显示:

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/core/utils/logger.hpp>using namespace cv;
using namespace std;int main()
{cout << "OpenCV Version: " << CV_VERSION << endl;utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);Mat lena_c = imread("lena_c.bmp", IMREAD_UNCHANGED);string winname = "lena_c";// 创建窗口 显示namedWindow(winname);imshow(winname, lena_c);destroyAllWindows();int k = waitKey(0); // Wait for a keystroke in the windowreturn 0;
}

imwrite:第三个参数设置保存图片格式属性标志

void AlphaMat(Mat &mat)
{CV_Assert(mat.channels() == 4);for (int i = 0; i < mat.rows; ++i){for (int j = 0; j < mat.cols; ++j){Vec4b& bgra = mat.at<Vec4b>(i, j);bgra[0] = UCHAR_MAX;  // 蓝色通道bgra[1] = saturate_cast<uchar>((float(mat.cols - j)) / ((float)mat.cols) * UCHAR_MAX);  // 绿色通道bgra[2] = saturate_cast<uchar>((float(mat.rows - i)) / ((float)mat.rows) * UCHAR_MAX);  // 红色通道bgra[3] = saturate_cast<uchar>(0.5 * (bgra[1] + bgra[2]));  // Alpha通道}}
}
int main(int agrc, char** agrv)
{// Create mat with alpha channelMat mat(480, 640, CV_8UC4);AlphaMat(mat);vector<int> compression_params;compression_params.push_back(IMWRITE_PNG_COMPRESSION);  //PNG格式图像压缩标志compression_params.push_back(9);  //设置最高压缩质量		bool result = imwrite("alpha.png", mat, compression_params);if (!result){cout << "保存成PNG格式图像失败" << endl;return -1;}cout << "保存成功" << endl;return 0;
}

这篇关于OpenCV2-Mat类、图像加载与保存的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/165364

相关文章

Spring Boot 配置文件之类型、加载顺序与最佳实践记录

《SpringBoot配置文件之类型、加载顺序与最佳实践记录》SpringBoot的配置文件是灵活且强大的工具,通过合理的配置管理,可以让应用开发和部署更加高效,无论是简单的属性配置,还是复杂... 目录Spring Boot 配置文件详解一、Spring Boot 配置文件类型1.1 applicatio

SpringBoot项目启动报错"找不到或无法加载主类"的解决方法

《SpringBoot项目启动报错找不到或无法加载主类的解决方法》在使用IntelliJIDEA开发基于SpringBoot框架的Java程序时,可能会出现找不到或无法加载主类com.example.... 目录一、问题描述二、排查过程三、解决方案一、问题描述在使用 IntelliJ IDEA 开发基于

Android WebView无法加载H5页面的常见问题和解决方法

《AndroidWebView无法加载H5页面的常见问题和解决方法》AndroidWebView是一种视图组件,使得Android应用能够显示网页内容,它基于Chromium,具备现代浏览器的许多功... 目录1. WebView 简介2. 常见问题3. 网络权限设置4. 启用 JavaScript5. D

SpringBoot项目启动错误:找不到或无法加载主类的几种解决方法

《SpringBoot项目启动错误:找不到或无法加载主类的几种解决方法》本文主要介绍了SpringBoot项目启动错误:找不到或无法加载主类的几种解决方法,具有一定的参考价值,感兴趣的可以了解一下... 目录方法1:更改IDE配置方法2:在Eclipse中清理项目方法3:使用Maven命令行在开发Sprin

使用Python开发一个图像标注与OCR识别工具

《使用Python开发一个图像标注与OCR识别工具》:本文主要介绍一个使用Python开发的工具,允许用户在图像上进行矩形标注,使用OCR对标注区域进行文本识别,并将结果保存为Excel文件,感兴... 目录项目简介1. 图像加载与显示2. 矩形标注3. OCR识别4. 标注的保存与加载5. 裁剪与重置图像

spring-boot-starter-thymeleaf加载外部html文件方式

《spring-boot-starter-thymeleaf加载外部html文件方式》本文介绍了在SpringMVC中使用Thymeleaf模板引擎加载外部HTML文件的方法,以及在SpringBoo... 目录1.Thymeleaf介绍2.springboot使用thymeleaf2.1.引入spring

关于Spring @Bean 相同加载顺序不同结果不同的问题记录

《关于Spring@Bean相同加载顺序不同结果不同的问题记录》本文主要探讨了在Spring5.1.3.RELEASE版本下,当有两个全注解类定义相同类型的Bean时,由于加载顺序不同,最终生成的... 目录问题说明测试输出1测试输出2@Bean注解的BeanDefiChina编程nition加入时机总结问题说明

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解

vscode保存代码时自动eslint格式化图文教程

《vscode保存代码时自动eslint格式化图文教程》:本文主要介绍vscode保存代码时自动eslint格式化的相关资料,包括打开设置文件并复制特定内容,文中通过代码介绍的非常详细,需要的朋友... 目录1、点击设置2、选择远程--->点击右上角打开设置3、会弹出settings.json文件,将以下内

基于WinForm+Halcon实现图像缩放与交互功能

《基于WinForm+Halcon实现图像缩放与交互功能》本文主要讲述在WinForm中结合Halcon实现图像缩放、平移及实时显示灰度值等交互功能,包括初始化窗口的不同方式,以及通过特定事件添加相应... 目录前言初始化窗口添加图像缩放功能添加图像平移功能添加实时显示灰度值功能示例代码总结最后前言本文将