视觉组寒假任务中 OpenCV 的部分,循序渐进的感觉,其实做的时候还是碰到很多问题,在搜索的过程中也发现了很多有趣快捷的方法。但是赶紧掌握的还是不是很 OK
13 个通道的值统一改为 3 个通道的平均值
通道的值 img.at<Vec3b>(i, j)[0-2]
整体赋值
1
img . at < Vec3b > ( i , j ) = Vec3b ( average , average , average );
图片变灰
2 && 3简单实现之后想用按键控制 threshold
值的增减,更直观的看出变化。
一开始没有多想就直接把修改的那一段放进循环里面,但是发现图像没变化,
后面想起来,从第一次修改之后,通道的值已经发生了变化,图像已经变成黑白的了,此时三通道的数值已经完全相同,不会再改变。
于是就想着再新建一个 Mat src
用来读取图片, Mat img = src
,从 src
读取通道的值,修改 img
的通道值。
本来以为解决了,但是发现还是没变化。之后突然想到 Mat
本应该是没有 =
操作的,应该是重载过了。
了解之后发现 a = b
只是将 a
的各个地址的值改为 b
的,在操作的时候两者会一起改变。
感觉 =
相当于是引用,而真正地复制可以用 a = b.clone()
b.CopyTo(a)
。
clone()
实际上是新建一个 Mat m
然后执行 CopyTo
,再返回 m
。
4
1
2
3
4
5
6
7
// 通道分离
split ( src , channels );
channels . at ( i ) // 012 BGR
// 通道合并
merge ( channels , mergeIMG );
5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 读取摄像头
VideoCapture capture ( 0 );
capture >> frame ;
// 判断摄像头是否打开
capture . isOpened ()
// 判断图片是否读取成功
frame . empty ()
bool Mat :: empty () const {
return data == 0 || total () == 0 || dims == 0 ;
}
// 在图像上显示文字
putText ( img , text , position , fontFace , fontScale , color );
// 获取帧率
capture . get ( CAP_PROP_FPS )
6
1
2
3
4
5
6
7
8
9
10
11
12
13
// thickness < 0 颜色填充
void circle ( InputOutputArray img , Point center , int radius ,
const Scalar & color , int thickness = 1 ,
int lineType = LINE_8 , int shift = 0 );
void line ( InputOutputArray img , Point pt1 , Point pt2 , const Scalar & color ,
int thickness = 1 , int lineType = LINE_8 , int shift = 0 );
// (Point pt1, Point pt2) -> Rect(x, y, width, height)
void rectangle ( InputOutputArray img , Point pt1 , Point pt2 ,
const Scalar & color , int thickness = 1 ,
int lineType = LINE_8 , int shift = 0 );
7Gamma 矫正
1
2
3
float f = ( i + 0.5 ) / 256 ; // 归一化
float p = pow ( f , fPrecompensation ); // 预补偿
GammaTable [ i ] = p * 256 - 0.5 ; // 反归一化
从显示效果上来看可以让人眼所看到的黑白对比增加,应该是相当于增加了对比度吧,在应用中的化可以让我们看黑暗中的东西看得更清楚
8HSV 转换,颜色提取
1
2
3
cvtColor ( src , HSVMat , COLOR_BGR2HSV );
inRange ( hsvMat , Scalar ( minH , minS , minV ), Scalar ( maxH , maxS , maxV ), detectMat );
9腐蚀、膨胀、开闭运算
1
2
3
4
5
6
7
8
9
10
// 获取结构元素
// shape: MORPH_RECT MORPH_CROSS MORPH_ELLIPSE
// Size: Size(x, y)
Mat getStructuringElement ( int shape , Size ksize , Point anchor = Point ( - 1 , - 1 ));
// erode 腐蚀 dilate 膨胀 kernel 结构元素
void erode ( InputArray src , OutputArray dst , InputArray kernel ,
Point anchor = Point ( - 1 , - 1 ), int iterations = 1 ,
int borderType = BORDER_CONSTANT ,
const Scalar & borderValue = morphologyDefaultBorderValue () );
开运算:先腐蚀后膨胀
闭运算:先膨胀后腐蚀 欲使其自闭,必先让其膨胀
相对亮的区域来说,
开运算,把小的断断续续的亮处给断开,这就需要先通过腐蚀去掉小亮点,再通过膨胀恢复原来的大片连续亮区。
闭运算就是把原来断断续续的亮区给闭合,这就需要通过膨胀扩大亮区,之后通过腐蚀恢复大片连续暗区
10 && 11threshold
1
2
3
4
// 二值化
// type: enum ThresholdTypes
double threshold ( InputArray src , OutputArray dst ,
double thresh , double maxval , int type );
形态学梯度:膨胀图与腐蚀图之差,提取物体边缘
顶帽:原图像 - 开运算图,突出原图像中比周围亮的区域
黑帽:闭运算图 - 原图像,突出原图像中比周围暗的区域
1
2
3
4
5
6
7
8
9
10
11
12
13
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 () );
/* op:
* MORPH_OPEN – 开运算
* MORPH_CLOSE – 闭运算
* MORPH_GRADIENT - 形态学梯度
* MORPH_TOPHAT - 顶帽
* MORPH_BLACKHAT - 黑帽
*/
1
2
// 用指针遍历
int * t = stats . ptr < int > ( i );
1
2
3
4
5
// 返回连通块个数
// stats nx5 四边形的 x y width height
int connectedComponentsWithStats ( InputArray image , OutputArray labels ,
OutputArray stats , OutputArray centroids ,
int connectivity = 8 , int ltype = CV_32S );
12
1
2
3
blur ( frame , blurMat , Size size );
medianBlur ( frame , medianBlurMat , int size );
GaussianBlur ( frame , GaussianBlurMat , Size size , x , y );
13Canny
1
Canny ( grayMat , edgeMat , edgeThresh , edgeThresh * 3 );
14最开始直接尝试各种滤波,但是除了脸部区域其他地方也会变模糊。
然后尝试连通块检测,想找到脸部区域的连通块,仅对脸部区域滤波。最开始碰到的问题就是太多小个连通块了,不知道脸部的连通块是第几个,后面想到用 GaussianBlur
可以把小的全部先过滤了。
获得连通块滤波之后需要叠加,一开始只想到遍历整个图像进行连通块区域的覆盖,后面搜到可以直接
1
ROI = src ( Rcet ( x , y , width , height ))
操作 ROI
的时候 src
中相对于区域也回改变。但是发现用 medianBlur
的时候是可行的,换成了 bilateralFilter
就失效了,src
中的区域不会改变,即使我之后再把滤波的结果拷贝给 ROI
也不行,现在还不知道为什么
但是这个方法操作的是一个矩形,并不是单纯的脸部,还是会有其他区域模糊的问题。搜索之后又找到一个新的方法:
通过连通块检测得到的 labelsMat
对脸部区域做一个 mask
,脸部为 255(黑),其他区域为 0(黑),然后通过 bitwise_and
获取 src
的脸部
1
bitwise_and ( src , maskMat , faceMat );
这样出来的脸部就不再是一个矩形了,这样滤波再叠加就基本没问题了,在叠加的时候也发现了 copyTo 的新用法
1
2
// mask 和 image 重叠以后把 mask 中像素值为 0(黑色)的点对应的 image 中的点变为透明,而保留其他点
image . copyTo ( dst , mask )
Final