一. 基本数据类型
Mat
一幅图像被保存为一个头加上包含像素数据的内存区
图像有若干通道,灰度图有一个通道,彩色图像通常有红、绿、蓝三个构成成分(OpenCV
以蓝、绿、红来存储这三个分量
)
此外还有第四个通道,及透明度(alpha)通道
用img.channels()来获取一幅img图像的通道数
每个像素使用若干位来存储,称之为图像深度(image depth)
对于灰度图像,像素通常存储为8位,因此允许256个(0~255)个灰度级
对于彩色图像,每个像素存储为3个字节,每个颜色通道占一个字节
某些操作必须以浮点格式存储像素
可以使用img.depth()来获取图像深度,返回值是:
使用convertTo( )可以将一种图像深度转换成另外一种图像深度
Mat img=imread("cumt.png",IMREAD_GRAYSCALE);
Mat fp;
img.covertTo(fp,CV_32F);
二. 像素级访问
方法:
at<>
|
必须指定矩阵单元的类型
|
//As for gray image
Matsrc1
= imread("lena.jpg",IMREAD_GRAYSCALE);
ucharpixel1=
src1.at<uchar>(0, 0);
cout<<"Value
of pixell(0,0) is:"<<(unsignedint)pixel1<<endl;
//As for colorful image
Matsrc2
= imread("lena.jpg",IMREAD_COLOR);
Vec3bpixel2
= src2.at<Vec3b>(0, 0);
cout<<"B
component of pixel (0,0) is:"<<(unsignedint)pixel2[0]<<endl;
//将浮点矩阵初始化为PI
MatM(200, 200,CV_64F);
for(int
i = 0; i < M.rows; i++)
{
for(intj
= 0; j < M.cols; j++)
{
M.at<double>(i,
j) =CV_PI;
}
}
| 简单但是费时 |
ptr函数
|
返回指向图像特定行的指针
|
//To attain every pixel's
R, G, B
ucharR,
G, B;
for(inti=0;i<src2.rows;i++)
{
Vec3b*
pixrow = src2.ptr<Vec3b>(i);
for(intj
= 0;j < src2.cols;j++)
{
B = pixrow[j][0];
G = pixrow[j][1];
R = pixrow[j][2];
}
}
|
测量时间
getTickCount()
getTickFrequencY()
//Time measuring
doublet0
= (double)getTickCount();
doubleelapsed
= ((double)getTickCount() - t0) / getTickFrequency();//单位为秒
常用操作
操作
|
代码示例
|
设置矩阵的值 |
img.setTo(0);//对于一个通道的图像
img.setTo(Scalar(B,
G, R));//对于三通道的图像
|
MATLAB风格的矩阵初始化 |
Matm1
=Mat::eye(m, n,CV_64F);//m*n阶单位矩阵
Matm2
=Mat::zeros(m, n,CV_8UC1);//m*n阶全0矩阵
Matm3
=Mat::ones(m, n,CV_68UC1)*255;//m*n阶全1矩阵
|
随机初始化 |
Matm4
=Mat(100, 100,CV_8UC1);
randu(m4, 0, 255);
|
创建矩阵的一个副本 |
Matimg1
= img.clone();
|
创建一个(具有掩码)矩阵的副本 |
img.copy(img1, mask);
|
引用一个子矩阵(不复制数据) |
Matimg2
= img(Range(start1,
end1),Range(start2, end2));
|
图像裁剪 |
Rectroi(r1,
c2, width, height);
Matimg5
= img(roi).clone();//数据拷贝
|
调整图像大小 |
resize(img, img1,Size(),
0.5, 0.5);//将图像变为原来的1/2
|
翻转图像 |
flip(imgsrc, imgdst, code);
//code=0 =>垂直翻转
//code>0 =>水平翻转
//code<0 =>垂直和水平翻转
|
分割通道 |
Matchannel[3];
split(img, channel);
imshow("B",
channel[0]);//显示蓝色
|
合并通道 |
merge(channel, img);
|
计算非零像素数 |
intnz
= countNonZero(img);
|
最小值和最大值 |
doublem,
M;
PointmLoc,
MLoc;
minMaxLoc(img, &m, &M, &mLoc, &MLoc);
|
像素值均值 |
Scalarm,
stdd;
meanStdDev(img, m, stdd);
uintmean_pxl
= mean.val[0];
|
检查图像数据是否为空 |
if(img.empty())
cout<<"Could
not load image.";
|
算数运算
位运算
bitwise_and();
bitwise_not();
bitwise_or();
bitwise_xor();
#include<opencv2/opencv.hpp>
#include<iostream>
usingnamespacestd;
usingnamespacecv;
intmain()
{
/*
*用bitwise_and()进行图像的剪裁
*
*/
Matimg1 = imread("lena.png",IMREAD_GRAYSCALE);
if(img1.empty())
{
cout<<"Error, cannot load image!"<<endl;
}
imshow("Original", img1);
//创建掩码图像
Matmask(img1.rows, img1.cols,CV_8UC1,Scalar(0, 0, 0));
circle(mask,Point(img1.rows / 2, img1.cols / 2), 150, 255, -1);
imshow("Mask", mask);
//执行AND运算
Matr;
bitwise_and(img1, mask, r);
//使用白色填充外部
constucharwhite = 255;
for(inti = 0;i < r.rows;i++)
for(intj = 0;j < r.cols;j++)
if(!mask.at<uchar>(i, j))
r.at<uchar>(i, j) = white;
imshow("Result", r);
waitKey();
return0;
}
应用2用来 估计PI的值
/*
*示例二
*用bitwise_and()对PI的值进行估计
*
*/
constintside
= 200;
constintnpixels
= 600000;
inti,
j;
Mats1
=Mat::zeros(side, side,CV_8UC1);
Mats2
= s1.clone();
circle(s1,Point(side
/ 2, side / 2), side / 2, 255, -1);
imshow("s1",
s1);
for(intk=0;k<npixels;k++)
{
i = rand() % side;
j = rand() % side;
s2.at<uchar>(i,
j) = 255;
}
imshow("s2",
s2);
Matr;
bitwise_and(s1, s2, r);
imshow("r",
r);
intAcircle
= countNonZero(r);
intAsquare
= countNonZero(s2);
floatPi
= 4 * (float)Acircle / Asquare;
cout<<"Estimated
value of Pi is:"<<fixed<<setprecision(6)<<Pi<<endl;
waitKey();
return0;
}数据持久化
OpenCV中除了读取、写入图像和视频特定的函数之外,还有一种更加通用的方式来保存和加载数据,这种方法称之为数据持久化(data persistence):程序中对象和变量的值被记录(序列化)在磁盘上。
主类是aptly,命名是FilleStorage,表示磁盘上一个文件。实际上,数据被储存为XML,或者YAML格式。
写入数据时的步骤:
1 调用构造函数FileStorage,使用FileStorage::WRITE值传递一个文件名称和一个标志,数据格式则是由文件扩展名(即:.xml,.yml或.yaml)定义的;
2 使用运算符<<将数据写入文件,数据通常被写为字符串值对;
3 使用release方法关闭文件
读取数据时需要如下步骤:
1 调用构造函数FileStorage,使用FileStorage::READ值床底一个文件名和一个标志;
2 使用运算符>>或者[ ]从文件中读取数据
3 使用release方法关闭文件
#include<opencv2/opencv.hpp>
#include<iostream>
usingnamespacestd;
usingnamespacecv;
Matimg1;
voidtb1_Callback(intvalue,void*)
{
Mattemp = img1+value;
imshow("main_win", temp);
}
intmain()
{
img1=imread("girl.png",IMREAD_GRAYSCALE);
if(img1.empty())
{
cout<<"Error! Cannot load image!"<<endl;
}
inttb1_value = 0;
//加载滑动条的值
FileStorage fs1("config.xml",FileStorage::READ);
tb1_value = fs1["tb1_value"];//读取数据tb1_value的方法1
//fs1["tb1_value"]>>tb1_value ;//读取数据tb1_value的方法2
fs1.release();
//创建滑动条
namedWindow("main_win");
createTrackbar("brightness","main_win", &tb1_value, 255, tb1_Callback);
tb1_Callback(tb1_value,NULL);
waitKey();
//退出时保存滑动条的值
FileStoragefs2("config.xml",FileStorage::WRITE);
fs2<<"tb1_value"<<tb1_value;
fs2.release();
return0;
}
图像遍历
#include <opencv.hpp>
#include <opencv2/opencv.hpp>
#include <opencv/highgui.h>
#include <iostream>
using namespace std;
using namespace cv;
/*
* To reduce the color of R,G,B
* @param src
* @param dst
* @param div how many parts should we divide the color space to
*/
void reduceColor(Mat src, Mat &dst,int div)
{
for (int i = 0;i < src.rows;i++)
{
for (int j = 0; j < src.cols; j++)
{
cout << "shame!-----------\n";
dst.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i, j)[0] / div*div + div / 2;
dst.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i, j)[1] / div*div + div / 2;
dst.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i, j)[2] / div*div + div / 2;
}
}
}
int main()
{
int div=64;
//read the souce image
Mat src;
src=imread("dog2.png");
namedWindow("Original", WINDOW_AUTOSIZE);
imshow("Original", src);
//reduce color
Mat rdc_img;
reduceColor(src, rdc_img, div);
//show
namedWindow("Reduced color image", WINDOW_AUTOSIZE);
imshow("Reduced color image", rdc_img);
waitKey();
return 0;
}