matlab opencv 大矩阵计算,2015-04-28-OpenCV for Matlab Users (2) - cv::Mat 类的属性和方法

我是 OpenCV 乃至 C++ 的初学者,过去的两个礼拜里刚把一个 Matlab 程序转化成使用了 OpenCV 的 C++ 代码,过程中遇到了很多困难。这些困难有些来源于我对 Matlab 和 OpenCV 之间语言和类库设计差异的不了解,有些是单纯的不知道 Matlab 函数的 OpenCV 对照物造成的,还有一些是由于 OpenCV 并不具有一些功能的函数。我打算将这些天的所学所得写下来,借鉴《NumPy for Matlab Users》,姑且将这个系列叫作《OpenCV for Matlab Users》。这篇是这个系列的第 2 篇,因为大体结构最早完成,所以也就先出来了。整个系列的大体安排如下:

OpenCV for Matlab Users (1) - MATLAB 与 OpenCV / C++ 设计上的差异

OpenCV for Matlab Users (2) - cv::Mat 类的属性和方法

OpenCV for Matlab Users (3) - MATLAB 函数的 OpenCV 对应实现

OpenCV for Matlab Users (4) - MATLAB 与 OpenCV / C++ 易搞混的语法

OpenCV for Matlab Users (5) - OpenCV 自身易搞混的函数辨析

下面开始本章正文:

以我浅薄的 C++ 知识来看,一个对象包括成员变量和成员方法两类,有些貌似是成员变量,其实是方法,这篇先按照是成员变量还是方法分为两类。目前成员变量只有 5 个:rows,cols,dims,data,step,还有一些比如像 channels,depth,elemSize,empty,isContinuous,size,total,type 貌似是成员变量,其实是没有参数的成员方法。

1. 成员变量

rows

成员变量,返回矩阵的行数

int imgRows = img.rows;

std::cout<

cols

是成员变量,返回矩阵的列数

int imgCols = img.cols;

std::cout<

data

成员变量data事实上是指向已分配的内存块的指针,包括图像数据。当不存在数据时,它被简单设置为0.

uchar *data = img.data;

std::cout<

std::cout<

dims

成员变量,返回一个图像的维数,但是好奇怪,我以为会有 3 维的,结果返回的还是 2

int imgDims = img.dims;

std::cout<

step

成员变量,代表以字节为单位的图像的行宽,即列数(包括填补像素),即使你的图像元素类型不是 uchar,step 仍然带代表着行的字节数

int imgStep = img.step;

std::cout<

2. 成员方法

at

成员函数,at(int y, int x) 可以用来存取图像元素。 但是必须在编译期知道图像的数据类型,因为 cv::Mat 可以存放任意数据类型的元素。所以使用 at 方法要指定数据类型,而且 at 方法本身不会进行任何数据类型转换。

cv::Vec3b vec3b = img.at<:vec3b>(0,0);

uchar vec3b0 = img.at<:vec3b>(0,0)[0];

uchar vec3b1 = img.at<:vec3b>(0,0)[1];

uchar vec3b2 = img.at<:vec3b>(0,0)[2];

std::cout<

std::cout<

std::cout<

std::cout<

cv::Vec3b,即由三个 unsigned char 组成的向量。

channels

方法,返回通道数

int imgChannels = img.channels();

std::cout<

clone

貌似也是深拷贝,但是除了返回值类型不同,clone 返回 cv::Mat,而 copyTo 是 void 类型,其余 clone 跟 copyTo 有什么区别我还没搞清楚

cv::Mat cloneMat1 = cv::Mat::ones(3,4,CV_64F);

cv::Mat cloneMat2 = cloneMat1.clone();

cloneMat1.at(0,0) = 0.0;

std::cout<

std::cout<

col

返回指定的一列(从 0 开始)

cv::Mat colMat = img.col(0);

std::cout<

colRange

方法,返回若干列组成的矩阵

cv::Mat colRangeImg = img.colRange(imgCols / 2, imgCols);

cv::imshow("colRangeImg",colRangeImg);

cv::waitKey();

convertTo

方法,在缩放或不缩放的情况下转换为另一种指定的数据类型

cv::Mat doubleImg;

img.convertTo(doubleImg, CV_64FC4);

std::cout<

cv::imshow("doubleImg", doubleImg);

cv::waitKey();

copyTo

把矩阵深拷贝赋值给另一个矩阵

cv::Mat copyMat1 = cv::Mat::ones(3,4,CV_64F);

cv::Mat copyMat2;

copyMat1.copyTo(copyMat2);

copyMat1.at(0,0) = 0.0;

std::cout<

std::cout<

create

create 类似Mat(nrows,ncols,type [,fillValue])构造函数,把当前对象重新绑定到一个新的矩阵对象上

cv::Mat createMat = cv::Mat::ones(3,4,CV_64F);

createMat.create(5,6, CV_8UC(2));

std::cout<

cross

计算两个 3 元素向量的叉乘积,注意,必须是 3 个元素的。

cv::Mat crossVec1 = cv::Mat::ones(1,3,CV_64F);

cv::Mat crossVec2 = cv::Mat::ones(1,3,CV_64F);

cv::Mat crossMat = crossVec1.cross(crossVec2);

std::cout<

depth

方法,该方法返回矩阵元素深度(每个单独的通道类型)的标识符。

int imgDepth = img.depth();

std::cout<

diag

抽取矩阵对角线上的元素,返回的是一个 min(rows,cols) * channels 的矩阵。

cv::Mat diagMat = img.diag();

//std::cout<

std::cout<

std::cout<

dot

内积运算,最后返回一个 double 类型的数。两个矩阵必须是相同大小的。如果是非单行或者非单列矩阵,那么结果相当于把两个矩阵拉成一行或者一列后做内积。多个通道的,每个通道的内积会被加起来。

cv::Mat onesMat1 = cv::Mat::ones(3,4,CV_64F);

cv::Mat onesMat2 = cv::Mat::ones(3,4,CV_64F);

double dotVal = onesMat1.dot(onesMat2);

std::cout<

elemSize

方法,返回图像(矩阵)像素(元素)大小 (以字节为单位),因为我们这里读入的 PNG 格式,有 4 个通道,每个通道都是 uchar 类型的,所以是返回 4 个字节。

int elementSize = img.elemSize();

std::cout<

elemSize1

方法,以字节为单位返回每个矩阵元素通道的大小,结果也就是上面的 elemSize 方法得到的除以通道数。

int elementSize1 = img.elemSize1();

std::cout<

empty

方法,如果是个空矩阵,则返回 true。

bool isEmpty = img.empty();

std::cout<

eye

返回单位矩阵,跟 Matlab 类似,先是行数,然后列数,最后指定元素类型

cv::Mat eyeMat = cv::Mat::eye(3,4,CV_64F);

std::cout<

isContinuous

方法,返回矩阵是否连续

bool isContinuous = img.isContinuous();

std::cout<

ones

方法,产生全一矩阵,跟matlab的ones类似,先是行数,然后是列数,就是必须要显式地指定数据元素(像素)类型

cv::Mat onesMat = cv::Mat::ones(4, 3, CV_64F);

std::cout<

ptr

函数,为了简化指针运算,ptr 函数可以得到图像给定行的首地址。ptr 函数是一个模板函数,它返回第 j 行的首地址:

uchar *ptr = img.ptr(0);

std::cout<

std::cout<

reshape

方法,返回一个改变了形状的矩阵,但要注意的是,跟 Matlab 不同,OpenCV 中的 reshape 的第一个参数是 通道数,第二个参数是 行数,且只有这两个参数,如果想维持通道数不变,那对应位置填 0 即可。

cv::Mat reshapeMat1 = cv::Mat::ones(3,4,CV_64F);

cv::Mat reshapeMat2 = reshapeMat1.reshape(0, 2);

std::cout<

std::cout<

row

方法,返回矩阵特定的某一行(行号从 0 开始)

cv::Mat rowMat = img.row(0);

std::cout<

rowRange

方法,取规定的行,返回一个子矩阵

int imgRows = img.rows;

std::cout<

setTo

方法,将矩阵元素都设置为某个值

cv::Mat setMat = cv::Mat::ones(3,4,CV_64F);

setMat.setTo(cv::Scalar(0));

std::cout<

setMat.row(0).setTo(2);

std::cout<

size

方法,返回一个 cv::Size 对象,使用情形如下:

cv::Size imgSize = img.size();

int imgHeight = imgSize.height;

int imgWidth = imgSize.width;

std::cout<

std::cout<

std::cout<

需要注意的是,它只能返回2维的尺寸,还有就是它先返回的是列数,然后才是行数,行数列数分别可以用 height 和 width 两个成员变量得到。

t

函数,返回当前对象的转置矩阵

cv::Mat tMat1 = cv::Mat::ones(3,4,CV_64F);

tMat1.at(1,2) = 0.0;

cv::Mat tMat2 = tMat1.t();

std::cout<

std::cout<

total

方法,该方法返回数组元素(如果该数组表示图像的像素数)的数目,注意这个跟 Matlab 里面的 numel 可不同,Matlab 是将对象当作是矩阵(张量)来处理,只不过图像刚好可以用矩阵和张量来表示,本质上Matlab还是一个通用的数学软件,而 OpenCV 是讲对象当作图像来看待,是专门为图像而设计的,所以 即使这个图像有 RGB 3 个通道,total 返回的也只是 height * width,不过这也对,这就是像素数。

int imgPixels = img.total();

std::cout<

type

方法,返回图像像素的数据类型

int imgType = img.type();

std::cout<

zeros

方法,产生全零矩阵,跟 Matlab 的 zeros 类似,先是行数,然后是列数,就是必须要显式地指定数据元素(像素)类型

cv::Mat zerosMat = cv::Mat::zeros(4, 3, CV_64F);

std::cout<

3. 内存管理相关

有关引用计数的一些函数,作为普通用户,我们并不需要了解。

addref

该方法递增与矩阵数据关联的引用计数,通常情况下,为避免内存泄漏,不应显式调用该方法。

release

在必要的情况下,递减引用计数并释放该矩阵。

~Mat

4. 待日后补充

下面是一些我目前还没有用到,或者在 Reference Manual 里面搜不到相关介绍的,留待日后补充:

adjustROI

AUTO_STEP

copySize

checkVector

CONTINUOUS_FLAG

SUBMATRIX_FLAG

allocate

assignTo

step1

deallocate

datastart

datalimit

dataend

flags

initEmpty

isSubmatrix

locateROI

MAGIC_VAL

operator cv::Matx<_tp m n>

operator cv::Vec<_tp n>

operator_CvMat

operator CvMatND

operator IplImage

operator std::vector<_tp std::allocator><_tp>>

operator()

operator=

push_back_

pop_back

refcount

reserve

mul

inv

begin

end

集合终止位置的迭代器,但是end 方法得到的迭代器其实已经超出了集合。这也意味着迭代过程必须在迭代器到达这个位置时结束

push_back

方法,如果对矩阵做,是添加行的。

还有一些函数,根据 Reference Manual 的应该是采用 cv::resize() 这样的方式调用,比如

resize

最后,上述代码整体如下:

#include

#include

int main(int argc, char **argv)

{

std::string imgPathStr("D:\\openCV_build\\doc\\opencv-logo2.png");

cv::Mat img = cv::imread(imgPathStr,-1); // Read image

//cv::imshow("img",img);

//cv::waitKey();

std::vector<:mat> rgbImg;

cv::split(img,rgbImg);

cv::Mat rgbImg_R = rgbImg[2];

cv::Mat rgbImg_G = rgbImg[1];

cv::Mat rgbImg_B = rgbImg[0];

// Attributes

int imgRows = img.rows;

std::cout<

int imgCols = img.cols;

std::cout<

// Methods

cv::Mat rowRangeImg = img.rowRange(imgRows / 2, imgRows);

//cv::imshow("rowRangeImg",rowRangeImg);

//cv::waitKey();

cv::Mat colRangeImg = img.colRange(imgCols / 2, imgCols);

//cv::imshow("colRangeImg",colRangeImg);

//cv::waitKey();

cv::Size imgSize = img.size();

int imgHeight = imgSize.height;

int imgWidth = imgSize.width;

std::cout<

std::cout<

std::cout<

int imgChannels = img.channels();

std::cout<

int imgType = img.type();

std::cout<

int imgPixels = img.total();

std::cout<

bool isContinuous = img.isContinuous();

std::cout<

int elementSize = img.elemSize();

std::cout<

int elementSize1 = img.elemSize1();

std::cout<

int imgDepth = img.depth();

std::cout<

bool isEmpty = img.empty();

std::cout<

int imgDims = img.dims;

std::cout<

cv::Mat onesMat = cv::Mat::ones(4, 3, CV_64F);

std::cout<

cv::Mat zerosMat = cv::Mat::zeros(4, 3, CV_64F);

std::cout<

int imgStep = img.step;

std::cout<

cv::Mat doubleImg;

img.convertTo(doubleImg, CV_64FC4);

std::cout<

//cv::imshow("doubleImg", doubleImg);

//cv::waitKey();

cv::Mat createMat = cv::Mat::ones(3,4,CV_64F);

createMat.create(5,6, CV_8UC(2));

//std::cout<

cv::Mat copyMat1 = cv::Mat::ones(3,4,CV_64F);

cv::Mat copyMat2;

copyMat1.copyTo(copyMat2);

copyMat1.at(0,0) = 0.0;

//std::cout<

//std::cout<

cv::Mat cloneMat1 = cv::Mat::ones(3,4,CV_64F);

cv::Mat cloneMat2 = cloneMat1.clone();

cloneMat1.at(0,0) = 0.0;

//std::cout<

//std::cout<

cv::Mat eyeMat = cv::Mat::eye(3,4,CV_64F);

std::cout<

cv::Mat diagMat = img.diag();

//std::cout<

std::cout<

std::cout<

cv::Mat rowMat = img.row(0);

std::cout<

cv::Mat colMat = img.col(0);

std::cout<

cv::Mat onesMat1 = cv::Mat::ones(3,4,CV_64F);

cv::Mat onesMat2 = cv::Mat::ones(3,4,CV_64F);

double dotVal = onesMat1.dot(onesMat2);

std::cout<

cv::Mat crossVec1 = cv::Mat::ones(1,3,CV_64F);

cv::Mat crossVec2 = cv::Mat::ones(1,3,CV_64F);

cv::Mat crossMat = crossVec1.cross(crossVec2);

std::cout<

cv::Mat tMat1 = cv::Mat::ones(3,4,CV_64F);

tMat1.at(1,2) = 0.0;

cv::Mat tMat2 = tMat1.t();

std::cout<

std::cout<

cv::Mat reshapeMat1 = cv::Mat::ones(3,4,CV_64F);

cv::Mat reshapeMat2 = reshapeMat1.reshape(0, 2);

std::cout<

std::cout<

cv::Mat setMat = cv::Mat::ones(3,4,CV_64F);

setMat.setTo(cv::Scalar(0));

std::cout<

setMat.row(0).setTo(2);

std::cout<

uchar *data = img.data;

std::cout<

std::cout<

uchar *ptr = img.ptr(0);

std::cout<

std::cout<

cv::Vec3b vec3b = img.at<:vec3b>(0,0);

uchar vec3b0 = img.at<:vec3b>(0,0)[0];

uchar vec3b1 = img.at<:vec3b>(0,0)[1];

uchar vec3b2 = img.at<:vec3b>(0,0)[2];

std::cout<

std::cout<

std::cout<

std::cout<

std::system("PAUSE");

return 0;

}

初写于 2015-04-28,未完待续。

参考资料:

《OpenCV 2 计算机视觉编程手册》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值