Python-OpenCV——进阶操作一网打尽

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()
image

旋转

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))
image
仿射

对于仿射变换,我们只需要知道变换前的三个点与其对应的变换后的点,就可以通过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()
image

形态学操作

形态学操作是指改变物体形状,一般作用于二值化图,来连接相邻的元素或分离成独立的元素。腐蚀和膨胀是针对图片中的白色部分!


原图
腐蚀
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提出。包括四个步骤:

  1. 减少噪声

因为边缘检测容易受到噪声的影响,因此使用5×5高斯滤波消除噪声,前面已经提到过。

  1. 计算图像梯度

    首先用上面提到的Sobel方法提取水平垂直两个方向的边缘,然后根据以下公式计算出每条边的梯度及其梯度方向:
    梯度方向总是垂直于边缘的。梯度方向被归为四类:水平、垂直、及其对角线方向。
  2. 非极大值抑制

    在找到梯度后,应该对整幅图像做一个扫描,去除那些非边界上的点。对每一个像素进行检查,看这个点的梯度是不是周围具有相同梯度方向的点中最大的。如下所示:
    非极大值抑制
    如果A的梯度大于B、C,那么保留A的值,将B、C置为0。

    简而言之,你得到的结果是一个二进制图像,“薄边缘”。

  3. 滞后阈值法

    这个阶段决定谁是边缘,谁不是边缘
    滞后阈值法
  • 像素点的值大于最高阈值,那肯定是边缘(上图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的内容会再更新,大家有什么需要的也可以留言告诉我,我也会考虑更新的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容