cv2.add()——将两张图片的像素叠加起来,与两张图片直接相加结果不同
>>> x = np.uint8([250])
>>> y = np.uint8([10])
>>> print cv2.add(x,y) # 250+10 = 260 => 255 最高为255
[[255]]
>>> print x+y # 250+10 = 260 % 256 = 4
[4]
cv2.addWeighted() —— 以一定权重相加
img1 = cv2.imread('ml.png')
img2 = cv2.imread('opencv_logo.jpg')
#图片1 0.7 图片2 0.3
dst = cv2.addWeighted(img1,0.7,img2,0.3,0)
cv2.imshow('dst',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
- cv2.bitwise() —— 位图操作,包括AND OR NOT XOR
# 载入两张图片
img1 = cv2.imread('messi5.jpg')
img2 = cv2.imread('opencv_logo.png')
# 创建一个ROI:region of image即图片的一部分区域
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols ]
# 转换颜色模式为灰度模式;用cv2.threshold将图片二值化处理,
#即将像素值高于或者低于设定阈值的像素置为0,然后将低于或者高于的值置为其它颜色,
#需要四个参数cv2.threshold(灰度图,阈值,设定值,方法),方法有五种:
#cv2.THRESH_BINARY - 二值化处理,低于阈值的像素点灰度值置为0;高于阈值的值置为参数3
#cv2.THRESH_BINARY_INV - 大于阈值的像素点灰度值置为0;小于阈值置为参数3
#cv2.THRESH_TRUNC - 小于阈值的像素点灰度值不变,大于阈值的像素点置为该阈值
#cv2.THRESH_TOZERO - 小于阈值的像素点灰度值不变,大于阈值的像素点置为0,其中参数3任取
#cv2.THRESH_TOZERO_INV - 大于阈值的像素点灰度值不变,小于阈值的像素点置为0,其中参数3任取
img2gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
ret, mask = cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY)
#将图片进行反向掩码的位操作得到logo图像
mask_inv = cv2.bitwise_not(mask)
# 保留除logo外的背景
img1_bg = cv2.bitwise_and(roi,roi,mask = mask_inv)
# 去除除logo图中logo外的背景
img2_fg = cv2.bitwise_and(img2,img2,mask = mask)
# 进行融合
dst = cv2.add(img1_bg,img2_fg)
# 融合后放在原图上
img1[0:rows, 0:cols ] = dst
cv2.imshow('res',img1)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.blur(), cv2.GaussianBlur(), cv2.medianBlur(), cv2.bilateralFilter() —— 各种滤波进行图像平滑处理
首先要了解卷积是什么?
卷积就是循环对图像跟一个核逐个元素相乘再求和得到另外一副图像的操作,比如结果图中第一个元素5是怎么算的呢?原图中3×3的区域与3×3的核逐个元素相乘再相加:1×1 + 2×0 + 1×0 + 0×0 + 1×0 + 1×0 + 3×0 + 0×0 + 2×2 = 5。算完之后,整个框再往右移一步继续计算,横向计算完后,再往下移一步继续计算……网上有一副很经典的动态图,方便我们理解卷积:
- 滤波有均值滤波、方框滤波、高斯滤波、中值滤波、双边滤波
- 在不知道用什么滤波器好的时候,优先高斯滤波cv2.GaussianBlur(),然后均值滤波cv2.blur()
- 斑点和椒盐噪声优先使用中值滤波cv2.medianBlur()
- 要去除噪点的同时尽可能保留更多的边缘信息,使用双边滤波cv2.bilateralFilter()
- 线性滤波方式:均值滤波、方框滤波、高斯滤波(速度相对快)
-
非线性滤波方式:中值滤波、双边滤波(速度相对慢)
cv2.threshold, cv2.adaptiveThreshold —— 图像阈值化
简单的阈值处理cv2.threshold在基础操作已经讲过,接下来说高级处理cv2.adaptiveThreshold
有三个输入变量和一个输出量:
- Adaptive Method - 决定阈值计算类型.
- cv2.ADAPTIVE_THRESH_MEAN_C : 根据相邻区域
- cv2.ADAPTIVE_THRESH_GAUSSIAN_C : 通过高斯函数窗口
- Block Size - 决定相邻区域的范围
- C -计算中需要的一个常数
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('dave.jpg',0)
img = cv2.medianBlur(img,5)
ret,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
th2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,\
cv2.THRESH_BINARY,11,2)
th3 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv2.THRESH_BINARY,11,2)
titles = ['Original Image', 'Global Thresholding (v = 127)',
'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
for i in range(4):
plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
孰优孰劣显而易见!
几何变换
缩放
import cv2
import numpy as np
img = cv2.imread('messi5.jpg')
# 缩放图片,就一行代码,很简单
res = cv2.resize(img,None,fx=2, fy=2, interpolation = cv2.INTER_CUBIC)
#OR
height, width = img.shape[:2]
res = cv2.resize(img,(2*width, 2*height), interpolation = cv2.INTER_CUBIC)
旋转、平移
平移
import cv2
import numpy as np
img = cv2.imread('messi5.jpg',0)
rows,cols = img.shape
M = np.float32([[1,0,100],[0,1,50]])
# 用cv2.warpAffine平移一段距离M
dst = cv2.warpAffine(img,M,(cols,rows))
cv2.imshow('img',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
旋转
img = cv2.imread('messi5.jpg',0)
rows,cols = img.shape
M = cv2.getRotationMatrix2D((cols/2,rows/2),90,1)
# 用cv2.warpAffine旋转
dst = cv2.warpAffine(img,M,(cols,rows))
仿射
对于仿射变换,我们只需要知道变换前的三个点与其对应的变换后的点,就可以通过cv2.getAffineTransform求得变换矩阵.
img = cv2.imread('sudokusmall.png')
rows,cols,ch = img.shape
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))
plt.subplot(121),plt.imshow(img),plt.title('Input')
plt.subplot(122),plt.imshow(dst),plt.title('Output')
plt.show()
形态学操作
形态学操作是指改变物体形状,一般作用于二值化图,来连接相邻的元素或分离成独立的元素。腐蚀和膨胀是针对图片中的白色部分!
腐蚀
import cv2
import numpy as np
img = cv2.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
erosion = cv2.erode(img,kernel,iterations = 1)
膨胀
dilation = cv2.dilate(img,kernel,iterations = 1)
开运算
先腐蚀后膨胀叫开运算(因为先腐蚀会分开物体,这样容易记住),其作用是:分离物体,消除小区域。
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
闭运算
先膨胀后腐蚀(先膨胀会使白色的部分扩张,以至于消除/“闭合”物体里面的小黑洞,所以叫闭运算)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
形态学梯度运算
膨胀图减去腐蚀图,这样会得到物体的轮廓
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
不常用,不做解释
##### 顶帽
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
##### 黑帽
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
图像梯度
如果你还记得高数中用一阶导数来求极值的话,就很容易理解了:把图片想象成连续函数,因为边缘部分的像素值是与旁边像素明显有区别的,所以对图片局部求极值,就可以得到整幅图片的边缘信息了。不过图片是二维的离散函数,导数就变成了差分,这个差分就称为图像的梯度。
滤波是应用卷积来实现的,卷积的关键就是卷积核,我们来考察下面这个卷积核:这个核是用来提取图片中的垂直边缘的,怎么做到的呢?看下图:
当前列左右两侧的元素进行差分,由于边缘的值明显小于(或大于)周边像素,所以边缘的差分结果会明显不同,这样就提取出了垂直边缘。同理,把上面那个矩阵转置一下,就是提取水平边缘。这种差分操作就叫图像的梯度计算:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('dave.jpg',0)
laplacian = cv2.Laplacian(img,cv2.CV_64F)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(laplacian,cmap = 'gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,3),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,4),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()
边缘检测
边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。
侃尼边缘检测原理
侃尼边缘检测是一个流行的边缘检测算法,1986年由 John F. Canny提出。包括四个步骤:
- 减少噪声
因为边缘检测容易受到噪声的影响,因此使用5×5高斯滤波消除噪声,前面已经提到过。
-
计算图像梯度
首先用上面提到的Sobel方法提取水平垂直两个方向的边缘,然后根据以下公式计算出每条边的梯度及其梯度方向: 梯度方向总是垂直于边缘的。梯度方向被归为四类:水平、垂直、及其对角线方向。
-
非极大值抑制
在找到梯度后,应该对整幅图像做一个扫描,去除那些非边界上的点。对每一个像素进行检查,看这个点的梯度是不是周围具有相同梯度方向的点中最大的。如下所示: 如果A的梯度大于B、C,那么保留A的值,将B、C置为0。
简而言之,你得到的结果是一个二进制图像,“薄边缘”。
-
滞后阈值法
这个阶段决定谁是边缘,谁不是边缘
- 像素点的值大于最高阈值,那肯定是边缘(上图A)
- 同理像素值小于最低阈值,那肯定不是边缘
- 像素值介于两者之间,如果与高于最高阈值的点连接,也算边缘,所以上图中C算,B不算
进过这一步就得到了清晰地边界。
OpenCV中Canny边缘检测的使用
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('messi5.jpg',0)
# 第一个参数为图片,第二第三个参数为上述原理第四步的最大最小阈值
edges = cv2.Canny(img,100,200)
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()
图像匹配
SIFT算法由D.G.Lowe 1999年提出,2004年完善总结,论文发表在2004年的IJCV上,主要用于提取具有图像旋转不变性和伸缩不变性的特征点。这项技术可以推广到图像识别、图像拼接以及图像恢复等。
David G. Lowe, "Distinctive image features from scale-invariant keypoints," International Journal of Computer Vision,60, 2 (2004), pp. 91-110
论文详细地址
其中就用到了OpenCV的cv2.pyrDown()
和cv2.pyrUp()
函数来创建图像金字塔。
图像金字塔主要有两类:高斯金字塔和拉普拉斯金字塔。
高斯金字塔的顶部是通过将底部图像的连续行与列去掉得到的。每一层图像中的像素值等于下一层图像中对应位置5个像素的高斯加权平均值。这样操作一个MN的图像就变成了(M/2)(N/2)的图像,图像的面积就变为原来的1/4,连续进行这样的操作,就会得到一些列的金字塔的图像。
一个小应用
import cv2
import numpy as np,sys
A = cv2.imread('apple.jpg')
B = cv2.imread('orange.jpg')
# 生成苹果图像金字塔
G = A.copy()
gpA = [G]
for i in range(6):
G = cv2.pyrDown(G)
gpA.append(G)
# 生成橘子图像金字塔
G = B.copy()
gpB = [G]
for i in range(6):
G = cv2.pyrDown(G)
gpB.append(G)
# 生成苹果图像拉普拉斯金字塔
lpA = [gpA[5]]
for i in range(5,0,-1):
GE = cv2.pyrUp(gpA[i])
L = cv2.subtract(gpA[i-1],GE)
lpA.append(L)
# 生成橘子图像拉普拉斯金字塔
lpB = [gpB[5]]
for i in range(5,0,-1):
GE = cv2.pyrUp(gpB[i])
L = cv2.subtract(gpB[i-1],GE)
lpB.append(L)
# 添加苹果、橘子各一半的图像
LS = []
for la,lb in zip(lpA,lpB):
rows,cols,dpt = la.shape
ls = np.hstack((la[:,0:cols/2], lb[:,cols/2:]))
LS.append(ls)
# 重新组合
ls_ = LS[0]
for i in range(1,6):
ls_ = cv2.pyrUp(ls_)
ls_ = cv2.add(ls_, LS[i])
# 直接组合
real = np.hstack((A[:,:cols/2],B[:,cols/2:]))
cv2.imwrite('Pyramid_blending2.jpg',ls_)
cv2.imwrite('Direct_blending.jpg',real)
未完待续
关于OpenCV的学习就先告一段落,接下来我会更新关于机器学习的内容,然后再用到关于OpenCV的内容会再更新,大家有什么需要的也可以留言告诉我,我也会考虑更新的。