首页 飞桨领航团 帖子详情
【AI达人养成营】学习笔记-图像处理
收藏
快速回复
飞桨领航团 文章AI达人创造营 347 0
【AI达人养成营】学习笔记-图像处理
收藏
快速回复
飞桨领航团 文章AI达人创造营 347 0

图像处理

2021年11月28日

8:50

记的是我自己不太熟悉的一些操作,很多都是文档上的,偶尔有自己的一些理解。

 

PIL库
 

基本操作

import numpy as np

import matplotlib.pyplot as plt

from PIL import Image

path = "baidu_bear.png" # 图片路径

image = Image.open(path)

plt.imshow(image)

 

 

 

# 输出的时候可加参数

plt.imshow(img,'gray')

 

# 将图片转为矩阵表示

 np.array(mnist[0][0])

 

# 将矩阵保存成文本,数字格式为整数

np.savetxt('7.txt', np.array(mnist[0][0]), fmt='%4d')

 

# 使用PIL分离颜色通道

r,g,b = img.split()

 

# 这里也是三个通道,0,1,2

img.getchannel(0)

 

# PIL库的crop函数做简单的图片裁剪

r.crop((100,100,128,128))

 

# 将图片第一行的像素点逐个取出

np.array(img)[0]

 

#1*4的第一张图

plt.subplot(1,  4, 1)

plt.imshow(RGB_Image,'gray')

plt.title('RGB_Image')#加标题

CV库
 

cv2.IMREAD_COLOR:彩色图,默认值(1)
cv2.IMREAD_GRAYSCALE:灰度图(0)
cv2.IMREAD_UNCHANGED:包含透明通道的彩色图(-1)
 

Cv2路径中不能有中文

 

 

# 将彩色图的BGR通道顺序转成RGB

img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# 将彩色图的BGR通道直接转为灰度图

img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

 

 

OpenCV中彩色图是以B-G-R通道顺序存储的,灰度图只有一个通道。

 

 

# 查看图片的形状 cv库

img.shape

 

# 加载灰度图

img = cv2.imread('lena.jpg', 0)

# 显示这张灰度图

plt.imshow(img,'gray')

 

保持图片

cv2.imwrite('lena-grey.jpg',img)

 

# 加载四通道图片

img = cv2.imread('cat.png',-1)

# 将彩色图的BGR通道顺序转成RGB,注意,在这一步直接丢掉了alpha通道

img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

plt.imshow(img)

 

# 默认三通道图片

img = cv2.imread('cat.png', 1)

# 转颜色通道为RGB

img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

plt.imshow(img)

 

Cv2截取

face = img[0:740, 400:1000]

plt.imshow(face)

 

# 通道合并与分割

 

 

HSV主要就是颜色处理

hsv提取颜色

 

缩放、平移、翻转、绘图
缩放图片

缩放就是调整图片的大小,使用cv2.resize()函数实现缩放。可以按照比例缩放,也可以按照指定的大小缩放: 我们也可以指定缩放方法interpolation,更专业点叫插值方法,默认是INTER_LINEAR,全部可以参考:InterpolationFlags

缩放过程中有五种插值方式:

cv2.INTER_NEAREST 最近邻插值
cv2.INTER_LINEAR 线性插值
cv2.INTER_AREA 基于局部像素的重采样,区域插值
cv2.INTER_CUBIC 基于邻域4x4像素的三次插值
cv2.INTER_LANCZOS4 基于8x8像素邻域的Lanczos插值
 

来自

 

 

 

 

 

 

 

 

 

 

平移图片

要平移图片,我们需要定义下面这样一个矩阵,tx,ty是向x和y方向平移的距离:

M = [[1, 0, tx], [0, 1, ty]]

平移是用仿射变换函数cv2.warpAffine()实现的:

 

 

 

 

 

绘图功能

绘制各种几何形状、添加文字
OpenCV函数:cv2.line(), cv2.circle(), cv2.rectangle(), cv2.ellipse(), cv2.putText()
绘制形状的函数有一些共同的参数,提前在此说明一下:

img:要绘制形状的图片
color:绘制的颜色
彩色图就传入BGR的一组值,如蓝色就是(255,0,0)
灰度图,传入一个灰度值就行
thickness:线宽,默认为1;对于矩形/圆之类的封闭形状而言,传入-1表示填充形状
lineType:线的类型。默认情况下,它是8连接的。cv2.LINE_AA 是适合曲线的抗锯齿线。
 

 

 

 

 

 

 

 

(线宽-1就是填充)

 

添加文字

使用cv2.putText()添加文字,它的参数也比较多,同样请对照后面的代码理解这几个参数:

参数2:要添加的文本
参数3:文字的起始坐标(左下角为起点)
参数4:字体
参数5:文字大小(缩放比例)

 

加入中文

 

小结

cv2.line()画直线,cv2.circle()画圆,cv2.rectangle()画矩形,cv2.ellipse()画椭圆,cv2.polylines()画多边形,cv2.putText()添加文字。
画多条直线时,cv2.polylines()要比cv2.line()高效很多。
要在图像中打上中文,可以用PIL库结合OpenCV实现。
图像间数学运算
 

图片间的数学运算,如相加、按位运算等
OpenCV函数:cv2.add(), cv2.addWeighted(), cv2.bitwise_and()
 

图片相加

要叠加两张图片,可以用cv2.add()函数,相加两幅图片的形状(高度/宽度/通道数)必须相同。

cv2.add(x, y)

 

图像混合

图像混合cv2.addWeighted()也是一种图片相加的操作,只不过两幅图片的权重不一样,γ相当于一个修正值:

 

 

 

 

 

阈值处理图片
固定阈值分割

固定阈值分割很直接,一句话说就是像素点值大于阈值变成一类值,小于阈值变成另一类值。

cv2.threshold()用来实现阈值分割,ret是return value缩写,代表当前的阈值。函数有4个参数:

参数1:要处理的原图,一般是灰度图
参数2:设定的阈值
参数3:最大阈值,一般为255
参数4:阈值的方式,主要有5种,详情:ThresholdTypes
0: THRESH_BINARY  当前点值大于阈值时,取Maxval,也就是第四个参数,否则设置为0
1: THRESH_BINARY_INV 当前点值大于阈值时,设置为0,否则设置为Maxval
2: THRESH_TRUNC 当前点值大于阈值时,设置为阈值,否则不改变
3: THRESH_TOZERO 当前点值大于阈值时,不改变,否则设置为0
4:THRESH_TOZERO_INV  当前点值大于阈值时,设置为0,否则不改变

 

阈值分割五种案例

 

 

 

 

自适应阈值

看得出来固定阈值是在整幅图片上应用一个阈值进行分割,它并不适用于明暗分布不均的图片。 cv2.adaptiveThreshold()自适应阈值会每次取图片的一小部分计算阈值,这样图片不同区域的阈值就不尽相同。它有5个参数,其实很好理解,先看下效果:

参数1:要处理的原图
参数2:最大阈值,一般为255
参数3:小区域阈值的计算方式
ADAPTIVE_THRESH_MEAN_C:小区域内取均值
ADAPTIVE_THRESH_GAUSSIAN_C:小区域内加权求和,权重是个高斯核
参数4:阈值方式(跟前面讲的那5种相同)
参数5:小区域的面积,如11就是11*11的小块
参数6:最终阈值等于小区域计算出的阈值再减去此值
建议读者调整下参数看看不同的结果。

 

 

 

 

几种滤波
均值滤波

均值滤波是一种最简单的滤波处理,它取的是卷积核区域内元素的均值,用cv2.blur()实现,如3×3的卷积核:

 

img = cv2.imread('lena.jpg')
blur = cv2.blur(img, (3, 3))  # 均值模糊

 

 

方框滤波

方框滤波跟均值滤波很像,如3×3的滤波核如下:

 

用cv2.boxFilter()函数实现,当可选参数normalize为True的时候,方框滤波就是均值滤波,上式中的a就等于1/9;normalize为False的时候,a=1,相当于求区域内的像素和。

blur = cv2.boxFilter(img, -1, (9, 9), normalize=True)

 

高斯滤波

前面两种滤波方式,卷积核内的每个值都一样,也就是说图像区域中每个像素的权重也就一样。高斯滤波的卷积核权重并不相同:中间像素点权重最高,越远离中心的像素权重越小。

显然这种处理元素间权值的方式更加合理一些。图像是2维的,所以我们需要使用2维的高斯函数,比如OpenCV中默认的3×3的高斯卷积核:

 

OpenCV中对应函数为cv2.GaussianBlur(src,ksize,sigmaX): 参数3 σx值越大,模糊效果越明显。高斯滤波相比均值滤波效率要慢,但可以有效消除高斯噪声,能保留更多的图像细节,所以经常被称为最有用的滤波器。均值滤波与高斯滤波的对比结果如下(均值滤波丢失的细节更多)

gaussian = cv2.GaussianBlur(img, (9, 9), 1) # 高斯滤波

 

中值滤波

中值又叫中位数,是所有数排序后取中间的值。中值滤波就是用区域内的中值来代替本像素值,所以那种孤立的斑点,如0或255很容易消除掉,适用于去除椒盐噪声和斑点噪声。中值是一种非线性操作,效率相比前面几种线性滤波要慢。

 

median = cv2.medianBlur(img, 9) # 中值滤波

 

双边滤波

模糊操作基本都会损失掉图像细节信息,尤其前面介绍的线性滤波器,图像的边缘信息很难保留下来。然而,边缘(edge)信息是图像中很重要的一个特征,所以这才有了双边滤波。用cv2.bilateralFilter()函数实现:可以看到,双边滤波明显保留了更多边缘信息。

blur = cv2.bilateralFilter(img, 9, 75, 75) # 双边滤波

 

 

 

边缘检测
 

cv2.Canny()进行边缘检测,参数2、3表示最低、高阈值,下面来解释下具体原理。

Canny边缘检测

Canny边缘提取的具体步骤如下:

使用5×5高斯滤波消除噪声:
边缘检测本身属于锐化操作,对噪点比较敏感,所以需要进行平滑处理。

 

计算图像梯度的方向:
首先使用Sobel算子计算两个方向上的梯度$ G_x 和和和 G_y $,然后算出梯度的方向:

 

保留这四个方向的梯度:0°/45°/90°/135°,有什么用呢?我们接着看。

取局部极大值:
梯度其实已经表示了轮廓,但为了进一步筛选,可以在上面的四个角度方向上再取局部极大值

滞后阈值:
经过前面三步,就只剩下0和可能的边缘梯度值了,为了最终确定下来,需要设定高低阈值:

像素点的值大于最高阈值,那肯定是边缘
同理像素值小于最低阈值,那肯定不是边缘
像素值介于两者之间,如果与高于最高阈值的点连接,也算边缘,所以上图中C算,B不算
Canny推荐的高低阈值比在2:1到3:1之间。

 

 

 

腐蚀与膨胀
 

了解形态学操作的概念
学习膨胀、腐蚀、开运算和闭运算等形态学操作
OpenCV函数:cv2.erode(), cv2.dilate(), cv2.morphologyEx()
啥叫形态学操作

形态学操作其实就是改变物体的形状,比如腐蚀就是"变瘦",膨胀就是"变胖"。

腐蚀

腐蚀的效果是把图片"变瘦",其原理是在原图的小区域内取局部最小值。因为是二值化图,只有0和255,所以小区域内有一个是0该像素点就为0。

这样原图中边缘地方就会变成0,达到了瘦身目的

OpenCV中用cv2.erode()函数进行腐蚀,只需要指定核的大小就行:

 

这个核也叫结构元素,因为形态学操作其实也是应用卷积来实现的。结构元素可以是矩形/椭圆/十字形,可以用cv2.getStructuringElement()来生成不同形状的结构元素,比如:

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))  # 矩形结构kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))  # 椭圆结构kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))  # 十字形结构

膨胀

膨胀与腐蚀相反,取的是局部最大值,效果是把图片"变胖":

 

开/闭运算

先腐蚀后膨胀叫开运算(因为先腐蚀会分开物体,这样容易记住),其作用是:分离物体,消除小区域。这类形态学操作用cv2.morphologyEx()函数实现:

 

 

闭运算则相反:先膨胀后腐蚀(先膨胀会使白色的部分扩张,以至于消除/"闭合"物体里面的小黑洞,所以叫闭运算)

 

 

 

如果我们的目标物体外面有很多无关的小区域,就用开运算去除掉;如果物体内部有很多小黑洞,就用闭运算填充掉。

使用OpenCV摄像头与加载视频
 

OpenCV函数:cv2.VideoCapture(), cv2.VideoWriter()
打开摄像头

要使用摄像头,需要使用cv2.VideoCapture(0)创建VideoCapture对象,参数0指的是摄像头的编号,如果你电脑上有两个摄像头的话,访问第2个摄像头就可以传入1,依此类推。

 

 

另外,通过cap.get(propId)可以获取摄像头的一些属性,比如捕获的分辨率,亮度和对比度等。propId是从0~18的数字,代表不同的属性,完整的属性列表可以参考:VideoCaptureProperties。也可以使用cap.set(propId,value)来修改属性值。比如说,我们在while之前添加下面的代码:

 

播放本地视频

跟打开摄像头一样,如果把摄像头的编号换成视频的路径就可以播放本地视频了。回想一下cv2.waitKey(),它的参数表示暂停时间,所以这个值越大,视频播放速度越慢,反之,播放速度越快,通常设置为25或30。

 

录制视频

之前我们保存图片用的是cv2.imwrite(),要保存视频,我们需要创建一个VideoWriter的对象,需要给它传入四个参数:

输出的文件名,如'output.avi'
编码方式FourCC码
帧率FPS
要保存的分辨率大小
FourCC是用来指定视频编码方式的四字节码,所有的编码可参考Video Codecs。如MJPG编码可以这样写: cv2.VideoWriter_fourcc(*'MJPG')或cv2.VideoWriter_fourcc('M','J','P','G')

 

 

0
收藏
回复
在@后输入用户全名并按空格结束,可艾特全站任一用户