python-opencv常用的处理函数

resize扩展缩放

改变图像的尺寸大小

height,width=img.shape[:2]
res=cv2.resize(img,(2*width,2*height),interpolation=cv2.INTER_LINEAR)

getRotationMatrix2D 旋转

M=cv2.getRotationMatrix2D((cols/2,rows/2),45,0.6)

#这里的第一个参数为旋转中心,第二个为旋转角度,第三个为旋转后的缩放因子
#可以通过设置旋转中心,缩放因子,以及窗口大小来防止旋转后超出边界的问题

dst=cv2.warpAffine(img,M,(2*cols,2*rows)) M为旋转矩阵(2*3)

第三个参数是输出图像的尺寸中心

getAffineTransform 仿射变换

二维坐标到二维坐标之间的线性变换,保持二维图形的“平直性”(译注:straightness,即变换后直线还是直线不会打弯,圆弧还是圆弧)和“平行性”(译注:parallelness,其实是指保二维图形间的相对位置关系不变,平行线还是平行线,相交直线的交角不变。)

pts1 = np.float32([[50,50],[200,50],[50,200]])
pts2 = np.float32([[10,100],[200,50],[100,250]])
M = cv2.getAffineTransform(pts1,pts2)
dst = cv2.warpAffine(img,M,(cols,rows))
第三个参数是输出图像的尺寸中心

getPerspectiveTransform 透视变换

透视变换(Perspective Transformation)的本质是将图像投影到一个新的视平面,在变换前后直线还是直线

pts1 = np.float32([[56,65],[368,52],[28,387],[389,390]])
pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]])
M = cv2.getPerspectiveTransform(pts1,pts2)
dst = cv2.warpPerspective(img,M,(300,300))

图像阈值

简单阈值,自适应阈值,Otsu’s 二值化

简单阀值

ret,thresh1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)

自适应阀值

th2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,\
            cv2.THRESH_BINARY,11,2) 	
            – cv2.ADPTIVE_THRESH_MEAN_C:阈值取自相邻区域的平均值
  		– cv2.ADPTIVE_THRESH_GAUSSIAN_C:阈值取值相邻区域的加

权和,权重为一个高斯窗口
11为区域大小 2为平均值要减去的常数

otsu‘s

blur = cv2.GaussianBlur(img,(5,5),0)
ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
 可以查看:https://blog.csdn.net/jinzhichaoshuiping/article/details/51207942

图像平滑

卷积

kernel = np.ones((5,5),np.float32)/25
dst = cv2.filter2D(img,-1,kernel)

图像模糊(图像平滑)

使用低通滤波器可以达到图像模糊的目的。这对与去除噪音很有帮助。其实就是去除图像中的高频成分(比如:噪音,边界)。所以边界也会被模糊一点。(当然,也有一些模糊技术不会模糊掉边界)。OpenCV 提供了四种模糊技术:

平均

blur = cv2.blur(img,(5,5))

高斯模糊

blur = cv2.GaussianBlur(img,(5,5),0)

中值模糊

median = cv2.medianBlur(img,5)

顾名思义就是用与卷积框对应像素的中值来替代中心像素的值。这个滤波器经常用来去除椒盐噪声。前面的滤波器都是用计算得到的一个新值来取代中心像素的值,而中值滤波是用中心像素周围(也可以使他本身)的值来取代他。他能有效的去除噪声。卷积核的大小也应该是一个奇数。

双边滤波

blur = cv2.bilateralFilter(img,9,75,75)
9 邻域直径,两个 75 分别是空间高斯函数标准差,灰度值相似性高斯函数标准差
函数 cv2.bilateralFilter() 能在保持边界清晰的情况下有效的去除噪音。但是这种操作与其他滤波器相比会比较慢。我们已经知道高斯滤波器是求中心点邻近区域像素的高斯加权平均值。这种高斯滤波器只考虑像素之间的空间关系,而不会考虑像素值之间的关系(像素的相似度)。所以这种方法不会考虑一个像素是否位于边界。因此边界也会别模糊掉,而这正不是我们想要。双边滤波在同时使用空间高斯权重和灰度值相似性高斯权重。空间高斯函数确保只有邻近区域的像素对中心点有影响,灰度值相似性高斯函数确保只有与中心像素灰度值相近的才会被用来做模糊运算。所以这种方法会确保边界不会被模糊掉,因为边界处的灰度值变化比较大。

形态学转换

形态学操作是根据图像形状进行的简单操作。一般情况下对二值化图像进行的操作。需要输入两个参数,一个是原始图像,第二个被称为结构化元素或核,它是用来决定操作的性质的。两个基本的形态学操作是腐蚀和膨胀。他们的变体构成了开运算,闭运算,梯度等。

腐蚀

其中1为前景,0为背景,腐蚀是指把前景腐蚀掉,所以前景物体会变小,整幅图像的白色区域会减少。这对于去除白噪声很有用,也可以用来断开两个连在一块的物体等。

kernel = np.ones((5,5),np.uint8) 卷积核对应的原图像的所有像素值都是 1,那么中心元素就保持原来的像素值,否则就变为零
erosion = cv2.erode(img,kernel,iterations = 1)

膨胀

一般在去噪声时先用腐蚀再用膨胀。因为腐蚀在去掉白噪声的同时,也会使前景对象变小。所以我们再对他进行膨胀。这时噪声已经被去除了,不会再回来了,但是前景还在并会增加。膨胀也可以用来连接两个分开的物体。

kernel = np.ones((5,5),np.uint8) 与卷积核对应的原图像的像素值中只要有一个是 1,中心元素的像素值就是 1。
dilation = cv2.dilate(img,kernel,iterations = 1) 

开运算

先进性腐蚀再进行膨胀就叫做开运算。就像我们上面介绍的那样,它被用来去除噪声。

kernel = np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)

闭运算

先膨胀再腐蚀。它经常被用来填充前景物体中的小洞,或者前景物体上的小黑点。

kernel = np.ones((5,5),np.uint8)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

形态学梯度

其实就是一幅图像膨胀与腐蚀的差。


kernel = np.ones((5,5),np.uint8)
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)


礼帽

原始图像与进行开运算之后得到的图像的差。

tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)

黑帽

进行闭运算之后得到的图像与原始图像的差

tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)

图像梯度

梯度简单来说就是求导。OpenCV 提供了三种不同的梯度滤波器,或者说高通滤波器:Sobel,Scharr 和 Laplacian

Sobel 算子和 Scharr 算子

Sobel 算子是高斯平滑与微分操作的结合体,所以它的抗噪声能力很好。你可以设定求导的方向(xorder 或 yorder)。还可以设定使用的卷积核的大小(ksize)。如果 ksize=-1,会使用 3x3 的 Scharr 滤波器,它的的效果要比 3x3 的 Sobel 滤波器好(而且速度相同,所以在使用 3x3 滤波器时应该尽量使用 Scharr 滤波器)。3x3 的 Scharr 滤波器卷积核如下:  在这里插入图片描述

sobelx8u = cv2.Scharr(img,cv2.CV_8U,1,0,ksize=5)

Laplacian 算子

拉普拉斯算子可以使用二阶导数的形式定义,可假设其离散实现类似于二阶 Sobel 导数,

laplacian = cv2.Laplacian(img,cv2.CV_64F)

Canny 边缘检测

噪声去除

图像灰度化:只有灰度图才能进行边缘检测
使用高斯滤波器,以平滑图像,滤除噪声

计算图像梯度

对平滑后的图像使用 Sobel 算子计算水平方向和竖直方向的一阶导数(图像梯度)(Gx 和 Gy)。根据得到的这两幅梯度图(Gx 和 Gy)找到边界的梯度和方向:
在这里插入图片描述

非极大值(Non-Maximum Suppression)抑制

将当前像素的梯度强度与沿正负梯度方向上的两个像素进行比较。
如果当前像素的梯度强度与另外两个像素相比最大,则该像素点保留为边缘点,否则该像素点将被抑制。

滞后阈值

现在要确定那些边界才是真正的边界。这时我们需要设置两个阈值:minVal 和 maxVal。当图像的灰度梯度高于 maxVal 时被认为是真的边界,那些低于 minVal 的边界会被抛弃。如果介于两者之间的话,就要看这个点是否与某个被确定为真正的边界点相连,如果是就认为它也是边界点,如果不是就抛弃。

图像金字塔

有两类图像金字塔:高斯金字塔和拉普拉斯金字塔

高斯金字塔

先对图像进行卷积操作,然后去除偶数行。这样操作一次一个 MxN 的图像就变成了一个 M/2xN/2 的图像。所以这幅图像的面积就变为原来图像面积的四分之一。这被称为 Octave。连续进行这样的操作我们就会得到一个分辨率不断下降的图像金字塔。我们可以使用函数cv2.pyrDown() 和 cv2.pyrUp() 构建图像金字塔。

lower_reso = cv2.pyrDown(higher_reso)
higher_reso2 = cv2.pyrUp(lower_reso)

拉普拉斯金字塔

在这里插入图片描述

OpenCV 中的轮廓

获取轮廓

函数 cv2.findContours() 有三个参数,第一个是输入图像,第二个是轮廓检索模式,第三个是轮廓近似方法。返回值有三个,第一个是图像,第二个是轮廓,第三个是(轮廓的)层析结构。轮廓(第二个返回值)是一个 Python列表,其中存储这图像中的所有轮廓。每一个轮廓都是一个 Numpy 数组,包含对象边界点(x,y)的坐标。

绘制轮廓

函数 cv2.drawContours() 可以被用来绘制轮廓。它可以根据你提供的边界点绘制任何形状。它的第一个参数是原始图像,第二个参数是轮廓,一个 Python 列表。第三个参数是轮廓的索引(在绘制独立轮廓是很有用,当设置为 -1 时绘制所有轮廓)。接下来的参数是轮廓的颜色和厚度等。

imgray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,127,255,0)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

#但是大多数时候,下面的方法更有用:
img1 = cv2.drawContours(im, contours, 8, (0,255,0), 3)

直方图

img = cv2.imread('home.jpg',0)
#别忘了中括号 [img],[0],None,[256],[0,256] ,只有 mask 没有中括号
hist = cv2.calcHist([img],[0],None,[256],[0,256])

使用掩膜

create a mask

要统计图像某个局部区域的直方图只需要构建一副掩模图像。将要统计的部分设置成白色,其余部分为黑色,就构成了一副掩模图像。

img = cv2.imread('home.jpg',0)
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255
masked_img = cv2.bitwise_and(img,img,mask = mask)
hist_full = cv2.calcHist([img],[0],None,[256],[0,256])
hist_mask = cv2.calcHist([img],[0],mask,[256],[0,256])

直方图均衡

直方图大部分在灰度值较高的部分,而且分布很集中。而我们希望直方图的分布比较分散,能够涵盖整个 x 轴。所以,我们就需要一个变换函数帮助我们把现在的直方图映射到一个广泛分布的直方图中。这就是直方图均衡化要做的事情。
img = cv2.imread(‘wiki.jpg’,0)
equ = cv2.equalizeHist(img)
res = np.hstack((img,equ)) #stacking images side-by-side
cv2.imwrite(‘res.png’,res)

CLAHE 有限对比适应性直方图均衡化

的确在进行完直方图均衡化之后,图片背景的对比度被改变了。但是你再对比一下两幅图像中雕像的面图,由于太亮我们丢失了很多信息。
为了解决这个问题,我们需要使用自适应的直方图均衡化。这种情况下,整幅图像会被分成很多小块,这些小块被称为“tiles”(在 OpenCV 中 tiles 的大小默认是 8x8),然后再对每一个小块分别进行直方图均衡化(跟前面类似)。
img = cv2.imread(‘tsukuba_l.png’,0)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(img)

图像变换

Numpy 中的傅里叶变换

如果它的幅度变化非常快,我们可以说他是高频信号,如果变化非常慢,我们称之为低频信号。你可以把这种想法应用到图像中,图像那里的幅度变化非常大呢?边界点或者噪声。所以我们说边界和噪声是图像中的高频分量(注意这里的高频是指变化非常快,而非出现的次数多)。如果没有如此大的幅度变化我们称之为低频分量。
img = cv2.imread(‘messi5.jpg’,0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)##将图像中的低频部分移动到图像的中心
(np.fft.ifftshift(img) # 进图像的低频和高频部分移动到图像原来的位置)
magnitude_spectrum = 20*np.log(np.abs(fshift)) #这里构建振幅图的公式没学过

掩膜操作,去除低频成分

傅里叶变换得到图像中心店为低频成分,现在我们可以进行频域变换了,我们就可以在频域对图像进行一些操作了,例如高通滤波和重建图像(DFT 的逆变换)。比如我们可以使用一个60x60 的矩形窗口对图像进行掩模操作从而去除低频分量。然后再使用函数np.fft.ifftshift() 进行逆平移操作,所以现在直流分量又回到左上角了,左后使用函数 np.ifft2() 进行 FFT 逆变换。同样又得到一堆复杂的数字,我们可以对他们取绝对值:

rows, cols = img.shape
crow,ccol = rows/2 , cols/2
fshift[crow-30:crow+30, ccol-30:ccol+30] = 0
f_ishift = np.fft.ifftshift(fshift)##进图像的低频和高频部分移动到图像原来的位置
img_back = np.fft.ifft2(f_ishift)##FFT 逆变换
img_back = np.abs(img_back)#获取绝对值

cv2实现

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

dft = cv2.dft(np.float32(img),flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))
rows, cols = img.shape
crow,ccol = rows/2 , cols/2

低通滤波器

mask = np.zeros((rows,cols,2),np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1
# apply mask and inverse DFT
fshift = dft_shift*mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1])

``
# 模板匹配
使用模板匹配在一幅图像中查找目标
res = cv2.matchTemplate(img,template,method)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)找到其中的最小值和最大值的位置

# Hough 直线变换 
cv2.HoughLinesP() 
返回值就是(ρ,θ)。ρ 的单位是像素,θ 的单位是弧度。这个函数的第个参数是一个二值化图像,所以在进行霍夫变换之前要首先进行二值化,或者进行Canny 边缘检测。第二和第三个值分别代表 ρ 和 θ 的精确度。第四个参数是阈值,只有累加其中的值高于阈值时才被认为是一条直线
minLineLength = 100
maxLineGap = 10
lines = cv2.HoughLinesP(edges,1,np.pi/180,100,minLineLength,maxLineGap)
for x1,y1,x2,y2 in lines[0]:
    cv2.line(img,(x1,y1),(x2,y2),(0,255,0),2)
## Hough 圆环变换
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,20,
param1=50,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
#draw the outer circle
cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)
#draw the center of the circle
cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)

cv2.HoughCircles(image, method, dp, minDist, circles, param1, param2, minRadius, maxRadius)  
image 不用多说,输入矩阵
method cv2.HOUGH_GRADIENT 也就是霍夫圆检测,梯度法
dp 计数器的分辨率图像像素分辨率与参数空间分辨率的比值(官方文档上写的是图像分辨率与累加器分辨率的比值,它把参数空间认为是一个累加器,毕竟里面存储的都是经过的像素点的数量),dp=1,则参数空间与图像像素空间(分辨率)一样大,dp=2,参数空间的分辨率只有像素空间的一半大
minDist 圆心之间最小距离,如果距离太小,会产生很多相交的圆,如果距离太大,则会漏掉正确的圆
param1 canny检测的双阈值中的高阈值,低阈值是它的一半
param2 最小投票数(基于圆心的投票数)
minRadius 需要检测院的最小半径
maxRadius 需要检测院的最大半径

# 图像分割 
## 分水岭算法
总的概括一下watershed图像自动分割的实现步骤:

1. 图像灰度化、滤波、Canny边缘检测

2. 查找轮廓,并且把轮廓信息按照不同的编号绘制到watershed的第二个入参merkers上,相当于标记注水点。

3. watershed分水岭运算

4. 绘制分割出来的区域,视觉控还可以使用随机颜色填充,或者跟原始图像融合以下,以得到更好的显示效果。

markers3 = cv2.watershed(img,markers)
说第二个入参markers必须包含了种子点信息。Opencv官方例程中使用鼠标划线标记,其实就是在定义种子,只不过需要手动操作,而使用findContours可以自动标记种子点。而分水岭方法完成之后并不会直接生成分割后的图像,还需要进一步的显示处理,如此看来,只有两个参数的watershed其实并不简单
##  GrabCut 算法进行交互式前景提取
mask = np.zeros(img.shape[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
rect = (50,50,450,290)
#函数的返回值是更新的 mask, bgdModel, fgdModel
cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img = img*mask2[:,:,np.newaxis]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值