opencv库函数学习笔记

基本操作

1. 读写图像文件:

src = imread("图像路径(文件夹斜杠是/,直接复制过来的是错的)");
if (src.empty())
{
	printf("这什么狗屁图片打不开\r\n");
	return -1;
}

imwrite("图像路径", dst);

srcdstMat对象。if语句用于判断图像是否正常打开。

2. 打印图像:

 namedWindow("output", WINDOW_AUTOSIZE);
 imshow("output", dst);

output是输出窗口名字,不人工新建也没关系,opencv会自己建一个;dst也是Mat对象。

3. 图像线性叠加

addWeighted(src1, alpha, src2, beta, gamma, dst);

src1src2是两张加数图片,图像长宽像素必须完全一致。alphabeta是加权系数。一般她们的和取为1。gamma是常数。这个函数的作用可以由公式表示:
d s t ( i ) = a l p h a × s r c 1 ( i ) + b e t a × s r c 2 ( i ) + g a m m a dst(i)=alpha\times src1(i)+beta\times src2(i)+gamma dst(i)=alpha×src1(i)+beta×src2(i)+gamma

4. 图像相减

subtract(src2, src1, dst, Mat());

src2是被减数,src是减数,dst是差。

5. 图像归一化

normalize(src, dst, max, min, NORM_MINMAX);

将图像像素灰度归一化到上下限规定的值以内。(感觉max和min就算交换一下也没什么影响) 最后一个参数是归一化类型,一般都用NORM_MINMAX

6. 像素范围处理

saturate_cast<uchar>(像素值);

就是对像素限幅,0-255

7. 指针遍历图像

for (int i = 0; i < src.rows; i++)//遍历每一行
{
	uchar* point = src.ptr<uchar>(i);
	for (int j = 0; j < src.cols * src.channels; j++)//遍历每一列
	{
		point[j];//当前像素点
		//亦可以用src.ptr<uchar>[i][j];
	}
}

8. 轨迹条操作

createTrackbar("轨迹条名", "输出窗口名", int* value, int count, Call_Back);

value是轨迹条默认值,count是轨迹条最大值,Call_Back是回调函数。这个函数的入口参数之一必须是void*。回调函数可以设置为Null。
每一次滑动轨迹条的时候都会调用回调函数,执行其中的代码。回调函数结尾可以弄个return;。使用轨迹条操作之前注意变量和函数的声明。
轨迹条value取值范围感觉最好不要弄太大,要不然会很卡。

9.图像绘制

定点

Point p;
p.x = 数值;
p.y = 数值;
或者
p = Point(x, y);

坐标原点从左上角开始。其他定义直角坐标系一样。

画几何图形

。。。

10.通道分割和合并

vector<Mat> channels;
split(image1, channels);//分割image1的通道
Mat channels1 = channels[0];//获取通道1
Mat channels2 = channels[1];//获取通道2
Mat channels3 = channels[2];//获取通道3

channel[0]、[1]、[2]按照 b、g、r 成分的顺序分割。

Mat MultiImage;
merge(channels, MultiImage);

合并是这个样子。合完之后应该跟拆分之前一毛一样的。

图像预处理

1. 亮度和对比度

原理很简单,可由如下公式表示: d s t ( i ) = a l p h a × s r c ( i ) + b e t a dst(i)=alpha\times src(i)+beta dst(i)=alpha×src(i)+beta

dst.at<Vec3b>(i, j)[0] = saturate_cast<uchar>(alpha*src1.at<Vec3b>(i, j)[0] + beta);
dst.at<Vec3b>(i, j)[1] = saturate_cast<uchar>(alpha*src1.at<Vec3b>(i, j)[1] + beta);
dst.at<Vec3b>(i, j)[2] = saturate_cast<uchar>(alpha*src1.at<Vec3b>(i, j)[2] + beta);

<Vec3b>是一种长度为3的向量数据类型;alpha是像素点系数,其值>0。如果alpha>1那么亮度和对比度提高,<1的话对比度和亮度是降低的。beta就是增加或者减小亮度的。如果是单通道的灰度图像,那就不需要分成三个数组来写了。

这个是像素点操作,需要配合遍历图像算法来完成操作。

还有一种更加简单的方法:

src.convertTo(dst, -1, alpha, beta);

-1代表图像深度和原来相同。

2. 色彩空间转化

cvtColor(src, srcInGray, COLOR_BGR2GRAY);

3.图像尺寸变化

resize(src,dst,Size(x像素,y像素),double x变化倍数,double y变化倍数,int interpolation);

可以通过Size直接指定放缩之后图像像素大小,也可以通过放缩倍数来调节。确定Size之后,后面的xy可以直接置零,反过来也是。
最后的int参数是插值方法,一共有5种:

  • INTER_NEAREST - 最近邻插值法
  • INTER_LINEAR - 双线性插值法(默认)
  • INTER_AREA - 基于局部像素的重采样
  • INTER_CUBIC - 基于4x4像素邻域的3次插值法
  • INTER_LANCZOS4 - 基于8x8像素邻域的Lanczos插值

图像放大的时候最好用INTER_LINEAR ,缩小时最好选 INTER_AREA

3. 平滑

平滑滤波方式很多种,基本原理都是掩膜操作。

3.1 均值滤波

blur(src, dst, Size(n,m), Point(-1, -1));

Size(n, m)是卷积核的大小,越大平滑效果越强烈。一般n、m都取奇数。

3.2 高斯滤波

GaussianBlur(src, dst, Size(3, 3), 0, 0);

Size可以自己调节大小,必须是奇数。越大模糊效果越强。最后两个参数是默认的 0, 0。

3.3 中值滤波

medianBlur(src, dst, 3);

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

3.4 双边滤波

相当于平原和高地分别滤波,保留了悬崖,即图像中明显边界。

bilateralFilter(src, dat, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT);

第三个参数是邻域半径,就是滤波的邻域范围;
第四个参数是颜色空间过滤器的sigma值,这个参数的值月大,表明该像素邻域内有月宽广的颜色会被混合到一起,产生较大的半相等颜色区域;
第五个参数是坐标空间中滤波器的sigma值,如果该值较大,则意味着颜色相近的较远的像素将相互影响,从而使更大的区域中足够相似的颜色获取相同的颜色。
最后一个参数是边界条件,有默认值不用管。

4. 锐化

Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); 
filter2D(src, dst, -1, kernel, Point(-1, -1));

先建立一个卷积核kernel,然后用filter2D函数(源,目标,深度(默认-1即可),卷积核,锚定点(默认-1,-1))

5. 均值漂移

bilateralFilter(src, dst, double sp, double sr,  int maxLevel);

第三、四个参数是漂移的物理空间和色彩空间大小。这个函数需要迭代运算的,轨迹条跑的有点慢。难受。

6. 二值化(阈值处理)

threshold(src, dst, threshold, max, THRESH_BINARY);

两个double数字分别代表阈值,灰度最大值。然后最后一个参数是阈值类型:第五个参数,int类型的type,阈值类型:

THRESH_BINARY 当前点值大于阈值时,取Maxval,也就是第四个参数,下面再不说明,否则设置为0

THRESH_BINARY_INV 跟上面相反,当前点值大于阈值时,设置为0,否则设置为Maxval

THRESH_TRUNC 当前点值大于阈值时,设置为阈值,否则不改变

THRESH_TOZERO 当前点值大于阈值时,不改变,否则设置为0
THRESH_TOZERO_INV 当前点值大于阈值时,设置为0,否则不改变

7. 形态学操作

6.1 形态结构:

Mat struct = getStructuringElement(MORPH_RECT, Size(3,3), Point(-1, -1));

形态学操作之前,先建立一个struct核,包括结构的形状:

  • 矩形:MORPH_RECT
  • 交叉形:MORPH_CROSS
  • 椭圆形:MORPH_ELLIPSE

然后尺寸,尺寸的设置在开闭操作当中很有用。

7.2 腐蚀和膨胀

先建立一个结构对象,然后

erode(src,dst,struct);//腐蚀
dilate(src, dst, struct);//膨胀

腐蚀是图像中深色向浅色侵蚀。膨胀是图像中浅色向深色扩张。

7.3 高级形态学变化

包括开操作,闭操作,形态学梯度,礼帽,黑帽。
开操作就是先腐蚀后膨胀,闭操作相反。可以用一个封装好的函数进行操作:

morphologyEx(src, dst, CV_MOPOPEN, struct);

MORPH_ERODE = CV_MOP_ERODE,
MORPH_DILATE = CV_MOP_DILATE,
MORPH_OPEN = CV_MOP_OPEN,
MORPH_CLOSE = CV_MOP_CLOSE,
MORPH_GRADIENT = CV_MOP_GRADIENT,
MORPH_TOPHAT = CV_MOP_TOPHAT,
MORPH_BLACKHAT = CV_MOP_BLACKHAT

开操作可以将深色区域中的亮色小洞洞消掉。闭操作就是反过来,将白色背景里的黑点去掉。如果要去掉的部分有特定的形状,比如水平线,竖直线,那就将核的形状设成跟要去掉的东西一样。比如如果要去掉水平/竖直线,struct的形状可以设成矩形,size设成一个长条。

8. 图像金字塔

图像金字塔顾名思义,就是通过处理得到的一堆大小不同的图像一张张叠起来,空间中看就像个金字塔一样。

得到图像金字塔分为两步,第一步先对图像进行高斯模糊,然后删除/增加行列。

8.1 上采样

pyrUp(src, dst, Size(src.cols * 2, src.rows * 2));

每个像素点复制,可以将图片放大

8.2 降采样

pyrDown(src, dst, Size(src.cols / 2, src.rows / 2));

隔一行/列删除,将图片缩小。

8.3 高斯不同

就是把同一张图像在不同的参数下做高斯模糊之后的结果相减,得到的输出图像。称为高斯不同(DOG)

高斯不同是图像的内在特征,在灰度图像增强、角点检测中经常用到。

GaussianBlur(g1, g2, Size(3, 3), 0, 0);
GaussianBlur(g2, g3, Size(3, 3), 0, 0);
subtract(g2, g3, dog, Mat());

将高斯模糊的size增大,高斯不同会更加明显。

8. 边缘检测

8.1 Sobel算子

横向: [ − 1 − 2 − 1 0 0 0 1 2 1 ] \begin{bmatrix} -1 & -2 &-1\\ 0&0&0\\1&2&1 \\ \end{bmatrix} 101202101,可以检测水平方向的边缘;纵向的话就是这个矩阵转置,检测竖直边缘。

Sobel(src, dst, CV_32F, int x, int y, 3, 1, 0, BORDER_DEFAULT);

src为灰度图像,第三个参数是图像深度,有CV_16/32/64等等很多,-1表示和,但这里最好设置成比原图深度大。后面两个参数表示x、y方向差分的阶数,再后面一个是卷积核大小,还是只能为奇数,一般3。最后三个是缩放系数、灰度增加常数,还有个不知道,反正都有默认值,可以直接不用。
关于x、y方向的阶数,可以一起差分,也可以分开差分然后再将得到的结果叠加,这两种方法得到的结果会有细微差别。
Sobel算子噪声敏感,所以前面一般加上高斯滤波。后面为了防止差分之后结果超限,一般也加上限幅。

8.2 Laplace算子

图像求二阶导,也可以用来检测边缘。跟上面的很相似。

Laplacian(src, dst, CV_16F, 3);

src是灰度图,第三个参数是深度,然后卷积核大小,后面其实也有三个参数,可缺省。
拿bliss试了一下,结果贼tm奇葩。

8.3 Canny边缘检测

Canny(src, dst, threshold1, threshold2, 3, false);

src必须是8位图,threshold1threshold2是下限、上限两个阈值,一般设置成1:2或者1:3的关系,后面两个默认值不用管。在Canny之前,需要先滤波和灰度转换。不模糊也没关系,Canny自带。
Canny算法其实分为3步:

  1. Sobel算法计算梯度
  2. 非最大信号抑制
  3. 按阈值输出二值图像

第一步,计算梯度之前必须平滑降噪,因为噪声对梯度计算影响很大,canny自带的滤波有可能不靠谱;第二步非最大信号抑制,实际上就是得到边缘之后,沿着梯度法向方向,把不是最大值的地方去掉,把本来比较宽的边缘变窄一点;完了之后还要使用双阈值筛选,输出一个二值图像,具体方法是:高于第二个阈值的保留,然后从高于阈值的点出发,寻找亮度不断降低的像素点,直到亮度低于第一个阈值为止,低于第一个阈值的就直接去掉。

9.霍夫变换

这概念好特喵的难懂
笛卡尔坐标系内,一条直线可以由斜截式表达: y = a x + b y=ax+b y=ax+b这个式子里面, x x x y y y是变量, a a a b b b是参数,如果固定 x x x y y y而改变 a a a b b b,就可以得到经过同一点的一组直线;如果固定 a a a b b b而反过来改变 x x x y y y,得到的就是同一根直线上不同的点点。

如果把 a a a b b b当做变量, x x x y y y当做参数,那就将 x x x y y y坐标空间变成了 a a a b b b参数空间,这种变换就是直角坐标系下的霍夫变换。变换之后,经过同一点的一组直线,变成了同一根直线上不同的点。

根据对称性原则可以推出来,某一空间内多条经过同一点的一组曲线,霍夫变换之后就是一条直线。如果霍夫变换发生在极坐标系,上面这个法则依然成立。极坐标下,直线霍夫变换之后会变成经过同一点的一组曲线。这一组曲线有几根,就意味着这条直线有多长(多少个像素点)。这就是霍夫变换直线检测的原理。
霍夫原检测一样。直角坐标系内确定一个圆需要 a a a b b b r r r三个参数。变换到参数坐标系内,就变成经过点 ( a , b , r ) (a, b,r) (a,b,r)的一组曲面。变换之后的参数空间变成了三维的,计算难度加大。因此实际的算法中,通常不采用这种,而是另一种方法:圆心一定在圆上各点切线的法线上。因此在边缘图像上寻找梯度曲线的交点,交点越多说明就越有可能是圆心,然后根据圆心找半径。因此其准确性很依赖第一步圆心的寻找。

9.1 霍夫直线检测

vector<Vec4f> plines;
HoughLinesP(srcInGray, plines, 1, CV_PI / 180.0, threshold, minLenth, maxGap);

直线检测必须在canny边缘检测的基础上完成。输入是8位灰度图,输出直线极坐标;后面两个参数是扫描步长,一般就默认这么多;最后几个参数是:阈值,最小直线长度,最大直线间隔。
vector容器类需要using namespace std;
阈值就是经过同一交点的直线根数,代表直线长度;最小直线长度默认设为0;最大间隔就是距离很近(最小间隔)的几条线会被认为是同一根线。

Scalar color = Scalar(0, 0, 0);
for (int i = 0; i < plines.size(); i++)
{
	line(dst, Point(plines[i][0], plines[i][1]), Point(plines[i][2], plines[i][3]), color, 1, LINE_AA);
}

最后可以用这个函数把找到的直线显示出来。

9.2 霍夫圆检测

vector<Vec3f> pCircles;
HoughCircles(src, pCircles, int HOUGH_GRADIENT, double dp, double minDist, double para1, double para2, int minRadius, int maxRadius);

参数很多。前两个是输入输出。第三个室检测方法。一般用HOUGH_GRADIENT这个。第四个是图片缩小,一般取1。第5个是分辨率,同心圆之间的最小距离。小于这个距离就认为是同一个园。第6个是canny边缘检测的低阈值。第7个是候选圆心累加的阈值。最后两个是找到的园的最大最小半径。
Canny阈值和累加阈值越高,检测要求越严格。

10.直方图操作

不仅每个像素,其实图像梯度等所有的图像统计数据都可以建立直方图。

10.1 直方图均衡化

equalizeHist(src, dst);

src必须是8位单通道图像。直方图均衡化就是把直方图在灰度刻度上弄得分布更加均匀,具体计算方法设计复杂的概率论知识,可惜之前学的概率论已经全忘光了。
可用于增强对比度。

10.2 直方图计算

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值