opencvmat访问
Ⅰ opencv怎么给mat赋值
在OpenCV中有三种方式访问矩阵中的数据元素:容易的方式,困难的方式,以及正确的方式。今天主要讲容易方式:
最容易的方式是使用宏CV_MAT_ELEM( matrix, elemtype, row, col ),输入参数是矩阵,不是指针,网上有很多人说是指针,矩阵元素类型,行,列,返回值是相应行,列的矩阵元素。CV_MAT_ELEM可以给矩阵赋值,也可以访问矩阵元素。
CV_MAT_ELEM宏实际上会调用CV_MAT_ELEM_PTR(matrix,row,col)宏来完成任务。 CV_MAT_ELEM_PTR()宏的参数是矩阵,行,列。CV_MAT_ELEM()宏和CV_MAT_ELEM_PTR()宏的区别是,在调用CV_MAT_ELEM时,指向矩阵元素的指针的数据类型已经依据输入参数中的元素类型而做了强制转换:
如下程序:
CvMat* mat = cvCreateMat(3,3,CV_32FC1);//创建矩阵
cvZero(mat);//将矩阵置0
//为矩阵元素赋值
CV_MAT_ELEM( *mat, float, 0, 0 ) = 1.f; CV_MAT_ELEM( *mat, float, 0, 1 ) = 2.f;
CV_MAT_ELEM( *mat, float, 0, 2 ) = 3.f;
CV_MAT_ELEM( *mat, float, 1, 0 ) = 4.f;
CV_MAT_ELEM( *mat, float, 1, 1 ) = 5.f;
CV_MAT_ELEM( *mat, float, 1, 2 ) = 6.f;
CV_MAT_ELEM( *mat, float, 2, 0 ) = 7.f;
CV_MAT_ELEM( *mat, float, 2, 1 ) = 8.f;
CV_MAT_ELEM( *mat, float, 2, 2 ) = 9.f;
//获得矩阵元素的值
float element = CV_MAT_ELEM(*mat,float,2,2);
float element_1_1 = 7.7f;
*((float*)CV_MAT_ELEM_PTR(m, 1, 1)) = element_1_1;
float element = CV_MAT_ELEM(m,float, 1,1 );
cout<<element<<endl;
以上使用矩阵中元素的方式很方便,但不幸的是,该宏在每次调用时,都会重新计算指针的位置。这意味着,先查找矩阵数据区中第0个元素的位置,然后,根据参数中的行和列,计算所需要的元素的地址偏移量,然后将地址偏移量与第0个元素的地址相加,获得所需要的元素的地址。
所以,以上的方式虽然很容易使用,但是却不是获得矩阵元素的最好方式。特别是当你要顺序遍历整个矩阵中所有元素时,这种每次对地址的重复计算就更加显得不合理。
Ⅱ opencv 中 Mat 数据结构的用法
#include "stdafx.h"
#include <string>
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int _tmain(int argc, _TCHAR* argv[])
{
//创建一个用1+3j填充的 7 x 7 复矩阵-----1
Mat M(7, 7, CV_32FC2, Scalar(1,3));
//现在将 M转换为100 x 60的CV_8UC(15)的矩阵,旧内容将会被释放
M.create(100, 60, CV_8UC(15));//不能为矩阵设置初值
//第 5行,乘以 3,加到第 3 行,
M.row(3) = M.row(3) + M.row (5) * 3;
//现在将第7列复制到第1列, M.col(1) = M.col(7);//这个不能实现,对列操作时要新建一个Mat
Mat M1 = M.col(1);
M.col(7).To(M1);
//创建一种新的 320 x 240 图像-----2
Mat img(Size(320,240), CV_8UC3, Scalar::all(255));
string strWindowName = "ShowImage";
namedWindow(strWindowName, WINDOW_AUTOSIZE);
imshow(strWindowName, img);
waitKey(0);
//选择ROI(region of interest)
Mat roi(img, Rect(10, 10, 100, 100));
//填充 (0,255,0) 的ROI (这是RGB 空间中的绿色),320 x 240 原始图像将被修改
roi = Scalar(0, 255, 0) ;
imshow(strWindowName, img);
waitKey(0);
//获取数组中的子块-----3
Mat A = Mat::eye(10, 10, CV_32S);
//提取 A 的1 (含)到 3 (不包含)列
Mat B = A(Range::all(), Range(1, 3));
//提取 B 的5 (含)到 9 (不包含)行,即 C ~ A(Range(5,9),Range (1,3))
Mat C = B(Range(5, 9), Range::all());
Size size;
Point ofs;
C.locateROI(size, ofs);//使用locateROI() 计算子数组在主容器数组中的相对的位置
cout<<size.width<<" "<<size.height<<" "<<ofs.x<<" "<<ofs.y<<endl;
//快速初始化小矩阵-----4
double m[3][3] = {{1, 2, 3}, {1, 2, 5}, {3, 4, 6}};
Mat M2 = Mat(3, 3, CV_64F, m);//.inv();
Mat E = Mat::eye(4, 4, CV_64F);
cout<<"E = "<<endl<<" "<<E<<endl;
Mat O = Mat::ones(2, 2, CV_32F);
cout<<"O = "<<endl<<" "<<O<<endl;
Mat Z = Mat::zeros(3,3, CV_8UC1);
cout<<"Z = "<<endl<<" "<<Z<<endl;
//IplImage、Mat、CvMat互转-----5
IplImage *img1 = cvLoadImage("aa.jpg", 2 | 4);
Mat mtx(img1);//IplImage *-> Mat,新的Mat类型与原来的IplImage类型共享图像数据,转换只是创建一个Mat矩阵头// or : Mat mtx = img1;
CvMat oldmat = mtx;//Mat-> CvMat //只是创建矩阵头,而没有复制数据,oldmat不用手动释放
CV_Assert((oldmat.cols == img1->width) && (oldmat.rows == img1->height) && (oldmat.data.ptr == (uchar *)img1->imageData) && (oldmat.step == img1->widthStep));
imshow(strWindowName, mtx);
waitKey(0);
cvNamedWindow(strWindowName.c_str(), 0);
cvShowImage(strWindowName.c_str(), &oldmat);
cvWaitKey(0);
IplImage img2 = mtx;//Mat->IplImage //只是创建图像头,而没有复制数据,img2不用手动释放
cvShowImage(strWindowName.c_str(), &img2);
cvWaitKey(0);
Mat mat3(&oldmat);//CvMat->Mat
imshow(strWindowName, mat3);
waitKey(0);
cvDestroyWindow(strWindowName.c_str());
cvReleaseImage(&img1);
//创建 3 x 3 双精度恒等矩阵-----6
Mat M3 = (Mat_ <double>(3,3) <<1,0,0, 0,1,0, 0,0,1);
//访问数组元素-----7
M2.at<double>(0, 0) += 10.f;
double sum = 0;//计算元素和,方法一
for (int i=0; i<M2.rows; i++)
{
const double *Mi = M2.ptr<double>(i) ;
for (int j=0; j<M2.cols; j++)
{
sum += std::max(Mi[j], 0.);
}
}
cout<<sum<<endl;
sum = 0;//计算元素和,方法二
int cols =M2.cols, rows = M2.rows ;
if (M2.isContinuous())
{
cols *= rows;
rows = 1 ;
}
for (int i=0; i<rows; i++)
{
const double *Mi = M2.ptr <double>(i);
for (int j=0; j<cols; j++)
{
sum += std::max(Mi[j], 0.);
}
}
cout<<sum<<endl;
sum = 0;//计算元素和,方法三
MatConstIterator_<double> it = M2.begin<double>(), it_end = M2.end<double>();
for(; it != it_end; ++it)
{
sum += std::max(*it, 0.);
}
cout<<sum<<endl;
return 0;
}
Ⅲ OpenCV (一)Mat基本操作以及灰度图转化
开始写OpenCV这篇文章的时候,不由想到,我的大学计算机图形学的第一门实操课程就是灰度转化,拉普拉斯锐化等。其中灰度图的转化,是计算机图形学基础中基础,这里就顺着OpenCV的灰度的转化,来看看OpenCV一些基础的api。
本文地址: https://www.jianshu.com/p/7963c7dbaf92
先来看看OpenCV,基础对象Mat,矩阵。什么是矩阵,实际上没有必要解释,一般人都能够明白数学意义上矩阵的含义。
OpenCV把每一个M * N的宽高图像,看成M*N的矩阵。矩阵的每一个单元就对应着图像中像素的每一个点。
我们如果放大图中某个部分,就会发现如下情况
图像实际上就如同矩阵一样每个单元由一个像素点构成。
因为OpenCV的Mat每一个像素点,包含的数据不仅仅只有一个单纯的数字。每一个像素点中包含着颜色通道数据。
稍微解释一下颜色通道,我们可以把世间万物肉眼能识别的颜色由3种颜色(R 红色,G 绿色,B 蓝色)经过调节其色彩饱和度组成的。也就是说通过控制RGB三种的色值大小(0~255)来调配新的颜色。
当我们常见的灰度图,一般是单个颜色通道,因为只用黑白两种颜色。我们常见的图片,至少是三色通道,因为需要RGB三种颜色通道。
我们常见Android的Bitmap能够设置ARGB_8888的标志位就是指能够通过A(透明通道),R,G,B来控制图片加载的颜色通道。
OpenCV为了更好的控制这些数据。因此采用了数学上的矩阵的概念。当OpenCV要控制如RGB三色通道的Mat,本质上是一个M * N * 3的三维矩阵。
但是实际上,我们在使用OpenCV的Mat的时候,我们只需要关注每个图片的像素,而每个像素的颜色通道则是看成Mat中每个单元数据的内容即可
我们先来看看Mat的构造方法
现阶段,实际上我们值得我们注意的是构造函数:
举个例子:
这个mat矩阵将会制造一个高20,宽30,一个1字节的颜色通道(也是Mat中每一个像素数据都是1字节的unchar类型的数据),同时颜色是白色的图片。
在这里面我们能够看到一个特殊的宏CV_8UC1。实际上这是指代OpenCV中图片带的是多少颜色通道的意思。
这4个宏十分重要,要时刻记住。
当我们需要把Mat 中的数据拷贝一份出来,我们应该调用下面这个api:
这样就能拷贝一份像素数据到新的Mat中。之后操作新的Mat就不会影响原图。
实际上,在本文中,我们能够看到OpenCV是这么调用api读取图片的数据转化为Mat矩阵。
OpenCV会通过imread去读图片文件,并且转化为Mat矩阵。
能看见imread,是调用imread_把图片中的数据拷贝的img这个Mat对象中。接着会做一次图片的颠倒。这个方面倒是和Glide很相似。
文件:moles/imgcodecs/src/loadsave.cpp
这里面做了几个事情,实际上和FFmpge的设计十分相似。
其核心也是操作Mat中的像素指针,找到颜色通道,确定指针移动的步长,赋值图片的数据到Mat矩阵中。核心如下:
其中还涉及到jpeg的哈夫曼算法之类的东西,这里就不深入源码。毕竟这是基础学习。
什么是灰度图,灰度度图实际上我们经常见到那些灰白的也可以纳入灰度图的范畴。实际上在计算机图形学有这么一个公式:
将RGB的多颜色图,通过 的算法,将每一个像素的图像的三颜色通道全部转化为为一种色彩,通过上面的公式转为为一种灰色的颜色。
一旦了解了,我们可以尝试编写灰度图的转化。我们通过矩阵的at方法访问每一个像素中的数据。
为了形象表示矩阵指针,指向问题,可以把RGB在OpenCV的Mat看成如下分布:
记住OpenCV的RGB的顺序和Android的不一样,是BGRA的顺序。和我们Android开发颠倒过来。
因此,我们可以得到如下的例子
我们经过尝试之后,确实能够把一个彩色的图片转化一个灰色图片。但是这就是
这里介绍一下Mat的一个api:
实际上OpenCV,内置了一些操作,可以把RGB的图像数据转化灰度图。
我们看看OpenCV实际上的转化出来的灰度图大小。我们通过自己写的方法,转化出来的灰度图是119kb,而通过cvtColor转化出来的是44kb。
问题出在哪里?还记得吗?因为只有灰白两种颜色,实际上只需要一种颜色通道即可,而这边还保留了3个颜色通道,也就说图片的每一个像素点中的数据出现了没必要的冗余。
这样就是44kb的大小。把三颜色通道的数据都设置到单颜色通道之后,就能进一步缩小其大小。
实际上在Android中的ColorMatrix中也有灰度图转化的api。
对画笔矩阵进行一次,矩阵变化操作。
实际上就是做了一次矩阵运算。绘制灰度的时候相当于构建了这么一个矩阵
接着通过矩阵之间的相乘,每一行的 0.213f,0.715f,0.072f控制像素的每个通道的色值。
对于Java来说,灰度转化的算法是: ,把绿色通道的比例调大了。
在OpenCV中有这么两个API,add和addWidget。两者都是可以把图像混合起来。
add和addWidget都是将像素合并起来。但是由于是像素直接相加的,所以容易造成像素接近255,让整个像素泛白。
而权重addWeighted,稍微能减轻一点这种问题,本质上还是像素相加,因此打水印一般不是使用这种办法。
等价于
saturate_cast这个是为了保证计算的值在0~255之间,防止越界。
饱和度,图片中色值更加大,如红色,淡红,鲜红
对比度:是指图像灰度反差。相当于图像中最暗和最亮的对比
亮度:暗亮度
控制对比度,饱和度的公式: , ,
因此当我们想要控制三通道的饱和度时候,可以通过alpha来控制色值成比例增加,beta控制一个色值线性增加。
如下:
在这里,看到了OpenCV会把所有的图片看成Mat矩阵。从本文中,能看到Mat的像素操作可以能看到有两种,一种是ptr像素指针,一种是at。ptr是OpenCV推荐的更加效率的访问速度。
当然还有一种LUT的核心函数,用来极速访问Mat矩阵中的像素。其原理是对着原来的色值进行预先的变换对应(设置一个颜色通道)。用来应对设置阈值等情况。