OpenCV2.4.3 CheatSheet学习

来源:互联网 发布:新倩女幽魂在mac上玩 编辑:程序博客网 时间:2024/06/05 00:34




OpenCV向MATLAB靠拢,图像的操作方法变得不那么C了,更m了一些。比如,MATLAB中的常用函数imshow、imread、imwrite函数在OpenCV中已经有了同名的兄弟。

此外,OpenCV 2.4.3中更加强调对矩阵的操作,以前的CvMat和CvArr目测现在正在被一个新成员Mat给后来居上了。
在doc目录里出现了一个Cheat Sheet,好家伙,原来是OpenCV的作弊纸呀,把基本用法整理成考试必备的模样,正好方便我们学习了。


所以,我们来共同学习一下吧:


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


image.create(480, 640, CV8UC3);
分配(或重新分配)image矩阵,把大小设为480×640,类型设为CV8UC3。


Mat A33(3, 3, CV_32F, Scalar(5));
定义并初始化一个3×3的32bit浮点数矩阵,每个元素都设为5。


Mat B33(3, 3, CV_32F);
B33 = Scalar(5);
和上面的作用一样。


Mat C33 = Mat::ones(3, 3, CV32F)*5.;
ones函数很像MATLAB里的语句吧。这句的意思是先定义一个3×3的32bit浮点数矩阵,元素全为1,所有元素再乘以5.0。


Mat D33 = Mat::zeros(3, 3, CV32F) + 5.;
和上面类似,先定义个3×3的32bit浮点数矩阵,元素全为0,再将所有元素加上5.0;


double a = CV_PI/3;
Mat A22 = (Mat_(2, 2) << cos(a), -sin(a), sin(a), cos(a));
CV_PI就是少年派的那个派。第二句这个写法很牛x的样子,我也没见过,不过看样子是创建一个2×2的float矩阵,把后面四个三角函数值分别赋给4个元素。


float B22data[] = {cos(a), -sin(a), sin(a), cos(a)};
Mat B22 = Mat(2, 2, CV32F, B22data).clone();
第一句创建一个普通数组B22data,第二句创建一个2×2的32bit浮点数矩阵,并使用用B22data数组里的值初始化,然后克隆一下赋给B22矩阵。
为什么这里还要克隆一下,不是多此一举吗?不是的,用一个数组去初始化一个矩阵的话,你会发现这个矩阵引用了数组的内存地址。不克隆的话,上面例程的后果是B22data数组、Mat(2,2...)这个临时变量矩阵、B22矩阵这三把勺子都插在同一个碗里。


randu(image, Scalar(0), Scalar(256));
把image弄成一个符合正太分布的随机数矩阵,rand表示random,u表示uniform;第二个参数是随机数下限,方括号哦;第三个参数是随机数上限,圆括号。


randn(image, Scalar(128), Scalar(10));
高斯分布的随机数矩阵;第二个参数是均值,第三个参数是标差。


Mat image_alias = image;
没有拷贝里面的数据。


float* Idata=new float[480*640*3];
Mat I(480, 640, CV32FC3, Idata);
第一句定义一个480×640×3×sizeof(float)字节这么大的数组;第二句创建一个矩阵I,引用的是Idata的地址。


vector iptvec(10);
Mat iP(iptvec); 
还能和STL一起用啊,以前真不知道。第一句话创建了一个有10个Point的向量,注意Point有两个int型元素。第二句创建了一个矩阵,元素类型根据就自动设为CV_32SC2,表示32bit signed int,2个channel。


IplImage* oldC0 = cvCreateImage(cvSize(320,240),16,1);
Mat newC = cvarrToMat(oldC0);
IplImage oldC1 = newC;
CvMat oldC2 = newC;
这是为了把经典的OpenCV图像导成矩阵,第一句创建一个320×240的图像;第二句话把IplImage转成Mat;第三句话把Mat转成IplImage;第四句把Mat转成CvMat。


Mat newC2 = cvarrToMat(oldC0).clone();
转换后克隆一下赋值。


vector ptvec = Mat(iP);
把Mat又转成vector。


(转来转去真复杂对吧)


A33.at(i,j) = A33.at(j,i)+1;
操作A33矩阵在row为i,col为j处的元素。需要显式指定A33里的元素类型,本例是float。


Mat dyImage(image.size(), image.type());
for(int y = 1; y < image.rows-1; y++)
{
Vec3b* prevRow = image.ptr(y-1);
Vec3b* nextRow = image.ptr(y+1);
for(int x = 0; y < image.cols; x++)
for(int c = 0; c < 3; c++)
dyImage.at(y,x)[c] = saturate_cast(nextRow[x][c] - prevRow[x][c]);
}
第一句话创建一个和image大小、类型都一样的矩阵。
Vec3b是一个预定义的类型,三个无符号字符组成的向量:
typedef Vec Vec3b;
saturate_cast是一个强制类型转换,把圆括号里的东西转换为尖括号里的类型。
for循环里的语句是把矩阵dyImage里的元素都赋值为后一行的值减去前一行的值,注意这个矩阵里的值是个具有三个元素的向量,理解为数组就行了,所以用方括号访问。


Mat::iterator it = image.begin(), itEnd = image.end();
for(; it != itEnd; ++it)
(*it)[1] ^= 255;
用过STL里的迭代器吗?用过的话就不解释了,没用过的还是先看看STL吧。
对image每个值(Vec3b向量)的第一个元素和0xFF做异或。
二、矩阵操作(拷贝、洗牌、局部访问):
      
src.copyTo(dst)
把src矩阵中的数据拷贝到dst。
 
src.convertTo(dst, type, scale, shift)
缩放并转换到另外一种数据类型:
dst:目的矩阵
type:需要的输出矩阵类型,或者更明确的,是输出矩阵的深度,如果是负值(常用-1)则输出矩阵和输入矩阵类型相同
scale和shift:缩放参数,也可以写为alpha和beta
这个命令也等价于下面的转换公式:
m(x,y) = saturate_cast(α(*this)(x,y)+β)
 
m.clone()
深度拷贝(啥意思?看《C++ Primer》吧)
 
m.reshape(nch,nrows)
重设矩阵的通道数和行数,不拷贝数据。
nch:新的通道数,若为0则不变
nrows:新的行数,若为0则不变
 
m.row(i), m.col(i)
创建一个矩阵头,指向m矩阵的第i行/列,O(1)复杂度,不拷贝数据,新的矩阵头所代表的矩阵和m矩阵的第i行/列共享数据。
 
m.rowRange(Range(i1,i2))
m.colRange(Range(j1,j2))
创建一个矩阵头,指向m矩阵的第i1到i2行或者第j1到j2列,O(1)复杂度,不拷贝数据。
 
m.diag(i)
创建一个矩阵头,指向m矩阵的对角线,生成的是一个单列矩阵,O(1)复杂度,不拷贝数据。i=0时表示主对角线,i>0表示下半边的对角线,i<0表示上半边的对角线。
 
m(Range(i1,i2),Range(j1,j2))
从矩阵m中的第i1行到第i2行以及第j1列到第j2列所划定的范围提取一个小矩阵。
 
m.repeat(ny,nx)
把m矩阵贴马赛克,获取一个大矩阵,在y方向上重复ny次,在x方向上重复nx次。
 
flip(src,dst,dir)
翻转矩阵,dir是翻转方向,0表示沿x轴翻转,1表示沿y轴翻转,-1表示沿x轴和y轴都进行翻转。
 
split(...)
把一个多通道矩阵分解为几个单通道矩阵,操作RGB图像之类的最常用了。
 
merge(...)
和上面的操作相反。
 
mixChannels(...)
上面两个函数的一般形式。
 
randShuffle(...)
把矩阵中的元素随机重排
 
 
示例1:
Mat imgroi = image(Rect(10, 20, 100, 100));
GaussianBlur(imgroi, imgroi, Size(5, 5), 1.2, 1.2);
第一句话取image的一个区域,第二句话对这个区域进行高斯平滑。
 
示例2:
m.row(i) += m.row(j)*alpha;
m矩阵的第j行乘以alpha后加到第i行中。
另外,在Mat::row的介绍中提到,在目前的实现中
A.row(i) = A.row(j);
这样的语句是不行的,改成
A.row(i) = A.row(j) + 0;
或者这样就可以了:
A.row(j).copyTo(A.row(i));
比较神奇~
 
示例3:
Rect r(1, 1, 10, 20);
Mat dstroi = dst(Rect(0,10,r.width,r.height));
src(r).convertTo(dstroi, dstroi.type(), 1, 0);
第一句定义一个矩形范围,第二句从dst矩阵中扣出一个ROI区域,第三句把src矩阵中由r定义的范围转换到dstroi中。
 
 
 
三、简单矩阵操作
 
add(), subtract(), multiply(), divide(), absdiff(), bitwiseand(), bitwiseor(), bitwisexor(), max(), min(), compare()
分别是加减乘除、按位与或异或、最大最小之类的。
 
sum(), mean(), meanStdDev(), norm(), countNonZero(), minMaxLoc()
求和、均值、均值方差、矩阵范数、非零个数、最大最小值。
 
exp(), log(), pow(), sqrt(), cartToPolar(), polarToCart()
指数、对数、乘方、开放、极坐标转换。
 
scaleAdd(), transpose(), gemm(), invert(), solve(), determinant(), trace(), eigen(), SVD
线性组合、转置、广义矩阵乘法、矩阵求逆、解线性系统或最小二乘问题、计算行列式、矩阵的迹、计算对称矩阵的特征值和特征向量、奇异值分解
 
dft(), idft(), dct(), idct()
离散傅立叶变换、离散余弦变换。
四、图像处理(呵呵,重头戏来了)
1. 滤波
filter2D()
用核函数对图像做卷积。


sepFilter2D()
用分解的核函数对图像做卷积。
首先,图像的每一行与一维的核kernelX做卷积;然后,运算结果的每一列与一维的核kernelY做卷积。


boxFilter()
就是滑动窗口平均滤波的二维版。


GaussianBlur()
高斯平均,也就是高斯模糊。


medianBlur()
中值滤波,个人最爱的滤波函数。


bilateralFilter()
双线性滤波。


前面这四个函数是原来OpenCV里的cvSmooth()取不同参数的应用。


Sobel()
使用扩展 Sobel 算子计算一阶、二阶、三阶或混合图像差分,看冈萨雷斯的那本《数字图像处理》。


Scharr()
计算一阶导,x方向或y方向,以前这个方法是放在cvSobel里的。


Laplacian()
拉普拉斯变换。


erode(), dilate()
腐蚀、膨胀。


示例:
filter2D(image, image, image.depth(), (Mat<float>(3,3)<<-1, -1, -1, -1, 9, -1, -1, -1, -1), Point(1,1), 128);
构造了一个如下所示的核对图像做卷积:
-1 -1 -1
-1 9 -1
-1 -1 -1
核的锚点在(1,1)位置,卷积之后每个像素加上128.


2. 几何变换
resize()
改变图像尺寸,可以指定x方向和y方向上的缩放比例,可以指定插值方法。


getRectSubPix()
以亚像素精度从图像中提取矩形。
dst(x,y)=src(x+center.x-(dst.cols-1)*0.5,y+center.y-(dst.rows-1)*0.5)
其中非整数象素点坐标采用双线性插值提取。


warpAffine()
放射变换,看冈萨雷斯《数字图像处理》。


warpPerspective()
透射变换。


remap()
几何变换。


convertMaps()



示例:
Mat dst;
resize(src, dst, Size(), 1./sqrt(2), 1./sqrt(2));
把图像缩小到原来的根号二分之一。


3. 图像变换
cvtColor()
色彩空间转换。
顺便提一句,这个函数可以用于把CCD的raw格式转换为RGB,请参考:
http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=18681
但是不能用于把灰度图转成伪彩图,请参考:
http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=23729


threshold()
二值化,常用操作,一般应用时建议用大津算法,即使用THRESH_OTSU参数。


adaptivethreshold()
自适应阈值的二值化。


floodFill()
填充连通域。


integral()
计算积分图像,一次或者二次。


distanceTransform()
距离变换,对原图像的每一个像素计算到最近非零像素的距离。


watershed()
分水岭图像分割。


grabCut()
一种彩色图像分割算法



4. 直方图
calcHist()
计算直方图。


calcBackProject()
计算反向投影。


equalizeHist()
灰度图像的直方图均衡化,常用操作。


compareHist()
比较两个直方图。


栗子:
Mat hsv, H;
cvtColor(image, hsv, CVBGR2HSV);
int planes[]=f0, 1g, hsize[] = f32, 32g;
calcHist(&hsv, 1, planes, Mat(), H, 2, hsize, 0);
计算图像的色调-饱和度直方图。
五、数据的输入和输出


1. 将数据写入YAML(或XML)


注意,在OpenCV中,无论读写,文件的格式均由指定的后缀名确定。示例:


FileStorage fs("test.yml", FileStorage::WRITE);
fs << "i" << 5 << "r" << 3.1 << "str" << "ABCDEFGH";
fs << "mtx" << Mat::eye(3,3,CV_32F);
fs << "mylist" << "[" << CV_PI << "1+1" << "{:" << "month" << 12 << "day" << 31 << "year" << 1969 << "}" << "]";
fs << "mystruct" << "{" << "x" << 1 << "y" << 2 << "width" << 100 << "height" << 200 << "lbp" << "[:";
const uchar arr[] = {0, 1, 1, 0, 1, 1, 0, 1};
fs.writeRaw("u", arr, (int)(sizeof(arr)/sizeof(arr[0])));
fs << "]" << "}";


读写方法基本和C++没什么两样,很简单哈。
用记事本或IE就可以打开test.yml文件,可以很清楚的看到文件中的数据结构:


%YAML:1.0
i: 5
r: 3.1000000000000001e+000
str: ABCDEFGH
mtx: !!opencv-matrix
   rows: 3
   cols: 3
   dt: f
   data: [ 1., 0., 0., 0., 1., 0., 0., 0., 1. ]
mylist:
   - 3.1415926535897931e+000
   - "1+1"
   - { month:12, day:31, year:1969 }
mystruct:
   x: 1
   y: 2
   width: 100
   height: 200
   lbp: [ 0, 1, 1, 0, 1, 1, 0, 1 ]


2. 将数据读回


FileStorage fs("test.yml", FileStorage::READ);
int i1 = (int)fs["i"];
double r1 = (double)fs["r"];
string str1 = (string)fs["str"];
Mat M;
fs["mtx"] >> M;
FileNode tl = fs["mylist"];
CV_Assert(tl.type() == FileNode::SEQ && tl.size() == 3);
double tl0 = (double)tl[0];
string tl1 = (string)tl[1];
int m = (int)tl[2]["month"], d = (int)tl[2]["day"];
int year = (int)tl[2]["year"];
FileNode tm = fs["mystruct"];
Rect r;
r.x = (int)tm["x"], r.y = (int)tm["y"];
r.width = (int)tm["width"], r.height = (int)tm["height"];
int lbp_val = 0;
FileNodeIterator it = tm["lbp"].begin();
for(int k = 0; k < 8; k++, ++it)
  lbp_val |= ((int)*it) << k;


这段代码把上面写入文件的数据再读回来。


3. 读写图像,最常用的操作


imwrite("myimage.jpg", image);
Mat imagecolorcopy = imread("myimage.jpg", 1);
Mat imagegrayscalecopy = imread("myimage.jpg", 0);


和MATLAB中的同名函数用法基本一样,很方便。文件格式由指定的后缀名确定。
imread可以指定读取图像的格式,参数0就是CV_LOAD_IMAGE_GRAYSCALE,即读取为灰度图;参数1就是CV_LOAD_IMAGE_COLOR,即读取为彩色图。
可以支持如下文件格式:
BMP (.bmp), JPEG (.jpg, .jpeg), TIFF (.tif, .tiff), PNG (.png), PBM / PGM / PPM (.pbm, .pgm, .ppm), Sun Raster (.sr), JPEG 2000 (.jp2).
每种格式都支持8比特的单通道或3通道图像,PNG和JPEG2000格式支持16bit。


4. 从文件或相机中读取视频


VideoCapture cap;
if(argc>1)
  cap.open(string(argv[1]));
else
  cap.open(0);
Mat frame;
namedWindow("video", 1);
for(;;)
{
  cap >> frame;
  if(!frame.data)
    break;
  imshow("video", frame);
  if(waitKey(30) >= 0)
    break;
}


如果指定了相机名称则打开对应的相机,否则打开默认相机0。打开一个名叫video的窗口,以30ms为间隔显示视频,也就是约33.33fps。




六、GUI(Graphical User Interface)组件


namedWindow(winname,flags)
创建一个窗口,用于显示。


destroyWindow(winname)
销毁一个窗口。


destroyAllWindows()
销毁所有窗口。


imshow(winname, mtx)
在指定的窗口中显示图像。


waitKey(delay)
等待delay毫秒,如果delay为0则一直等待。返回值是按键值,常用于显示图像的刷新和对按键的处理。


createTrackbar(...)
创建一个滑动条。


setMouseCallback(...)
设置鼠标事件的回调函数。


可以从camshiftdemo.cpp以及其他OpenCV示例工程中学习GUI的详细使用方法。




七、相机校准、姿态估计、深度估计


相机校准主要用到了张正友的棋盘格校准法,在OpenCV文件夹里可以找到棋盘格图像pattern.png,打印下来贴到一个硬板上就可以用以下函数对相机参数进行校准了。


calibrateCamera()
利用对棋盘格的一系列抓拍图像对相机进行校准。


findChessboardCorners()
寻找棋盘格的角点。


剩下的我都没用过:
solvePnP()
stereoCalibrate()
stereoRectify()
initUndistortRectifyMap()
StereoBM, StereoSGBM
reprojectImageTo3D()
findHomography()




八、目标识别
matchTemplate
CascadeClassifier

HOGDescriptor


原帖链接:点击打开链接