其中,对Mat中元素的访问则主要是参考点击打开链接
之前我也是开始接触的是IplImage,后来接触的是CvMat,到最后慢慢的发现,Mat才是比较好用的,所以这次自己想仔细的再学习一次Mat。
对Mat的操作:
1.初始化,只拷贝信息头,或者是拷贝原始Mat中的感兴趣区域
一地址而实现。而拷贝构造函数则 只拷贝信息头和矩阵指针 ,而不拷贝矩阵。
|
|
以上代码中的所有Mat对象最终都指向同一个也是唯一一个数据矩阵。虽然它们的信息头不同,但通过任何一个对象所做的改变也会影响其它对象。实际上,不同的对象只是访问相同数据的不同途径而已。这里还要提及一个比较棒的功能:你可以创建只引用部分数据的信息头。比如想要创建一个感兴趣区域( ROI ),你只需要创建包含边界信息的信息头:
|
|
现在你也许会问,如果矩阵属于多个 Mat 对象,那么当不再需要它时谁来负责清理?简单的回答是:最后一个使用它的对象。通过引用计数机制来实现。无论什么时候有人拷贝了一个 Mat 对象的信息头,都会增加矩阵的引用次数;反之当一个头被释放之后,这个计数被减一;当计数值为零,矩阵会被清理。但某些时候你仍会想拷贝矩阵本身(不只是信息头和矩阵指针),这时可以使用函数 clone() 或者copyTo() 。
|
|
你可以通过 Mat 的运算符 << 来实现,但要记住这只对二维矩阵有效。
Mat 不但是一个很赞的图像容器类,它同时也是一个通用的矩阵类,所以可以用来创建和操作多维矩阵。创建一个Mat对象有多种方法:
-
Mat() 构造函数
比如 CV_8UC3 表示使用8位的 unsigned char 型,每个像素由三个元素组成三通道。预先定义的通道数可以多达四个。 Scalar是个short型vector。指定这个能够使用指定的定制化值来初始化矩阵。当然,如果你需要更多通道数,你可以使用大写的宏并把通道数放在小括号中,如下所示
-
在 C\C++ 中通过构造函数进行初始化
上面的例子演示了如何创建一个超过两维的矩阵:指定维数,然后传递一个指向一个数组的指针,这个数组包含每个维度的尺寸;其余的相同
-
为已存在IplImage指针创建信息头:
-
Create() function: 函数
![]()
这个创建方法不能为矩阵设初值,它只是在改变尺寸时重新为矩阵数据开辟内存。
-
对于小矩阵你可以用逗号分隔的初始化函数:
从上面的例子中可以看到默认格式,除此之外,OpenCV还支持以下的输出习惯
-
默认方式
-
Python
-
以逗号分隔的数值 (CSV)
-
Numpy
-
C语言
打印其它常用项目
OpenCV支持使用运算符<<来打印其它常用OpenCV数据结构。
-
2维点
-
3维点
-
基于cv::Mat的std::vector
-
std::vector点
/***************************************************************
*
* 内容摘要:本例采用8种方法对图像Mat的像素进行扫描,并对像素点的像
* 素进行压缩,压缩间隔为div=64,并比较扫描及压缩的效率,效
* 率最高的是采用.ptr及减少循环次数来遍历图像,并采用位操
* 作来对图像像素进行压缩。
* 作 者:Jacky Liu
* 完成日期:2012.8.10
* 参考资料:《OpenCV 2 computer Vision Application Programming
* cookbook》
*
***************************************************************/
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
//利用.ptr和数组下标进行图像像素遍历
void colorReduce0(cv::Mat &image, int div = 64)
{
int nl = image.rows;
int nc = image.cols * image.channels();
//遍历图像的每个像素
for(int j=0; j<nl ;++j)
{
uchar *data = image.ptr<uchar>(j);
for(int i=0; i<nc; ++i)
{
data[i] = data[i]/div*div+div/2; //减少图像中颜色总数的关键算法:if div = 64, then the total number of colors is 4x4x4;整数除法时,是向下取整。
}
}
}
//利用.ptr和 *++ 进行图像像素遍历
void colorReduce1(cv::Mat &image, int div = 64)
{
int nl = image.rows;
int nc = image.cols * image.channels();
//遍历图像的每个像素
for(int j=0; j<nl ;++j)
{
uchar *data = image.ptr<uchar>(j);
for(int i=0; i<nc; ++i)
{
*data++ = *data/div*div + div/2;
}
}
}
//利用.ptr和数组下标进行图像像素遍历,取模运算用于减少图像颜色总数
void colorReduce2(cv::Mat &image, int div = 64)
{
int nl = image.rows;
int nc = image.cols * image.channels();
//遍历图像的每个像素
for(int j=0; j<nl ;++j)
{
uchar *data = image.ptr<uchar>(j);
for(int i=0; i<nc; ++i)
{
data[i] = data[i]-data[i]%div +div/2; //利用取模运算,速度变慢,因为要读每个像素两次
}
}
}
//利用.ptr和数组下标进行图像像素遍历,位操作运算用于减少图像颜色总数
void colorReduce3(cv::Mat &image, int div = 64)
{
int nl = image.rows;
int nc = image.cols * image.channels();
int n = static_cast<int>(log(static_cast<double>(div))/log(2.0)); //div=64, n=6
uchar mask = 0xFF<<n; //e.g. div=64, mask=0xC0
//遍历图像的每个像素
for(int j=0; j<nl ;++j)
{
uchar *data = image.ptr<uchar>(j);
for(int i=0; i<nc; ++i)
{
*data++ = *data&mask + div/2;
}
}
}
//形参传入const conference,故输入图像不会被修改;利用.ptr和数组下标进行图像像素遍历
void colorReduce4(const cv::Mat &image, cv::Mat &result,int div = 64)
{
int nl = image.rows;
int nc = image.cols * image.channels();
result.create(image.rows,image.cols,image.type());
//遍历图像的每个像素
for(int j=0; j<nl ;++j)
{
const uchar *data_in = image.ptr<uchar>(j);
uchar *data_out = result.ptr<uchar>(j);
for(int i=0; i<nc; ++i)
{
data_out[i] = data_in[i]/div*div+div/2; //减少图像中颜色总数的关键算法:if div = 64, then the total number of colors is 4x4x4;整数除法时,是向下取整。
}
}
}
//利用.ptr和数组下标进行图像像素遍历,并将nc放入for循环中(比较糟糕的做法)
void colorReduce5(cv::Mat &image, int div = 64)
{
int nl = image.rows;
//遍历图像的每个像素
for(int j=0; j<nl ;++j)
{
uchar *data = image.ptr<uchar>(j);
for(int i=0; i<image.cols * image.channels(); ++i)
{
data[i] = data[i]/div*div+div/2; //减少图像中颜色总数的关键算法:if div = 64, then the total number of colors is 4x4x4;整数除法时,是向下取整。
}
}
}
//利用迭代器 cv::Mat iterator 进行图像像素遍历
void colorReduce6(cv::Mat &image, int div = 64)
{
cv::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; //利用operator[]处理每个通道的像素
(*it)[1] = (*it)[1]/div*div+div/2;
(*it)[2] = (*it)[2]/div*div+div/2;
}
}
//利用.at<cv::Vec3b>(j,i)进行图像像素遍历
void colorReduce7(cv::Mat &image, int div = 64)
{
int nl = image.rows;
int nc = image.cols;
//遍历图像的每个像素
for(int j=0; j<nl ;++j)
{
for(int i=0; i<nc; ++i)
{
image.at<cv::Vec3b>(j,i)[0] = image.at<cv::Vec3b>(j,i)[0]/div*div + div/2;
image.at<cv::Vec3b>(j,i)[1] = image.at<cv::Vec3b>(j,i)[1]/div*div + div/2;
image.at<cv::Vec3b>(j,i)[2] = image.at<cv::Vec3b>(j,i)[2]/div*div + div/2;
}
}
}
//减少循环次数,进行图像像素遍历,调用函数较少,效率最高。
void colorReduce8(cv::Mat &image, int div = 64)
{
int nl = image.rows;
int nc = image.cols;
//判断是否是连续图像,即是否有像素填充
if(image.isContinuous())
{
nc = nc*nl;
nl = 1;
}
int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));
uchar mask = 0xFF<<n;
//遍历图像的每个像素
for(int j=0; j<nl ;++j)
{
uchar *data = image.ptr<uchar>(j);
for(int i=0; i<nc; ++i)
{
*data++ = *data & mask +div/2;
*data++ = *data & mask +div/2;
*data++ = *data & mask +div/2;
}
}
}
const int NumTests = 9; //测试算法的数量
const int NumIteration = 20; //迭代次数
int main(int argc, char* argv[])
{
int64 t[NumTests],tinit;
cv::Mat image1;
cv::Mat image2;
//数组初始化
int i=0;
while(i<NumTests)
{
t[i++] = 0;
}
int n = NumIteration;
//迭代n次,取平均数
for(int i=0; i<n; ++i)
{
image1 = cv::imread("../boldt.jpg");
if(!image1.data)
{
std::cout<<"read image failue!"<<std::endl;
return -1;
}
// using .ptr and []
tinit = cv::getTickCount();
colorReduce0(image1);
t[0] += cv::getTickCount() - tinit;
// using .ptr and *++
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce1(image1);
t[1] += cv::getTickCount() - tinit;
// using .ptr and [] and modulo
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce2(image1);
t[2] += cv::getTickCount() - tinit;
// using .ptr and *++ and bitwise
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce3(image1);
t[3] += cv::getTickCount() - tinit;
//using input and output image
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce4(image1,image2);
t[4] += cv::getTickCount() - tinit;
// using .ptr and [] with image.cols * image.channels()
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce5(image1);
t[5] += cv::getTickCount() - tinit;
// using .ptr and *++ and iterator
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce6(image1);
t[6] += cv::getTickCount() - tinit;
//using at
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce7(image1);
t[7] += cv::getTickCount() - tinit;
//using .ptr and * ++ and bitwise (continuous+channels)
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce8(image1);
t[8] += cv::getTickCount() - tinit;
}
cv::namedWindow("Result");
cv::imshow("Result",image1);
cv::namedWindow("Result Image");
cv::imshow("Result Image",image2);
std::cout<<std::endl<<"-------------------------------------------------------------------------"<<std::endl<<std::endl;
std::cout<<"using .ptr and [] = "<<1000*t[0]/cv::getTickFrequency()/n<<"ms"<<std::endl;
std::cout<<"using .ptr and *++ = "<<1000*t[1]/cv::getTickFrequency()/n<<"ms"<<std::endl;
std::cout<<"using .ptr and [] and modulo = "<<1000*t[2]/cv::getTickFrequency()/n<<"ms"<<std::endl;
std::cout<<"using .ptr and *++ and bitwise = "<<1000*t[3]/cv::getTickFrequency()/n<<"ms"<<std::endl;
std::cout<<"using input and output image = "<<1000*t[4]/cv::getTickFrequency()/n<<"ms"<<std::endl;
std::cout<<"using .ptr and [] with image.cols * image.channels() = "<<1000*t[5]/cv::getTickFrequency()/n<<"ms"<<std::endl;
std::cout<<"using .ptr and *++ and iterator = "<<1000*t[6]/cv::getTickFrequency()/n<<"ms"<<std::endl;
std::cout<<"using at = "<<1000*t[7]/cv::getTickFrequency()/n<<"ms"<<std::endl;
std::cout<<"using .ptr and * ++ and bitwise (continuous+channels) = "<<1000*t[8]/cv::getTickFrequency()/n<<"ms"<<std::endl;
std::cout<<std::endl<<"-------------------------------------------------------------------------"<<std::endl<<std::endl;
cv::waitKey();
return 0;
}