1.图像处理方法.txt

UP 返回
视频地址:https://www.bilibili.com/video/BV1Qt4y1b79Z?p=21&spm_id_from=pageDriver&vd_source=d4b23777458c9f0c9e0eb065a7a765b9
			清华博士带你做项目!【OpenCV+TF2.0】深度学习计算机视觉项目实战教程!究极通俗易懂!(神经网络/图像处理/目标检测)
项目地址:D:\ProjectCodes\VS_Code\testOpenCV
对应视频:P1-P80

1.openCV环境搭建
	查看pip版本
	python -m pip --version
	!!@@202207111.png_604_47_1@@!!
	
	如果pip版本过低,需要升级:
	python -m pip install --upgrade pip
	
	通过pip安装numpy matplotlib opencv-python
	python -m pip install numpy matplotlib opencv-python

2. 图像矩阵(视频P37以前)
  2.1 初期方法
  	  窗口显示:win.py
		cv2.namedWindow('new', cv2.WINDOW_NORMAL)
		cv2.imshow('new',0)
	  图片显示:testopencv.py
		img = cv2.imread('D:\\TestFolder\\pinyougou_freemarker\\img\\ad.jpg')
		cv2.imshow('img', img)
	  图片处理:readimage.py
		cv2.imwrite('D:\\TestFolder\\fold1\\test001.png', img)
	  视频处理:capvideo.py	包括摄像头/文件的视频显示,保存
		fourcc = cv2.VideoWriter_fourcc(*'MJPG') #创建VideoWriter为写多媒体文件 fourcc表示用四个字符代表编码,编码有多种
		vw = cv2.VideoWriter('./out.mp4', fourcc, 25, (640, 480)) #参数:输出文件 文件格式 帧率 分辨率;最后参数分辨率需要与摄像头的分辨率一直 否则可能写入失败
		cap = cv2.VideoCapture(0) #获取视频设备 0直接从摄像头读取视频;文件路径即可读取文件视频
		ret, frame = cap.read() #从摄像头读视频帧。read返回2个:状态值,读到帧为true 视频帧 
		vw.write(frame) #写数据到多媒体文件
	  鼠标事件:mouse.py
		鼠标事件event与flag参数的定义源码路径:modules/highgui/include/opencv2/highgui.hpp
			cv2.setMouseCallback('mouse', mouse_callback, "123")
	  trackBar控件:trackbar.py
		cv2.createTrackbar('R', 'trackbar', 0, 255, callback) #创建trackbar:trackbar名 窗口名 默认初始值 最大值 回调函数
		r = cv2.getTrackbarPos('R', 'trackbar') #获取trackbar数值

  2.2 图像的色彩空间:color.py
	OpenCV默认使用颜色为BGR的排序
	!!@@202207161.png_763_450_0.5@@!!	!!@@202207162.png_750_446_0.5@@!!
	OpenCV使用的色彩空间为HSV/HSB
		Hue:色相,即色彩,如红色蓝色
		Saturation:饱和度,颜色的纯度
		Value:明度
		!!@@202207163.png_430_384_0.5@@!!	!!@@202207164.png_371_388_0.5@@!!
		!!@@202207167.png_366_386_0.5@@!!
		Hue相当于绕一圈对应不同颜色;Saturation表示从中心点向外,越靠外面颜色越饱和越浓,中间都成白色了;Value表示亮度,越往上越亮
	还有一种色彩空间HSL,用的比较少,一般用于CSS等api中,对比如下:
		!!@@202207168.png_688_364_0.5@@!!
	视频中的色彩空间为YUV,有多种类型,比如YUV4:4:4,表示三种比例,不同的比例即可对应不同类型。黑白电视只会读Y数据,彩色电视就会把YUV全部读取,所以YUV是为了兼容Y的
		!!@@202207169.png_530_345_0.5@@!!	
		4:2:0表示4个Y有2个U或者2个V,如果是2个U那第三个就是V,如果是2个V第三个就是U
  
  2.3 图片矩阵 矩阵区域选取 :testnumpy.py
	OpenCV中用到的矩阵都要转化为Numpy数组,Numpy是一个经高度优化的python数值库。Numpy创建矩阵:
		创建数组 array();创建全0数组 zeros() / ones();创建全值数组 full()
		创建单元数组 identity() / eye() 前者创建的都是正方形,后者可以创建矩形
			a = np.array([1, 2, 3])	#一维  多维数组
			b = np.array([[1, 2, 3],[4, 5, 6]])
			c = np.zeros((8, 8, 3), np.uint8)	e = np.ones((8, 8, 3), np.uint8)	#zero矩阵 ones矩阵。rgb三个通道,故对应3个矩阵
			d = np.zeros((8, 8), np.uint8)	f = np.ones((8, 8), np.uint8)
			g = np.full((8, 8), 10, np.uint8)		#full矩阵 指定值
			h = np.identity(8)	#identity单位矩阵 斜对角为1其他都是0
			i = np.eye(5)
			j = np.eye(5, 7)
			k = np.eye(5, 7, k = 3)		#k = 3可以控制从第几个赋值为1
			print(img[100, 100])	#图像矩阵的访问是[y,x],顺序刚好是反+的
	Mat属性: usemat.py		usemat2.py
  		!!@@2022071610.png_631_267_0.5@@!!	8UC3:每个元素占8位,数据类型为U(uint 无符号整型),C代表channel,3表示3个通道
		上面这是header的数据结构,其中data存放了实际的矩阵地址。普通的赋值就是复制了header,但是里面的data指向的是同一个矩阵,即浅拷贝
			img2 = img		#这种赋值即为浅拷贝
			img3 = img.copy()	#python使用copy即为深拷贝
	图片通道的分割与合并:spmerge.py
		b,g,r = cv2.split(img)
  2.4 画图 :draw.py	drawshape.py		源码地址 modules/imgproc/include/opencv2/imgproc.hpp
		#画线,坐标点为 (x,y) 和之前的相反
		#参数:图片 线起始位置 线结束位置 颜色 线宽 线型(越大线条越平滑饱满,取值有-1 4 8 16)
		cv2.line(img, (10, 20), (300, 400), (0, 0, 255), 5, 16)
		
		#画矩形 -1表示填充
		cv2.rectangle(img, (10,10), (50,100), (0,0,255), -1)
		
		#画圆
		cv2.circle(img, (320, 240), 100, (0,0,255))
		cv2.circle(img, (320, 240), 5, (0,0,255), -1)
		
		#画椭圆:img 中心点 长宽的一半 角度 从那个角度开始 到哪个角度结束 颜色...
		#度是按顺时针计算的,0度从右侧开始。参数角度表示图形的旋转
		cv2.ellipse(img, (320, 240), (100, 50), 90, 0, 360, (0, 0, 255), -1)
		
		#画多边形:img 点集 是否闭环 颜色
		#画多边形也可以一条条线去画,但是效率没这个方法高;多边形不可以通过-1去填充
		pts = np.array([(300, 10), (150, 100), (450, 100)], np.int32) #点集必须是32位
		cv2.polylines(img, [pts], True, (0, 0, 255))
		cv2.fillPoly(img, [pts], (255, 255, 0)) #多边形的填充
		
		#画文本:img 字符串 起始点 字体 字号 
		cv2.putText(img, "Hello World", (10, 400), cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0))
	
3.图像处理(视频)
  3.1 图像运算
	加减乘除:imgadd.py
		加类似于加大曝光,图片更亮,减图片就会更暗;乘除就是使图像变亮变暗的更快
			add(A,B)	subtract(A,B)	multiply(A,B)	divide(A,B)
	图像融合:add2.py
		addWeighted(A,α,B,β,γ)		图像A和B;α和β表示融合后两张图片的权重,占比高的显示的更清晰;γ静态权重,即把融合后的图像所有像素都加上这个权重
	图像位运算:bit.py	addlogo.py
		bitwise_not(img)	非	可以让黑色变白白色变黑
		bitwise_and(img1, img2)	与	两张图片都含有白色区域,与操作以后只有重叠的部分才会保留白色
		bitwise_or(img1, img2)	或	两张图片都含有白色区域,或操作以后只要有白色就仍然是白色
		bitwise_xor(img1, img2)	异或	两张图片都含有白色区域,异或操作以后只要有白色就仍然是白色,但是重叠的部分又会变黑
  3.2 图像变形
	缩放:scale.py
		resize(src, dst, dsize, fx, fy, interpolation)		参数解释如下:
			被放大的图像
			目的图像	python中可以不设置这个参数
			目标尺寸(缩放的大小)
			x轴的缩放因子
			y轴的缩放因子(即对应轴长度变为以前的多少倍,这两个参数与dsize会有冲突,一般指定一种即可,都指定以dsize为准)
			插值缩放算法:
				INTER_NEAREST 邻近插值,速度快效果差,放大的时候直接在原像素边上拷贝一个,放得越大差的效果越明显
				INTER_LINEAR 双线性插值,以原图的4个点作为参考生成像素点,比上面效果好,速度也很快。默认算法
				INTER_CUBIC 三次插值,以原图的16个点作为参考,效果更好,耗时更多
				INTER_AREA 参考的点更多,效果最好,最慢
	翻转:flip.py
		flip(img, flipCode)		flipCode:==0 上下;>0 左右;<0 上下+左右
	旋转:rotate.py
		rotate(img, rotateCode)	rotateCode:ROTATE_90_CLOCKWISEE,ROTATE_180,ROTATE_90_COUNTERCLOCKWISE
	仿射变换:affine.py
		图像的仿射变换是旋转 缩放 平移的总称
		warpAffine(src, M, dsize, flags, mode, value)
			M		变换矩阵。如果是平移,就是一个2*3的矩阵
			dsize 	输出尺寸大小		
			flag 	与resize中的插值算法一致
			mode	边界外推法标志
			value	填充边界的值
	获取仿射变换矩阵
		getRotationMatrix2D(center, angle, scale)	
			center		图片要旋转的中心点
			angle		围绕中心点旋转的角度,逆时针
			scale		缩放比例
		getAffineTransform(src[], dst[])	通过三个点的原位置和变换后的位置,即可以确定所有的变换
	透视变换:prespective.py
		warpPerspective(img, M, dsize, ...)
			M		变换矩阵。是一个3*3的矩阵
	获取透视变换矩阵
		getPersectiveTransform(src[], dst[])	这里需要四个点确定变换位置,即将倾斜图片变正时四个角对应的点
  3.3 图像滤波
	1) 相关概念
	滤波是指一幅图像通过滤波器得到另一幅图像;其中滤波器又称为卷积核,滤波的过程成为卷积
	!!@@202207181.png_618_292_0.5@@!!		滤波就是对矩阵做乘积,可以看到滤波以后图像更加锐化了
	!!@@202207182.png_297_397_0.5@@!!		滤波如果不对图像做处理,那么得到的图像一定是更小的,所以一般还要有边界扩充
	
	卷积核一般为奇数,如3*3 5*5 7*7等,一方面是为了增加padding的原因,即维持卷积后的图片尺寸;另一方面是保证锚点在中间,防止位置发生偏移。
	在深度学习中,卷积核越大,看到的信息(感受野)越多,提取的特征越好,同时计算量也就越大

	当卷积核大于1且不进行边界扩充,输出尺寸将相应缩小。当卷积核以标准方式进行边界扩充,则输出数据的空间尺寸将于输入相等
	!!@@202207183.png_320_397_0.5@@!!		可以看到再给原图补充边界以后,输入输出的大小都是5*5的图像了
	边界扩充的计算公式:N = (W - F + 2P)/S + 1		N输出图像大小 W原图大小 F卷积核大小 P扩充尺寸 S步长大小

	!!@@202207184.png_624_357_0.5@@!!	步长是指两次对应需要移动的间距,图中这个就是步长为2

	低通滤波可以去除噪音或平滑图像,比如人脸美白磨皮等;高通滤波可以帮助查找图像的边缘,比如抠图
	高通滤波可以帮助提取边缘,常见的高通滤波有3种算子:
		Sobel 索贝尔 通过高斯滤波以后再求导,抗干扰能力很强;很多其他高通滤波也是以此滤波为基础的
		Scharr 沙尔 卷积核不变尺寸固定3*3的大小,将Sobel的卷积核的size设为-1时自动就会使用该滤波,所以一般不会主动用这个方法。单纯的3*3的卷积核时,Sobel没有Scharr效果好
		Laplacian 拉普拉斯。上面两个滤波都只能在一个轴上卷积,卷积结果还需要相加;Laplacian则可以横纵两轴同时检测出来,但是对噪音比较敏感,所以使用前需要手动降噪

	2)代码操作
	图像卷积:filter.py
		filter2D(src, ddepth, kernel, anchor, delta, borderType)
			ddepth		卷积后输出图像的位深,比如32 16 8等,一般设置为-1,表示和原始图像一致
			kernel		卷积核
			anchor		锚点,即核的中心点。默认为(-1, -1),也可以不设,表示根据核的内容自动找到中心点
			delta		每一次卷积需要加上的值,默认为0,,可以不管
			bordrType	边界类型,比如加黑边。一般也不处理使用默认值
		!!@@202207186.png_270_160_0.5@@!!		比如这个卷积就是将所有值都乘1相加再除以25,就可恶意消除峰值,使得图像变平滑
	方盒滤波与均值滤波:filter.py		源码位置:modules/imgproc/include/opencv2/imgproc.hpp
		!!@@202207187.png_276_252_0.5@@!! 	参数a:方盒滤波有一个参数normalize,如果为true, a = 1/(W*H), 此时就是平均滤波; 如果为false, a = 1           
		boxFilter(src, ddepth, ksize, anchor, normalize, borderType)       方盒滤波
			ksize	卷积核的大小                            ,
			normalize	上述true/false参数
		blur(src, ksize, anchor, borderType)		均值滤波。上面的方盒滤波一般和下面都是等同的,只要normalize为false
	高斯滤波( 又称锥形滤波 )		用于处理高斯噪点
		!!@@202207188.png_468_352_0.5@@!!	!!@@202207189.png_343_311_0.5@@!! !!@@2022071810.png_455_341_0.5@@!! 越靠近中心的比重越高( 图上把高斯写错了 )
		GaussianBlur(img, kernel, sigmaX, sigmaY, ...)	sigmaX表示x方向上延展的宽度,即追锥形波边缘到中心点的距离。没有该参数时以kernelSize为准
	中值滤波( 滤波时,对方框选中的区域中的值进行排序,以中间值作为输出图像的对应元素 )		对胡椒噪音效果明显
		!!@@202207201.png_317_378_0.5@@!!	图中的噪点就像胡椒撒在上面一样
		medianBlur(img, ksize)
	双边滤波	可以保留边缘,同时可以对边缘内的区域进行平滑处理;作用就是美颜
		!!@@202207202.png_461_385_0.5@@!!
		bilateralFilter(img, d, sigmaColor, sigmaSpace, ...)		d直径,可以理解为filter的大小。两个sigma对应于图中空域核和值域核,即什么样的颜色作为边缘不处理,什么样的值需要平滑处理
	Sobel算子:sobel.py		先在x方向求导,再在y方向,最终将两个结果相加获得	
		Sobel(src, ddepth, dx, dy, ksize = 3, scale = 1, delta = 0, borderType = BORDER_DEFAULT)
			ddepth 输出位深
			dx = 1表示在x轴求导;dy表示y轴
			ksize 即为卷积核大小,置为-1时变为Scharr算子
			scale用于结果缩放,delta用于结果增量,borderType用于结果的边缘类型,这几个参数一般不使用
	Scharr算子		一般不用,直接用上面的那个方法就行了
		Scharr(src, ddepth, dx, dy, scale = 1, delta = 0, borderType = BORDER_DEFAULT)
			因为Scharr卷积核大小固定,所以没有ksize这个参数了
	Laplacian算子		对噪音敏感,所以一般先去噪,再使用该方法
		Laplacian(img, ddepth, ksize = 3, scale = 1, borderType = BORDER_DEFAULT)
	Canny		边缘检测大杀器。首先使用5*5高斯滤波消除噪声,再使用Sobel在四个梯度方向上(0°/45°/90°/135°)计算,同时取局部极大值,这个极大值还会做阈值计算
		!!@@202207211.png_451_336_0.5@@!! 如果计算的值超过了最大值就肯定是边缘,小于最小值就肯定不是;如果在之间,还需要与上一次计算值是零乱的。图中上一次计算A是边缘,C和他在一条线上,所以也是边缘;B不在一条线上,则不认为它是边缘
		Canny(img, minVal, maxVal, ...)		范围越小可以检测到的边缘就越多,因为需要判断之间的情况就越少	

4. 形态学图像处理
  4.1 相关概念
	基于图像形态进行处理的一些基本方法		例如找到图像中各个物体的位置,至于物体是什么并不关心
	这些处理方法基本是对二进制图像进行处理,即黑白图像
	卷积核决定了图像处理后的效果

	腐蚀与膨胀,即缩小和放大,最基础的方法
	开运算,即先腐蚀再膨胀;闭运算,即先膨胀再腐蚀;顶帽,黑帽

	图像二值化,将每个像素变成0或255,分为全局二值化和局部二值化

  4.2 相关方法
	全局二值化:binary.py
		threshold(img, thresh, maxVal, type)
			img 图像,最好是灰度图
			thresh 阈值,超过这个值就换成最大值
			maxVal 最大值
			type 类型:
				THRESH_BINARY和THRESH_BINARY_INV	前者即表示超过阈值变为最大值,否则为0;后者相反,超过阈值为0否则设为最大值
				THRESH_TRUNC
				THRESH_TOZERO和THRESH_TOZERO_INV
			!!@@202207241.png_386_390_0.5@@!!	第一条线可以理解为原图数据,其中间的一条线表示阈值。下面几张图就是原图在阈值上的表现区别,可以看见只有THRESH_BINARY和THRESH_BINARY_INV算是二值化,其他的处理都是存在保留原图的部分
	自适应二值化		由于光照不均匀以及阴影的存在,只有一个阈值会使得在阴影处的白色被二值化成黑色
		adaptiveThreshold(img, maxVal, adaptiveMethod, type, blockSize, C)
			adaptiveMethod 计算阈值的方法
				ADAPTIVE_THRESH_MEAN_C 计算邻近区域的平均值
				ADAPTIVE_THRESH_GAUSSIAN_C 高斯窗口加权平均值,即越靠近中心点的权重越大,一般都用这个效果更好
			blockSize 邻近区域的大小,即每次自适应计算的块的大小。如果图像明暗变化不大,希望计算的快一些,就可以把块设的大一些
			C 常量,应从计算出的平均值或加权平均值中减去
			type 类型,只包含THRESH_BINARY和THRESH_BINARY_INV这两个二值化的类型
	腐蚀:erode.py	卷积核一般是一个奇数的区域(3*3 5*5 7*7),也可能是椭圆。当图像中有两个物体中间有一些连线,通过腐蚀就可以将两者分开
		!!@@202207242.png_387_406_0.5@@!!	红色区域可以理解为一个5*5的卷积核,这个卷积核表示只有在所有的小方块都在白色区域时才保留原数据,否则都变为黑色;整个黑色包括以内的区域为原图,那么最里面的虚线就可以理解成最终会保留的区域
		!!@@202207243.png_489_360_0.5@@!!		A为原图,经过3*3的卷积核B腐蚀以后最终成为右图		!!@@202207244.png_363_329_0.5@@!!
		erode(img, kernel, iterations=1)		卷积核kernel越大执行越快,iterations腐蚀执行次数
	获取腐蚀/膨胀的卷积核		一般就是用全1的核
		getStructuringElement(type, size)
			type	MORPH_RECT 全1矩形	MORPH_ELLIPSE 椭圆	MORPH_CROSS 十字行(即中间横竖为1,其他为0)
			size	一般值为(3, 3), (5, 5) ...
	膨胀	腐蚀和膨胀是相反操作,但是如果核比较大,那么对原图的腐蚀就会比较大,膨胀去还原的时候就可能回不到原图了
		!!@@202207245.png_199_357_0.5@@!!	!!@@202207246.png_389_322_0.5@@!!	卷积时,只要保证卷积核中间元素不为0,则整个卷积的区域都不为0,于是原图形就膨胀了
		dilate(img, kernel, iterations=1)
	开运算:erode.py		腐蚀+膨胀,可以自己调用两个方法达到目的,但是提供了专门的方法使用
		!!@@202207247.png_352_309_0.5@@!!	开运算可以消除噪点
		morphologyEx(img, MORPH_OPEN, kernel)		如果噪点比较大,一般使用比较大的kernel消除噪点
	闭运算		膨胀+腐蚀
		!!@@202207248.png_332_297_0.5@@!!	开运算可以消除图像外面的噪点,而闭运算可以消除图形内部的噪点
		morphologyEx(img, MORPH_CLOSE, kernel)
	形态学梯度		梯度 = 原图 - 腐蚀,可以得到边缘
		!!@@202207249.png_376_308_0.5@@!!
		morphologyEx(img, MORPH_GRADIENT, kernel)		kernel比较大时获取的边缘就比较不清楚,但是比较小时边缘可能会很细
	顶帽		顶帽 = 原图 - 开运算,可以保留图像中非噪点的小区域(大图形外的小图形)
		!!@@2022072410.png_278_297_0.5@@!!		红色中即顶帽留下的区域	!!@@2022072411.png_437_162_0.5@@!!
		morphologyEx(img, MORPH_TOPHAT, kernel)
	黑帽		黑帽 = 原图 - 闭运算,可以获取图像内部的噪点(大图形内的小图形)
		!!@@2022072412.png_318_294_0.5@@!!
		morphologyEx(img, MORPH_BLACKHAT, kernel)

5. 图像轮廓	具有相同颜色或强度的连续点曲线,可用于图形分析和物体的识别检测
	为了检测的准确性,需要先对图像进行二值化或Canny操作;画轮廓时会修改输入的图像,所以注意备份原图
	!!@@2022072413.png_285_390_0.5@@!!
	查找轮廓:countours.py
		findContours(img, mode, ApproximationMode ...)		返回两个结果,contours为所有的轮廓,hierarchy为轮廓的层级
			mode	模式
				RETR_EXTERNAL = 0		只检测外轮廓,比较快
				RETR_LIST = 1	检测的轮廓不建立等级关系,这个用的比较多
				RETR_CCOMP = 2	轮廓最多建立两级层级关系
				RETR_TREE = 3	按树形存储轮廓
				!!@@2022072414.png_431_339_0.5@@!!	只检测外部
				!!@@2022072415.png_438_338_0.5@@!!	层级编号从里到外,从右到左(这张图的数字标的有点看不懂,感觉不对,但是规则确实如前所述)
				!!@@2022072416.png_419_339_0.5@@!!		编号顺序同上。0是最里面的五角星,左边的图像可以分成两个两层,右边外部的可以分成两层
				!!@@2022072417.png_437_335_0.5@@!!		从最外面开始编号
			ApproximationMode	近似模式
				CHAIN_APPROX_NONE	保存所有轮廓上的点
				CHAIN_APPROX_SIMPLE	只保存角点,会比上面少存储很多信息,一般使用的多
				还有很多其他模式可以参考源码
	绘制轮廓:countours.py
		drawContours(img, contours, contourIdx, color, thickness ...)
			contours	通过轮廓查找获取的轮廓
			contourIdx	需要绘制轮廓层数,可用于测试画不同的轮廓;-1表示绘制所有轮廓
			color	绘制的颜色,(0, 0, 255)
			thickness	线宽,-1是全部填充
	轮廓的面积
		contourArea(contour)
	轮廓的周长
		arcLength(curve, closed)	curve就是轮廓	closed表示轮廓是否闭合
	多边形逼近与凸包
		!!@@2022072418.png_636_331_0.5@@!!		左边是逼近,因为轮廓的点很多,所以只用存储部分关键点即可,可以通过调整精度使逼近的更精确;右边为凸包,只绘制突出的轮廓
		approxPolyDP(curve, epsilon, closed)		逼近,curve轮廓,closed是否闭合轮廓
			epsilon 精度,越小越接近原轮廓,数据量也越大
		convexHull(points, clockwise)		凸包,points轮廓,clockwise:true表示顺时针绘制
	外接矩阵
		!!@@2022072419.png_302_357_0.5@@!!		红色小外接矩阵 绿色最大外接矩阵;最小外接矩阵可以判断是否发生了旋转
		minAreaRect(points)	最小外接矩阵。
			points 轮廓
			返回值为RotatedRect,旋转的矩形。包括:x,y 起始点;width,height 长宽;angle 旋转角度
		boundingRect(array)
			array 轮廓
			返回值为Rect,即普通的矩形,不存在旋转角度









DOWN 返回