关闭

Opencv中的Mat类使用方法总结

标签: opencvMat
1326人阅读 评论(6) 收藏 举报
分类:

今天在看Opencv的SIFT源码,至于有关于SIFT算法的博客还没有写完,等着我把源码看完再一起写完吧。

之前用Opencv编过不少的程序了,没想道OpenCV 2.0版本里最基础的Mat类用法还是有些不清楚,这里就总结一下

一、Mat类的综述


1、Mat类存储图像

Mat类是OpenCV里使用广泛的一个类,其中我认为最重要的一个作用就是作为存储图像的数据结构。那么Mat类如何存储的图像呢?
我们都知道图像分为彩色图像和灰度图像,这里我有一个误区,一直认为彩色图像是一种三维矩阵,就是立方体的那种结构,一个图像分为三层。
但是这种理解是错误的,是错误的,是错误的!
其实在存储的图像不管是彩色的还是灰度图像,都是二维的矩阵,具体的存储格式如下
(1)灰度图像的格式:

(2)彩色图像的格式:

看到了吗,虽然彩色图像由BGR三个通道,但是是存储在同一个平面内的,只不过OpenCV在这里把三列才当作一列,因此有img.cols等于图像的列数。
一般我们用Opencv读取的灰度图像的数据类型为uchar类型的,而彩色图像的一个像素的数据类型为<Vec3b>类型的,灰度图一个像素占用1个字节,而彩色图像一个像素3个字节。
接下来就引出了我们如何按像素读取图像呢?

2、Mat按像素读取图像内容

这里主要介绍两种方法,一种非常简单,易于编程,但是效率会比较低;另外一种效率高,但是不太好记。下面依次看代码:
(1)易于编程的
对于灰度图像进行操作:
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main()
{
	Mat img = imread("1.jpg");
	resize(img, img, Size(375, 500));//resize为500*375的图像
	cvtColor(img, img, CV_RGB2GRAY);//转为灰度图
	imshow("gray_ori", img);
	for (int i = 0; i < img.rows; i++)
	{
		for (int j = 0; j < img.cols; j++)
		{
			//at<类型>(i,j)进行操作,对于灰度图
			img.at<uchar>(i, j) = i+j;
		}
	}
	imshow("gray_result", img);
	waitKey(0);
	return 0;
}
结果图如下:
可以看出,使用at的操作很容易定位,就跟操作一个普通的二维数组一样,那么对于彩色图像呢,方法很简单,只需要把at<类型>中的类型改变为Vec3b即可,代码如下:
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main()
{
	Mat img = imread("1.jpg");
	resize(img, img, Size(375, 500));//resize为500*375的图像
	imshow("ori", img);
	for (int i = 0; i < img.rows; i++)
	{
		for (int j = 0; j < img.cols; j++)
		{
			//at<类型>(i,j)进行操作,对于灰度图
			img.at<Vec3b>(i, j)[0] = 255;//对于蓝色通道进行操作
			//img.at<Vec3b>(i, j)[1] = 255;//对于绿色通道进行操作
			//img.at<Vec3b>(i, j)[2] = 255;//对于红色通道进行操作
		}
	}
	imshow("result", img);
	waitKey(0);
	return 0;
}

效果图如下:


(2)采用指针对图像进行访问
这里直接写对于彩色图像的操作:
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\core\core.hpp>
#include <iostream>

using namespace cv;
using namespace std;
int main()
{
    Mat img = imread("1.jpg");
	int rows = img.rows;
	int cols = img.cols * img.channels();
	if(img.isContinuous())//判断是否在内存中连续
	{
		cols = cols * rows;
		rows = 1;
	}
	imshow("ori",img);
	for(int i = 0;i<rows;i++)
	{
		//调取存储图像内存的第i行的指针
		uchar *pointer = img.ptr<uchar>(i);

		for(int j = 0;j<cols;j += 3)
		{
			//pointer[j] = 255;//对蓝色通道进行操作
			//pointer[j+1] = 255;//对绿色通道进行操作
			pointer[j+2] = 255;//对红色通道进行操作
		}
	}
	imshow("result",img);
	waitKey();
    return 0;
}
从上面个的代码中可以很明显的看出我们是如何操作图像的数据以及图像在Mat中的存放格式的,就是我们上面那个彩色图像的存放示意图中的格式,这里把彩色图像中的一个像素点分成三份,每一份都是uchar类型,因此我们这里不需要使用Vec3b数据类型。把彩色图像看成一个rows * (cols * channels)的二维数组进行操作,其中的每个元素的类型都是uchar类型。
这里需要注意的是j += 3是因为我们按照一个像素点进行操作,而一个像素点在这里面又被分成三份,因此需要j += 3,如果是灰度图像则直接j++即可
这种操作方式虽然复杂一些,但是执行效率会比上面的算法高很多。
下面是执行的结果:
下面我们给出这两种方式进行同一操作的时间对比图():
测试代码如下:
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\core\core.hpp>
#include <iostream>
#include <time.h>

using namespace cv;
using namespace std;
int main()
{
    Mat img = imread("1.jpg");
	Mat img2;
	img.copyTo(img2);

	cout<<"图像的行数: "<<img.rows<<endl;
	cout<<"图像的列数: "<<img.cols<<endl;
	cout<<"图像通道数: "<<img.channels()<<endl;

	double time1;
	time1 = (double)getTickCount();

	int rows = img.rows;
	int cols = img.cols * img.channels();
	if(img.isContinuous())//判断是否在内存中连续
	{
		cols = cols * rows;
		rows = 1;
	}
	
	for(int i = 0;i<rows;i++)
	{
		//调取存储图像内存的第i行的指针
		uchar *pointer = img.ptr<uchar>(i);

		for(int j = 0;j<cols;j += 3)
		{
			//pointer[j] = 255;//对蓝色通道进行操作
			//pointer[j+1] = 255;//对绿色通道进行操作
			pointer[j+2] = 255;//对红色通道进行操作
		}
	}
	time1 = 1000 * ((double)getTickCount() - time1) / getTickFrequency();
	//imshow("result",img);
	cout<<"第一种方法用时: "<<time1<<endl;

	double time2 = (double)getTickCount();
	for (int i = 0; i < img2.rows; i++)
	{
		for (int j = 0; j < img2.cols; j++)
		{
			//at<类型>(i,j)进行操作,对于灰度图
			img2.at<Vec3b>(i, j)[0] = 255;//对于蓝色通道进行操作
			//img.at<Vec3b>(i, j)[1] = 255;//对于绿色通道进行操作
			//img.at<Vec3b>(i, j)[2] = 255;//对于红色通道进行操作
		}
	}
	time2 = 1000 * ((double)getTickCount() - time2)/getTickFrequency();
	cout<<"第二种方法用时: "<<time2<<endl;
	imshow("img",img);
	imshow("img2",img2);
	waitKey(0);
    return 0;
}

2、Mat类中的变量参数

Mat类中包含了很多的变量,比如用的比较多的rows,cols等,这些我在这里就不总结了。
这里总结的是Mat类中用的比较少的几个变量和函数,step1(),step[],size,elemSize和elemSize1。
step1(i)表示的是Mat中的每一维的通道数;
step[i]表示的是Mat中的每一维的大小,以字节为单位;
size[i]表示的是Mat中元素的个数;
elemSize()表示的是每个元素的大小 ,以字节为单位;
elemSize1()表示的一个元素中每个通道的大小,以字节为单位。
这么说恐怕不好理解,我们用代码进行测试:
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\core\core.hpp>
#include <iostream>
#include <time.h>

using namespace cv;
using namespace std;
int main()
{
    Mat img = imread("1.jpg");
	cout<<"img.rows: "<<img.rows<<" img.cols: "<<img.cols<<" img.channels(): "<<img.channels()<<endl;
	cout<<"###############################################"<<endl;
	cout<<"img.step1(0): "<<img.step1(0)<<endl;
	cout<<"img.step1(1): "<<img.step1(1)<<endl;
	cout<<"img.step[0]: "<<img.step[0]<<endl;
	cout<<"img.step[1]: "<<img.step[1]<<endl;
	cout<<"img.size[0]: "<<img.size[0]<<endl;
	cout<<"img.size[1]: "<<img.size[1]<<endl;
	cout<<"img.elemSize(): "<<img.elemSize()<<endl;
	cout<<"img.elemSize1(): "<<img.elemSize1()<<endl;
    return 0;
}
看一下结果:

可以看出这是一张1000*750的彩色图像。以上面所说,step1(i)表示的每一维的通道数,我们知道彩色图像存储方式是一种二维矩阵,因此只有两维,其中最后一维表示的是一个点,前面一维表示的是线,如果有第三维,那么再前面一维表示的是面。这里有一个规律,就是最后一维一定是点,其他的依次往前推就可以了。
接着分析结果,img.step1(1)表示的是一个点的通道数,我们知道彩色图像的一个像素,通道数为3(BGR),因此img.step1(1) = 3,而img.step1(0)是一行的像素的通道数,因此为750*3 = 2250
而step[i]表示的是每一维中的大小,以字节计数,在彩色图像中一个像素分为三块,每一块为一个字节,因此step[0] = 2250 ,step[1] = 3
size表示的是每一维元素的大小,注意这里是元素,即像素,size[0]表示rows,size[1]表示cols
elemSize表示的是每个元素的大小,以字节计,一个元素分为三块,一块是1字节,因此为3(彩色图像)
elemSize1表示的是一个元素的每个通道的大小,因此为1


1
0
查看评论

OpenCV学习笔记(四十)——再谈OpenCV数据结构Mat详解

我记得开始接触OpenCV就是因为一个算法里面需要2维动态数组,那时候看core这部分也算是走马观花吧,随着使用的增多,对Mat这个结构越来越喜爱,也觉得有必要温故而知新,于是这次再看看Mat。 Mat最大的优势跟STL很相似,都是对内存进行动态的管理,不需要之前用户手动的管理内存,对于一些大型的...
  • yang_xian521
  • yang_xian521
  • 2011-12-28 10:33
  • 139165

OpenCV基础篇之Mat数据结构

程序及分析/* * FileName : MatObj.cpp * Author : xiahouzuoxin @163.com * Version : v1.0 * Date : Thu 15 May 2014 09:12:45 PM CST * Brief : ...
  • xiahouzuoxin
  • xiahouzuoxin
  • 2014-07-30 12:36
  • 50325

【转】OpenCV中Mat的详解

【转】OpenCV中Mat的详解 每次碰到Mat都得反复查具体的用法,网上的基础讲解不多,难得看到一篇,赶快转来收藏~ 原文地址:http://www.opencvchina.com/thread-1039-1-1.html 目标 我们有多种方法可以获得从现实世界的数字图像:数码相机...
  • sinat_31802439
  • sinat_31802439
  • 2015-11-28 13:21
  • 2803

OpenCV中Mat的基本用法

一、矩阵基础操作: Mat image(240, 320, CV8UC3); 第一个参数是rows,该矩阵的行数;第二个参数是cols,该矩阵的列数;第三个参数是该矩阵元素的类型。这句话表示创建一个大小为240×320的矩阵,里面的元素为8位unsigned型,通道数(channel)...
  • u012655441
  • u012655441
  • 2017-04-05 16:02
  • 1557

OpenCV Mat类详解和用法

Mat本质上是由两个数据部分组成的类: (包含信息有矩阵的大小,用于存储的方法,矩阵存储的地址等) 的矩阵头和一个指针,指向包含了像素值的矩阵(可根据选择用于存储的方法采用任何维度存储数据)。矩阵头部的大小是恒定的。然而,矩阵本身的大小因图像的不同而不同,通常是较大的数量级。因此,当你在您的程序中传...
  • guyuealian
  • guyuealian
  • 2017-04-13 16:58
  • 4243

OpenCv学习笔记(一)--OpenCv中Mat类源码的详细解读(1)

OpenCv学习笔记(一)OpenCv中Mat类源码的详细解读(一)(一)Mat类的引述1–自2010年以来,OpenCv的函数库一直是基于C接口构件的,因此,在最初的几个OpenCv版本中,一直使用IplImage的C语言结构体在内存中存储图像。直到今天,这仍旧出现在很多的旧版书籍中,比如经典的《...
  • maweifei
  • maweifei
  • 2016-04-21 19:54
  • 2035

OpenCv学习笔记(九)再谈OpenCv中Mat类源码的详细解读

/*********************************************************************************************************************************** 文件说明: 1)O...
  • maweifei
  • maweifei
  • 2017-04-13 16:09
  • 782

OpenCV中Mat类型格式

在OpenCV中,Mat类型的格式如下: CV_(S|U|F)C 
  • wunghao8
  • wunghao8
  • 2014-09-23 10:04
  • 1202

【OpenCV3】cv::Mat类成员函数详解

cv::Mat为OpenCV2和OpenCV3中最重要的类,不夸张得说,掌握了cv::Mat的操作,就掌握了OpenCV大半,这里主要介绍cv::Mat类的成员函数及其使用。
  • guduruyu
  • guduruyu
  • 2017-03-28 08:58
  • 1802

OpenCV中Mat 类型学习笔记

主要学学习了 如何查看 Mat类型的变量大小  printf("img.size()函数返回值: \n");//   cout   printf("size[0]:%d\n", img.size[0]);//512:线 &#...
  • qing101hua
  • qing101hua
  • 2016-10-12 13:55
  • 296
    个人资料
    • 访问:243391次
    • 积分:2838
    • 等级:
    • 排名:第14854名
    • 原创:68篇
    • 转载:1篇
    • 译文:1篇
    • 评论:162条
    介绍
    关注计算机视觉和深度学习小白一枚,评论和私信可能回不及时,可以邮件联系:lhanchao@mail.ustc.edu.cn。博客可能也写的有问题,希望大家一起交流
    最新评论