图像处理基础学习笔记——OpenCV已学API汇总

说明

这篇博客只用来记录目前我已经接触过的API,只涉及用法及效果,不涉及背后算法,具体算法我会在其他的博客中进行介绍。随着逐渐学习,我也会对这篇博客进行动态更新,有些内容缺少的就是我也还没弄懂的。

并不会详细解释API,更适合有一定经验的人查阅。

我也只是个初学者,很多内容都是跟着教程的框架进行学习,如果内容上有错误欢迎大家指正与补充。


基础操作

读入

图片

imread()函数

Mat imread( const String& filename, int flags = IMREAD_COLOR );

第一个参数是图片路径

第二个参数是读入色彩模式

常用的模式:

IMREAD_UNCHANGED 按图片原色彩模式读入

IMREAD_GRAYSCALE 按灰度图读入

IMREAD_COLOR 按BGR彩色读入

样例代码

Mat demo;
demo = imread("F://opencv file/picture.jpg" , IMREAD_UNCHANGED);
视频或摄像头

VideoCapture

使用样例

VideoCapture cap;
cap.open(0); // cap.open("F:\\opencv file/测试视频.mp4");
//以上等价于 VideoCaputure cap(0);
Mat demo;
while(1){
cap>>demo
imshow("demo",demo);
waitKey(30);
}

先创建一个VideoCapture通过open可以打开摄像头或者视频,摄像头的话是使用摄像头的index号,笔记本摄像头默认为0。视频是使用路径。

然后用>>读入到Mat中进行处理,循环输出。

VideoCapture还有一些常用的方法。

cap.get(CV_CAP_PROP_FRAME_WIDTH) //帧宽度
cap.get(CV_CAP_PROP_FRAME_HEIGHT) //帧高度
cap.get(CV_CAP_PROP_FPS);  //帧率 x frames/s
cap.get(CV_CAP_PROP_FRAME_COUNT); //总帧数

输出

图片

imwrite()函数

imwrite( const String& filename, InputArray img,const std::vector<int>& params = std::vector<int>());

第一个参数是输出的文件路径

第二个参数是要输出的图片

视频

VideoWriter

这部分我还没倒腾明白,之后用到的时候再补齐

颜色

Scalar对象与函数

样例代码

Scalar color = Scalar(255,255,255) 	//设置色彩为白色

随机数(随机色彩)

RNG

样例代码

RNG rng((unsigned)time(NULL));	//声明时需要设定一个种子,一般会设置为当前时间,可以保证每次的种子都不同。
int i = rng.uniform(0,255)	//uniform(a,b),可以生成[a,b)之间的随机数,返回值与输入的范围值相同,也可以用强制转换得到多种数据类型

像素范围处理

saturate_cast()函数

用于使数值不超过数据类型允许范围

样例代码

uchar r = saturate_cast<uchar>(400)		//返回255
uchar r = saturate_cast<uchar>(-100)	//返回0
uchar r = saturate_cast<uchar>(100)		//返回100

色彩转换

cvtColor()函数

void cvtColor( InputArray src, OutputArray dst, int code, int dstCn = 0 );

第一个参数是输入图像

第二个参数是输出图像

第三个参数是转换模式

常用的有:

CV_BGR2GRAY //BGR转灰度

CV_BGR2HSV //BGR转HSV

样例代码

cvtColor(demo,demo,CV_BGR2GRAY);  //从BGR彩色图转换为灰度图

形态学操作、直方图、边缘检测等API需要输入单通道的图,就可以将彩色图转换为单通道的灰度图,也可以选择将不同的通道分为不同的图像。

通道分离

将多通道的彩色图像,分为多个单通道的图。

split()函数

void split(const Mat& src, Mat* mvbegin)

第一个参数是输入Mat对象地址

第二个参数是分通道输出的Mat对象数组

样例代码

Mat dst[3];			//声明Mat对象数组
split(demo,dst);	//将demo对象的三个通道分别赋给dst数组

要注意分离后的图像都是单通道,直接显示都是灰度图。

通道合并

将多个单通道图像合并为一个多通道的图像

merge()函数

函数原型

void merge(InputArrayOfArrays mv, OutputArray dst);

第一个参数是输入的多个单通道矩阵/图像的阵列,必须有相同深度和尺寸

第二个参数是合并后的矩阵/图像,通道数需要与参数一中的个数一致

样例代码

vector<Mat> channels;	//用于储存分离后的通道
split(image_src, channels);		//分离通道
merge(channels, image_dst);		//合并通道

该函数是上边split()的逆操作,参数也类似。

图像线性混合

addWeighted()函数

函数原型

void addWeighted(InputArray src1, double alpha, InputArray src2,double beta, double gamma, OutputArray dst, int dtype = -1);

第一个参数是输入图像1

第二个参数是图像1所占的比例

第三个参数是输入图像2

第四个参数是图像2所占的比例

第五个参数是偏差值,用0就可以

第六个参数是输出图像

样例代码

addWeighted(image1, 0.4 , image2 , 0.6 , 0 , image_out);

图像线性运算

cvConvertScale()函数

函数原型

cvConvertScale( const CvArr* src, CvArr* dst, double scale ,double shift  );

第一个参数是输入矩阵

第二个参数是输出矩阵

第三个参数是乘数因子

第四个参数是加权值

最后结果是输入矩阵中的每个元素 乘以乘数加上加权值 得到输出矩阵

cvConvertScaleAbs()函数

参数与上边的API一致,区别是这个API输出时会取绝对值。

归一化

normalize()函数

函数原型

void normalize(InputArray src, InputOutputArray dst, double alpha=1, double beta=0, int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray() )

第一个参数是输入数组/图像

第二个参数是输出数组/图像

第三个参数是用来规范值或者规范范围,并且是下限。

第四个参数是用来规范范围并且是上限 只对NORM_MINMAX有效

第五个参数是归一化的模式

归一到(min,max)范围内:NORM_MINMAX 最小值为0,最大值为1,其他数根据比例计算

根据切比雪夫距离归一:NORM_INF 最大值为1,其他数除以最大值

根据曼哈顿距离归一:NORM_L1 每个数除以所有数和(绝对值)

根据欧几里得距离归一:NORM_L2 每个数除以所有数平方和的开方

样例代码

vector<double>demo = {10,17,20};
normalize(demo,demo, 1 , 255 ,NORM_L1);

画线与图形

线

line()函数

函数原型

void line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,int thickness = 1, int lineType = LINE_8, int shift = 0);

第一个参数是输入的图像

第二个参数是线段起始点

第三个参数是线段结束点

第四个参数是线段颜色

第五个参数是线段宽度

第六个参数是线形

样例代码

line(image , Point(50,50) , Point(100,100) , Scalar(255,0,0) , 3 , LINE_8);

矩形

rectangle()函数

函数原型

rectangle(InputOutputArray img, Point pt1, Point pt2,const Scalar& color, int thickness = 1, int lineType = LINE_8, int shift = 0);

与上边划线类似,第一个点是矩形左上角,第二个点是矩形右下角

样例代码

rectangle(image , Point(50,50) , Point(100,100) , Scalar(255,0,0) , 3 , LINE_8);

圆形

circle()函数

函数原型

void circle(InputOutputArray img, Point center, int radius, const Scalar& color, int thickness = 1,int lineType = LINE_8, int shift = 0);

第一个参数是输入图像

第二个参数是圆心坐标

第三个参数是半径

第四个参数是颜色

第五个参数是线宽

第六个参数是线形

样例代码

circle(image, Point(50,50) , 20 , Scalar(255,0,0) , 2 , LINE_8);

椭圆

ellipse()函数

函数原型

void ellipse(InputOutputArray img, Point center, Size axes,
double angle, double startAngle, double endAngle, const Scalar& color, int thickness = 1,int lineType = LINE_8, int shift = 0);

第一个参数是输入图像

第二个参数是椭圆中心点

第三个参数是椭圆的大小(姑且可以认为是可以框住椭圆的矩形尺寸,可以以此确定椭圆的长轴与短轴长度)

第四个参数是椭圆沿逆时针方向旋转的角度

第五、六个参数分别是椭圆开始的角度以及结束的角度(用于画带缺口的椭圆线,角度沿长轴顺时针方向)

第七个参数是颜色

第八个参数是线宽

第九个参数是线形

样例代码

ellipse(image , Point(100,100) , Size(30,60) , 30 , 0 , 180 , Scalar(255,0,0) , 2 , LINE_8);

多边形或折线

polylines()函数

支持一次性画多个多边形

函数原型

void polylines(Mat& img, const Point* const* pts, const int* npts,int ncontours, bool isClosed, const Scalar& color,int thickness = 1, int lineType = LINE_8, int shift = 0 );

第一个参数是画布图像

第二个参数是顶点数组,由于该函数支持同时画多个多边形,所以顶点数组是二维数组,且要将每个数组的头地址再装进一个一维数组中,这里的参数只用最后的一维数组

第三个参数是顶点数量,也是为了画多个图形,所以需要用数组的形式储存顶点数量

第四个参数是多边形的数量

第五个参数是折线是否闭合,真或假

后边的就是颜色、线型之类的了

样例代码

Point points[2][3];
points[0][0] = Point(50,50);
points[0][1] = Point(20,50);
points[0][2] = Point(30,40);
points[1][0] = Point(60,50);
points[1][1] = Point(50,80);
points[1][2] = Point(40,30);
const Point* ptr[]={points[0] , points[1]};
int npts[] = {3,3};
polylines(image , ptr , npts , 2 , ture , Scalar(255,0,0) , 2, LINE_8);

fillPoly()函数

该函数参数与polylines基本一致,区别是该函数是实心多边形,polylines只是边框

函数原型

fillPoly(Mat& img, const Point** pts,const int* npts, int ncontours,const Scalar& color, int lineType = LINE_8, int shift = 0, Point offset = Point() );

和上边很相似,基本只需要五个参数,输入图像,顶点数组,顶点数量,图形数量,填充颜色

文字

putText()函数

函数原型

void putText( InputOutputArray img, const String& text, Point org, int fontFace, double fontScale, Scalar color,
int thickness = 1, int lineType = LINE_8,bool bottomLeftOrigin = false );

第一个参数是输入图像

第二个参数是要输出的字符串

第三个参数是文本框左下角的坐标

第四个参数是字体

第五个参数是尺寸

第六个参数是颜色

样例代码

putText(image , "Hello world" , Point(50,50) , FONT_HERSHEY_COMPLEX , 2 , Scalar(255,0,0));

点操作

  1. at方法

样例代码

uchar value = image.at<uchar>(i,j);	//获得单通道图像i行j列像素点的值
image.at<uchar>(i,j) = 255;	//单通道图像赋值操作
uchar value = image.at<Vec3b>(i,j)[0];	//取出三通道图像i行j列像素点的第一个通道数值。
Vec3b pixel = image.at<Vec3b>(i,j);	//将三通道图像i行j列像素点数组赋给pixel

需要注意对遍历多通道图像时使用像素点数组进行操作时,列数为像素点的列数(及图像列数)。直接获取单通道数值进行操作时,列数为像素点的列数*通道数

at函数可读性高,但效率不高,不适合用来遍历图像。

  1. ptr方法

样例代码

const uchar* date_in = image_in.ptr<uchar>(i);	//获取图像第i行像素值的指针,设置为常量是为了保证原图不变,保留副本。
uchar* date_out = image_out.ptr<uchar>(i);	
date_out[j] = date_in[j];	//复制像素

需要特别注意,这里是将一行的数据装进数组,也就意味着对于通道的图,一行数据一共有图像列数*通道数个。如果要对单一通道进行操作,遍历时每次应该向下移动通道数个数据。

以这种方法遍历,效率更高。

遍历时,单点像素乘以常数可以调整对比度,加减常数可以调整亮度。

卷积操作

卷积核

想要进行卷积运算需要先定义一个卷积核。

Mat kernel = (Mat_<uchar>(3,3) << 0,1,0,1,-4,1,0,1,0);	

上式创建的掩膜为:

010
1-41
010

该卷积核可以用来提高对比度。又被成为拉普拉斯算子

sobel算子,可以用于检测边缘
检测横向边缘:

-101
-202
-101

检测纵向边缘

-1-2-1
000
121

卷积

filter2D()函数

函数原型

void filter2D( InputArray src, OutputArray dst, int ddepth,InputArray kernel, Point anchor = Point(-1,-1),double delta = 0, int borderType = BORDER_DEFAULT )

第一个参数是输入图像

第二个参数是输出图像

第三个参数是图像深度, -1 代表与输入图像一致

第四个参数是卷积核

第五个参数是锚点

样例代码

filter2D(image_in,image_out,-1,kernel);	//使用kernel为卷积核输出image_out

模糊

均值模糊/均值滤波

blur()函数

函数原型

void blur( InputArray src, OutputArray dst,Size ksize, Point anchor = Point(-1,-1),int borderType = BORDER_DEFAULT );

第一个参数是输入图像

第二个参数是输出图像

第三个参数是均值模糊卷积核的大小,全方向模糊一般取3*3或5*5,也可以去单列或者单行的大小以进行单方向的模糊。要注意这个尺寸要确保有中心点存在。(2*2就不存在绝对的中心点)

第四个参数是锚点,默认位卷积核中心

第五个是边缘处理模式,一般用默认

样例代码

blur(image_in , image_out , Size(3,3));

均值模糊无法保留边缘特征,只能整体模糊。

中值模糊/中值滤波

medianBlur()函数

函数原型

void medianBlur( InputArray src, OutputArray dst, int ksize );

第一个参数是输入图像

第二个参数是输出图像

第三个参数是卷积核的大小,固定是正方形,所以只取数字就可以,9就代表9*9的范围

样例代码

medianBlur(image_in , image_out , 3);

中值滤波很适合去除椒盐噪声。

高斯模糊/高斯滤波
高斯

GaussianBlur()函数

函数原型

void GaussianBlur( InputArray src, OutputArray dst, Size ksize,double sigmaX, double sigmaY = 0,int borderType = BORDER_DEFAULT );

第一个参数是输入图像

第二个参数是输出图像

第三个参数是卷积核的大小

第四个参数是x方向上的标准偏差

第五个参数是y方向上的标准偏差

第六个参数是边缘处理模式

样例代码

GaussianBlur(image_in , image_out , Size(3,3) , 0.5 , 0.5);

这种高斯模糊相较于均值模糊可以保留更多细节,但对边缘的保留一样效果不太好。

双边高斯

bilateralFilter()函数

函数原型

void bilateralFilter( InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace,int borderType = BORDER_DEFAULT );

第一个参数是输入图像

第二个参数是输出图像

第三个参数是过滤器的大小,太大的话会影响速度

第四个参数是色彩空间的sigma,大一点可以提高模糊效果

第五个参数是坐标空间的sigma,小一点可以提高边缘保留效果

样例代码

bilateralFilter(image_in , image_out , 5 , 150 , 3);

模糊效果不错,而且可以保留边缘,对人脸处理时,有类似于磨皮的效果。


形态学操作

结构体

getStructuringElement()函数

函数原型

Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1));

第一个参数是结构体的形状

预设的形状有:MORPH_RECT 矩形 MORPH_CROSS 十字 MORPH_ELLIPSE 椭圆形

第二个参数是结构体的大小

第三个参数是锚点位置

样例代码

Mat kernel = getStructuringElement(MORPH_RECT , Size(3,3));

运算

morphologyEx()函数

函数原型

void morphologyEx( InputArray src, OutputArray dst, int op, InputArray kernel, Point anchor = Point(-1,-1), int iterations = 1,int borderType = BORDER_CONSTANT,const Scalar& borderValue = morphologyDefaultBorderValue() );

第一个参数是输入图像

第二个参数是输出图像

第三个参数是操作模式

操作模式有:

膨胀:MORPH_DILATE 锚点处像素值取结构体覆盖下的最大值 扩大亮处

腐蚀:MORPH_ERODE 锚点处像素值取结构体覆盖下的最小值 扩大暗处

开运算:MORPH_OPEN 先腐蚀再膨胀 去掉小亮斑

闭运算: MORPH_CLOSE 先膨胀再腐蚀 去掉小暗斑

顶帽: MORPH_TOPHAT 原图像与开运算后的结果求差 可以观察开运算去除了哪些物体

黑帽: MORPH_BLACKHAT 原图像与闭运算后的结果求差 可以观察闭运算的效果

形态学梯度:MORPH_GRADIENT 膨胀后的图减去腐蚀后的图 可以得到图像大致的边缘

第四个参数是结构体

检测直线/字符

通过合理设置结构体,再经过开运算就可以检测直线或者字符

检测水平直线,就设置单行较长的结构体

检测竖直直线,就设置单列较长的结构体

检测较粗字符,可以设置正方形的结构体


阈值

阈值操作

threshold()函数

函数原型

threshold( InputArray src, OutputArray dst,double thresh, double maxval, int type );

第一个参数是输入图像

第二个参数是输出图像

第三个参数是阈值

第四个参数是最大值

第五个参数是操作模式

操作模式有:

二值化:THRESH_BINARY 大于阈值为最大,小于阈值为0

反二值化: THRESH_BINARY_INV 大于阈值为0,小于阈值为最大

截断: THRESH_TRUNC 大于阈值的限制为阈值,小于阈值的保持不变

取零: THRESH_TOZERO 大于阈值的不变,小于阈值的为0

反取零: THRESH_TOZERO_INV 大于阈值的为0,小于阈值的不变

最大类间方差: THRESH_OTSU 通过统计学计算阈值,常用于分离物体与背景

自适应阈值: THRESH_TRIANGLE 根据不同区域自行计算阈值,更多时候用另一个API实现

样例代码

threshold(image_in , image_out , 150 , 255 , THRESH_TOZERO);

adaptiveThreshold()函数

自适应阈值操作

函数原型

void adaptiveThreshold( InputArray src, OutputArray dst,double maxValue, int adaptiveMethod,int thresholdType, int blockSize, double C );

第一个参数是输入图像

第二个参数是输出图像

第三个参数是最大值

第四个参数是自适应类型

平均值:ADAPTIVE_THRESH_MEAN_C 邻域像素平均值减去C作为阈值

高斯值:ADAPTIVE_THRESH_GAUSSIAN_C 邻域像素通过高斯加权平均减去C,sigma值取决与邻域大小

第五个参数是阈值操作模式,只能是THRESH_BINARY或者THRESH_BINARY_INV

第六个参数是计算阈值的邻域大小,3,5,7之类的

第七个参数是偏差值,可以设为0

样例代码

adaptiveThreshold(image_in , image_out , 255 , ADAPTIVE_THRESH_MEAN_C , THRESH_BINARY , 3 , 0);

合理的设置阈值可以分离物体与背景,也可以用于获得物体的大致边缘。

边缘检测

Canny()函数

函数原型

void Canny( InputArray image, OutputArray edges,double threshold1, double threshold2, int apertureSize = 3, bool L2gradient = false );

第一个参数是输入图像

第二个参数是输出图像

第三个参数是阈值1 低于这个阈值的点被认为不是边缘

第四个参数是阈值2 高于这个阈值的点被认为是边缘, 处于1与2之间的点会检测是否与边缘点接触,若接触则为边缘点

第五个参数是邻域范围

第六个参数是 是否使用更加精确的计算方法

样例代码

Canny(image_in , image_out , 100 , 200 , 3 , ture);

使用Canny前,要先进行滤波,提高效果


霍夫变换

直线

HoughLinesP()函数

函数原型

void HoughLinesP( InputArray image, OutputArray lines,double rho, double theta, int threshold,double minLineLength = 0, double maxLineGap = 0 );

第一个参数是输入图像

第二个参数是输出的直线两个端点坐标

第三个参数是极坐标中像素的扫描步长

第四个参数是极坐标中角度的扫描步长,一般取 CV_PI/180 也就是一度

第五个参数是阈值,极坐标系中至少要多少个函数相交才会看作直线

第六个参数是直线的最小长度

第七个参数是直线延伸过程中最大的间隔

样例代码

vector<Vec4f> plines ;	//设置一个向量用于存放输出
HoughLinesP(image_in , plines , 1 , CV_PI/180 , 20 , 50 , 5);
for (size_t i = 0; i < plines.size(); i++)	//划出直线
	{
		Vec4f point = plines[i];	//取出第i个直线的端点数据,四个数据依次为,x0,y0,x1,y1
		line(image_out, Point(point[0], point[1]), Point(point[2], point[3]), Scalar(255, 255, 0), 2, LINE_AA);
	}

要先进行Canny边缘检测再进行霍夫直线检测

HoughCircles()函数

函数原型

void HoughCircles( InputArray image, OutputArray circles,int method, double dp, double minDist, double param1 = 100, double param2 = 100,int minRadius = 0, int maxRadius = 0 );

第一个参数是输入图像

第二个参数是输出数据

第三个参数是检测方法,只有CV_HOUGH_GRADIENT

第四个参数设为1

第五个参数是两个圆心之间的最小距离

第六个参数是Canny检测时的高阈值,低阈值默认为高阈值的一半

第七个参数是圆心的阈值,在极坐标系中至少多少个函数的交点会被认为是圆心

第八个参数是圆的最小半径

第九个参数是圆的最大半径

样例代码

vector<Vec3f> pcircles;		//设置一个向量存储输出数据
HoughCircles(image_in , pcircles , CV_HOUGH_GRADIENT , 1 , 100 , 120 , 50 , 100 , 200);
for (size_t i = 0; i < pcircles.size(); i++)	//画出所有的圆,输出的数据中三个数据依次为x0,y0,r
{
	Vec3f circles = pcircles[i];
circle(dst,Point(circles[0],circles[1]),circles[2],Scalar(0,0,255),1,LINE_AA);
	}

霍夫圆很容易受到椒盐噪声的影响,最好先进行中值滤波


直方图

直方图计算

calcHist()函数

函数原型

void calcHist( const Mat* images, int nimages,const int* channels, InputArray mask,SparseMat& hist, int dims,const int* histSize, const float** ranges,bool uniform = true, bool accumulate = false );

第一个参数是输入的图像数组,也可以是单一图像的指针

第二个参数是图像的个数

第三个参数是通道数的地址,需要计算的通道的下标,可以传入一个数组,如果是单通道就是只有0的数组。

第四个参数是掩膜,如果有的话就是只计算掩膜上非零位置,不使用的话可以输cv::Mat()

第五个参数是输出的直方图数据

第六个参数是直方图的维度,一般来说取1

第七个参数是每一维上直方图个数的指针,一般来说越大直方图越精细

第八个参数是要统计的灰度范围,用一维float数组装入最小值与最大值,再装入一个常量float指针数组。

第九个参数是是否归一化,默认开启

样例代码

int channels = 0;	//设置通道数
float range[] = {0,256};	//将最小值与最大值存入一维float数组
const float* histrange[] = {range};	//将一维数组装入常量float指针数组
int hist_size = 200;	//一共200个直方图
calcHist(image_in , 1 , &channels , Mat() , hist , 1 , &hist_size , histrange);	//将直方图数据存入hist
normalize(hist , hist , 0 , image_out.rows , NORM_MINMIX);//进行归一化
double bin_w = (double)image_out.rows/hist_size;
for (int i = 1; i < hist_size; i++)
{
	line(image_out , Point((i-1)*bin_w , image_out.rows - cvRound(hist.at<float>(i-1))) , Point(i*bin_w , image_out.rows - cvRound(hist.at<float>(i))) , Scalar(255,0,0) , 2 , LINE_AA);
} //cvRound是四舍五入取整

计算出直方图后要对结果数组进行进行归一化,然后循环画出直方图。

直方图对比

compareHist()函数

函数原型

double compareHist( InputArray H1, InputArray H2, int method );

第一个参数是输入直方图1

第二个参数是输入直方图2

第三个参数是对比模式

对比模式有:

相关性比较:CV_COMP_CORREL 概率论中的相关系数,绝对值为1时两图相同,为0时完全不相关

卡方比较: CV_COMP_CHISQR 返回值为0时两图相同,为1时两图无关

十字比较:CV_COMP_INTERSECT 取每个位置两者的最小值累加,不是很好用

巴氏距离:CV_COMP_BHATTACHARYYA 两图相同时为1,不相关时为0,相对而言效果最好

样例代码

double compare_12 = compareHist(hist_1 , hist_2 , CV_COMP_BHATTACHARYYA);

直方图对图像灰度比较敏感,要做比较时,最好先将图像转到HVS空间再进行直方图计算,最后进行比较。HSV图像计算直方图时,需要计算的通道为第一和第二通道。

直方图均衡化

equalizeHist()函数

函数原型

void equalizeHist( InputArray src, OutputArray dst );

第一个参数是输入图像,需要是单通道

第二个参数是输出图像

样例代码

equalizeHist(image_in , image_out);

直方图均衡化可以提高不同光照下的图像效果,使画面呈现出更多细节,增强后续处理的效果。

直方图反向映射

函数原型

calcBackProject( const Mat* images, int nimages,const int* channels, const SparseMat& hist, OutputArray backProject, const float** ranges,double scale = 1, bool uniform = true );

第一个参数是图像的地址

第二个参数是图像个数

第三个参数是通道的角标数组

第四个参数是直方图

第五个参数是输出图像

第六个参数是计算范围,与直方图计算里的那个一样

样例代码

int hist_size = 180;	//直方图个数
float range[] = { 0 , 180 };	//统计范围,这里使用的是HSV图像,所以是0-180
const float* ranges[] = { range };
int channels[2] = { 0,1 };	//统计HSV图像的前两个通道
calcHist(&image_hsv, 1, channels, Mat(), image_hist, 1 , &hist_size, ranges);	//计算出直方图
calcBackProject(&image_hsv, 1, channels, image_hist, hist_back, ranges);	直方图反向映射

直方图匹配

matchTemplate()函数

函数原型

void matchTemplate( InputArray image, InputArray templ,OutputArray result, int method, InputArray mask = noArray() );

第一个参数是待检测图像

第二个参数是用来检测的图像

第三个参数是输出图像

第四个参数是匹配模式

匹配模式有:
平方差匹配法:TM_SQDIFF

归一化平方差匹配法:TM_SQDIFF_NORMED //这两个最佳匹配结果在0处

相关匹配法:TM_CCORR

归一化相关匹配法:TM_CCORR_NORMED //这两个最佳匹配结果在最大处

系数匹配法:TM_CCOEFF

归一化相关系数匹配法:TM_CCOEFF_NORMED //这两个最佳匹配结果在1,最差在-1,0为不相关

通常会使用归一化的匹配模式

样例代码

matchTemplate(image_src, temp, temp_out, TM_CCOEFF_NORMED);	//在image_src上寻找与temp图像匹配的部分,并把计算结果输出到temp_out中
normalize(temp_out,temp_out, -1, 1, NORM_MINMAX); //对输出的匹配结果进行归一化
double MinVal = 0, MaxVal = 0;	//用于存储匹配结果中的最大最小值	
Point MinLoc(0, 0), MaxLoc(0, 0);	//用于存储匹配结果中最大值与最小值的坐标
minMaxLoc(temp_out, &MinVal, &MaxVal, &MinLoc, &MaxLoc);		//寻找最值坐标
circle(image_src, Point(MaxLoc.x + temp.cols / 2, MaxLoc.y + temp.rows / 2), 30, Scalar(0, 0, 255), 2, 8);	//在原图上的最佳匹配点处画上圆,要注意如果是用平方差匹配要在最小值处画。

轮廓

轮廓发现

findContours()函数

函数原型

void findContours( InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode,int method, Point offset = Point());

第一个参数是输入的图像,需要是完成Canny边缘检查的图像

第二个参数是输出的轮廓,包含多个轮廓,每个轮廓又包含很多的点,所以一般用vector<vector(Point)>类型的数据进行储存

第三个参数是拓扑信息,里边存储了每个轮廓与其他轮廓的拓扑关系,后一个轮廓 前一个轮廓 父轮廓 内嵌轮廓,一共四个相关轮廓的索引编号,所以一般用vector<Vec4i>进行储存,拓扑信息与每个轮廓一一对应

第四个参数是轮廓检索模式

轮廓检索模式有:

只检测最外围轮廓: CV_RETR_EXTERNAL 内部轮廓被忽略

检测所有的轮廓: CV_RETR_LIST 检测所有轮廓但检测到的轮廓不建立等级关系,相互独立。所以不存在父轮廓和内嵌轮廓。拓扑信息的第三、四位默认为-1

检测所有的轮廓且建立两个等级关系:CV_RETR_CCOMP 不同轮廓间外围为顶层,如果内围轮廓内部也包括其他轮廓,则它也被视作顶层。

检测所有的轮廓且建立等级树: CV_RETR_TREE 会根据轮廓的关系建立等级树,外围包括内围,内围也可包括其他轮廓。

第五个参数是轮廓的呈现方法

呈现方法有:

获取每个轮廓的每个像素:CV_CHAIN_APPROX_NONE 将相互之间距离不超过1的点全部相连

只取轮廓的拐点像素:CV_CHAIN_APPROX_SIMPLE 对信息进行了压缩

teh-Chinl chain 近似算法:CV_CHAIN_APPROX_TC89_L1

CV_CHAIN_APPROX_TC89_KCOS

轮廓绘制

drawContours()函数

函数原型

void drawContours( InputOutputArray image, InputArrayOfArrays contours,int contourIdx, const Scalar& color, int thickness = 1, int lineType = LINE_8, InputArray hierarchy = noArray(), int maxLevel = INT_MAX, Point offset = Point() );

第一个参数是绘制的图像

第二个参数是输入的轮廓信息

第三个参数是轮廓的索引号

第七个参数是拓扑信息

第八个参数是最大层数,0代表只绘制当前 ,1代表绘制当前以及内嵌

第九个参数是每个点的偏移量,一般不设置

样例代码

cvtColor(image_src, image_gray, CV_BGR2GRAY);
Canny(image_gray, image_canny, 40, 140);
Mat image_out(image_canny.size(),CV_8UC3);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(image_canny, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
for(size_t i = 0; i < contours.size();i++)
{ 
drawContours(image_out,contours,i,Scalar(255,0,0),1,LINE_AA,hierarchy);
}
imshow("image_out", image_out);

凸包

convexHull()函数

函数原型

void convexHull( InputArray points, OutputArray hull, bool clockwise = false, bool returnPoints = true );

第一个参数是输入的轮廓数据

第二个参数是输出的凸包数据,数据类型与轮廓数据一样 vector<vector<Point>>

第三个参数是检测的方向,默认为逆时针

第四个参数是操作标识符,一般不用设置

样例代码

vector<vector<Point>> contours ;	//用于存储轮廓数据
vector<Vec4i> hierarchy;	//用于存储轮廓的拓扑信息
findContours(image_canny, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_TC89_KCOS);	//发现轮廓
vector<vector<Point>>	convex(contours.size());	//用于存储凸包信息
for (size_t i = 0; i < contours.size(); i++)
{
convexHull(contours[i], convex[i]);		//检测凸包
}
for(size_t i = 0; i < contours.size();i++)
{ 
drawContours(image_out,convex,i,Scalar(0,0,0),2 , LINE_AA );	//绘制凸包
}
imshow("image_out", image_out);

绘制矩形/椭圆框

压缩点集

approxPolyDP()函数

函数原型

void approxPolyDP( InputArray curve, OutputArray approxCurve,double epsilon, bool closed );

第一个参数是输入点集,一般是轮廓数据

第二个参数是压缩后的点集

第三个参数是压缩精度,代表压缩后曲线与原曲线的最大距离

第四个参数是是否闭合,true是闭合

获得矩形
无角度

boundingRect()函数

函数原型

Rect boundingRect( InputArray points );

参数为点集

返回值为Rect,所以一般用vector<Rect>进行储存

倾斜

minAreaRect()函数

函数原型

RotatedRect minAreaRect( InputArray points );

参数为点集

返回值为RotateRect,包含矩形的四个顶点。一般用vector<RotateRect>进行储存。

需要注意,这个函数对输入点集的个数有要求,至少要有四个点以上。

获得圆
圆形

minEnclosingCircle()函数

函数原型

void minEnclosingCircle( InputArray points, CV_OUT Point2f& center, CV_OUT float& radius );

第一个参数是输入点集

第二个参数是圆心集合

第三个参数是半径集合

椭圆

fitEllipse()函数

函数原型

RotatedRect fitEllipse( InputArray points );

参数是输入点集

返回值与旋转矩形一致,需要注意这个函数的输入点集至少要有六个点,使用时需要进行判断。

样例代码
	//拟合

vector<vector<Point>> approx(contours.size());		//存储压缩后的点集
vector<Rect> rect(contours.size());			//存储拟合的无角度矩形
vector<RotatedRect>	rotate(contours.size());		//存储拟合的有角度矩形
vector<RotatedRect>  elipse_(contours.size());		//存储拟合的椭圆
vector<Point2f> css(contours.size());		//存储拟合圆的圆心
vector<float> rad(contours.size());			//存储拟合圆的半径
for(int i=0 ; i<contours.size() ; i++)		//遍历每一个轮廓点集
{
	approxPolyDP(contours[i], approx[i], 3 , true);		//压缩点集
	rect[i] = boundingRect(approx[i]);		//拟合无角度矩形
	rotate[i] = minAreaRect(approx[i]);		//拟合有角度矩形
	minEnclosingCircle(approx[i], css[i], rad[i]);		//拟合圆
	if (approx[i].size() > 5) {			//确保点集中至少六个点
		elipse_[i] = fitEllipse(approx[i]);		//拟合椭圆
	}
		
}

//绘制

Point2f rotate_point[4];		//用于取用拟合有角度矩形的四个顶点数据
for (int i = 0; i < approx.size(); i++)		//遍历每个轮廓进行绘制
{
	rotate[i].points(rotate_point);		//将顶点数据存入rotate_point
	rectangle(image_src, rect[i], Scalar(0, 0, 0), 3, LINE_AA);			//绘制无角度矩形
	circle(image_src, css[i], rad[i], Scalar(100, 0, 100));			//绘制圆
	ellipse(image_src, elipse_[i], Scalar(0, 100, 100));		//绘制椭圆
	for (int k = 0; k < 4; k++)
	{
		line(image_src, rotate_point[k], rotate_point[(k+1)%4],Scalar(0,0,255));		//使用四个顶点数据绘制旋转矩形,(k+1)%4是为了使矩形闭合且不超出边界
	}

}

图像矩以及轮廓面积/长度计算

图像矩

moments()函数与类

通过轮廓数据计算中心距,可以用来拟合轮廓质心

函数原型

Moments moments( InputArray array, bool binaryImage = false );

第一个参数是输入的轮廓数据

第二个参数是是否进行二值化,如果选true,灰度图中非零点均会被看作1

返回值的数据类型为Moments类,一般用vector<Moments>进行储存。

轮廓面积

contourArea()函数

通过轮廓数据计算轮廓面积

函数原型

double contourArea( InputArray contour, bool oriented = false );

第一个参数是输入的轮廓数据

轮廓长度

arcLength()函数

函数原型

double arcLength( InputArray curve, bool closed );

第一个参数是输入的轮廓数据

第二个参数是指定轮廓是否闭合

样例代码
vector<Moments> moment(contours.size());	//用于存储图像矩
vector<Point2f> points(contours.size());	//用于存储拟合的质心

for(size_t i = 0; i < contours.size();i++)
{ 
	drawContours(image_out, contours, i, Scalar(0, 0, 0), 2, LINE_AA);		//绘制轮廓
	moment[i] = moments(contours[i]);	//根据轮廓计算图像矩
	contourArea(contours[i]);		//根据轮廓计算面积
	//拟合质心,质心的x为图像矩的m10/m00,y为图像矩的m01/m00,static_cast是为了保证数据类型为float
	points[i] = Point(static_cast<float>(moment[i].m10 / moment[i].m00), static_cast<float>(moment[i].m01 / moment[i].m00));	
	circle(image_out, points[i], 2, Scalar(255, 255, 0), 2);		//绘制质心
	cout << "轮廓" << i << "的面积为" << contourArea(contours[i]) << endl << "长度为" << arcLength(contours[i],true)<<endl;		//输出面积以及轮廓长度
}
imshow("image_out", image_out);

点多边形检测

用于检测点与封闭轮廓的位置关系

pointPolygonTest()函数

函数原型

double pointPolygonTest( InputArray contour, Point2f pt, bool measureDist );

第一个参数是输入轮廓数据

第二个参数是检测点

第三个参数是设定是否要返回距离,如果是true则返回值为该点到轮廓的距离(外部为正,内部为负),如果为false,则返回值1、0、-1分别代表轮廓外,轮廓上、轮廓内

分水岭算法

用于分离图像上的不同部分

这部分的代码 我一直编译失败,查了半天资料也没找出来问题,就先放下了,之后具体用到的时候再补上。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值