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

相关文章

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.

springboot加载不到nacos配置中心的配置问题处理

《springboot加载不到nacos配置中心的配置问题处理》:本文主要介绍springboot加载不到nacos配置中心的配置问题处理,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录springboot加载不到nacos配置中心的配置两种可能Spring Boot 版本Nacos

Python中OpenCV与Matplotlib的图像操作入门指南

《Python中OpenCV与Matplotlib的图像操作入门指南》:本文主要介绍Python中OpenCV与Matplotlib的图像操作指南,本文通过实例代码给大家介绍的非常详细,对大家的学... 目录一、环境准备二、图像的基本操作1. 图像读取、显示与保存 使用OpenCV操作2. 像素级操作3.

C/C++的OpenCV 进行图像梯度提取的几种实现

《C/C++的OpenCV进行图像梯度提取的几种实现》本文主要介绍了C/C++的OpenCV进行图像梯度提取的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录预www.chinasem.cn备知识1. 图像加载与预处理2. Sobel 算子计算 X 和 Y

c/c++的opencv图像金字塔缩放实现

《c/c++的opencv图像金字塔缩放实现》本文主要介绍了c/c++的opencv图像金字塔缩放实现,通过对原始图像进行连续的下采样或上采样操作,生成一系列不同分辨率的图像,具有一定的参考价值,感兴... 目录图像金字塔简介图像下采样 (cv::pyrDown)图像上采样 (cv::pyrUp)C++ O

使用Python获取JS加载的数据的多种实现方法

《使用Python获取JS加载的数据的多种实现方法》在当今的互联网时代,网页数据的动态加载已经成为一种常见的技术手段,许多现代网站通过JavaScript(JS)动态加载内容,这使得传统的静态网页爬取... 目录引言一、动态 网页与js加载数据的原理二、python爬取JS加载数据的方法(一)分析网络请求1

IDEA下"File is read-only"可能原因分析及"找不到或无法加载主类"的问题

《IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题》:本文主要介绍IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题,具有很好的参... 目录1.File is read-only”可能原因2.“找不到或无法加载主类”问题的解决总结1.File

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示

在 PyQt 加载 UI 三种常见方法

《在PyQt加载UI三种常见方法》在PyQt中,加载UI文件通常指的是使用QtDesigner设计的.ui文件,并将其转换为Python代码,以便在PyQt应用程序中使用,这篇文章给大家介绍在... 目录方法一:使用 uic 模块动态加载 (不推荐用于大型项目)方法二:将 UI 文件编译为 python 模

Python+wxPython构建图像编辑器

《Python+wxPython构建图像编辑器》图像编辑应用是学习GUI编程和图像处理的绝佳项目,本教程中,我们将使用wxPython,一个跨平台的PythonGUI工具包,构建一个简单的... 目录引言环境设置创建主窗口加载和显示图像实现绘制工具矩形绘制箭头绘制文字绘制临时绘制处理缩放和旋转缩放旋转保存编