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 返回