OpenCV學習筆記1(二維卷積,形態學運算,Zip和Enumerate)

OpenCV是一套採用C/C++編寫的開源跨平臺計算機視覺庫,它提供了兩套Python調用接口。其一是cv2模塊:針對OpenCV 2.x API創建的,它直接採用NumPy的數組對象表示圖
其二是爲了兼容OpenCV 1.x API,在cv模塊下提供了原來的OpenCV 1.x API的擴展
cv(from cv2 import cv)。

1.圖像的輸入與輸出

"圖像的輸入(imread)輸出(write)"
import cc2
img=cv2.imread("路徑")#從指定的文件路徑讀入圖像數據,它返回的是一個uint8的數組(512,512,3)
cv2.write("路徑")#將圖像數據寫入文件
#的彩色圖像通過cvtColor()轉換成表示黑白圖像的二維數組,所有的顏色轉換常數均以COLOR_開頭
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
print img_gray.shape #(512,512)

注意:imread()函數讀入彩色圖像時,第2軸按照藍、綠、紅的順序排列。這種順序與matplotlib的imshow()函數所需的三通道的順序正好相反。因此,若需要在matplotlib中顯示圖像,則需要反轉第2軸:img[:, :, ::-1]。

2.二維卷積

圖像處理中最基本的算法就是將圖像和某個卷積核進行卷積,使用不同的卷積核可以得到各種不同的圖像處理效果。OpenCV提供了filter2D()來完成圖像的卷積運算,調用方式如下:

filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]])

參數列表:
src參數是原始圖像
dst參數是目標圖像,若省略dst參數,將創造一個新的數組來保存圖像數據
ddepth參數用於指定目標圖像的每個通道的數據類型,負數表示其數據類型和原始圖像相同
kernel參數設置卷積核,它將與原始圖像的每個通道進行卷積計算,並將結果存儲到目標圖像的對應通道中
anchor參數指定卷積核的錨點位置,當它爲默認值(-1, -1)時,以卷積核的中心爲錨點
delta參數指定在將計算結果存儲到dst中之前對數值的偏移量。

filter2D()的卷積運算過程如下:
(1)對於圖像src中的每個像素點,讓它和卷積核的錨點對齊。
(2)對於圖像src中與卷積核重疊的部分,計算像素值和卷積核的值乘積。
(3)圖像dst中的像素點的值爲上面所有乘積的總和。

顯然當卷積核的尺寸很大時,上述方法的運算速度將會很慢。因此對於較大的卷積核,filter2D()將使用離散傅立葉變換相關的算法進行卷積運算。

import cv2
import numpy as np
import matplotlib.pyplot as pl
#import matplotlib as pl,pl.subplots失敗
pl.rcParams["font.family"] = "SimHei"#直接修改配置字典,設置默認字體
src = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/LYF.jpg")
kernels = [
     (u"低通濾波器",np.array([[1, 1, 1],[1, 2, 1],[1, 1, 1]])*0.1),
     (u"高通濾波器",np.array([[0.0,-1, 0],[-1,5,-1],[0,-1,0]])),
     (u"邊緣檢測",np.array([[-1.0, -1, -1],[-1, 8, -1],[-1, -1, -1]]))
]#列表,元素爲元胞
index = 0,
fig, axes = pl.subplots(1, 3, figsize=(12, 4.3))
for ax, (name, kernel) in zip(axes, kernels):#zip 將數組和元胞放在一起
    print("ax:",ax,"\n","name",name,"\n","knl",kernel)
    dst=cv2.filter2D(src, -1, kernel)
    # 由於matplotlib的顏色順序和OpenCV的順序相反
    ax.imshow(dst[:, :, ::-1])#因爲ax是子圖,是matplotlib中的,故需要換順序
    ax.set_title(name)
    ax.axis("off")
fig.subplots_adjust(0.02, 0, 0.98, 1, 0.02, 0)#調節窗口大小

在這裏插入圖片描述
備註:Zip和Enumerate的區別

"""1.zip可以在處理循環時用到,返回一個將多個可迭代對象組合成一個元組序列的迭代器。
每個元組都包含所有可迭代對象中該位置的元素。"""
for i,p in zip(['a', 'b', 'c'], [1, 2, 3]):
    print(i,p)
#a 1
#b 2
#c 3

"2.zip除了可以將兩個列表組合到一起之外,還可以使用星號拆封列表,返回的是單個元組"
  some_list = [('a', 1), ('b', 2), ('c', 3)]
  letters, nums = zip(*some_list)
  print(letters) # ('a', 'b', 'c')
  print(nums) # (1, 2, 3)
  
"""3.enumerate 是一個會返回元組迭代器的內置函數,這些元組包含列表的索引和值。
當你需要在循環中獲取可迭代對象的每個元素及其"索引"時,將經常用到該函數。
"""
  letters = ['a', 'b', 'c']
  for i, letter in enumerate(letters):
      print(i, letter)
#0 a
#1 b
#2 c

有些特殊的卷積核可以表示成一個列矢量和一個行矢量的乘積,這時只需要將原始圖像按順序與這兩個矢量進行卷積,所得到的最終結果和直接與卷積核進行卷積的結果相同
由於將一個N×M的矩陣分解成了兩個N×1和1×M的矩陣,因此對於較大的卷積核能大幅度地提高計算速度
OpenCV提供了sepFilter2D()來進行這種分步卷積,調用參數如下:

sepFilter2D(src, ddepth, kernelX, kernelY[, dst[, anchor[, delta[, borderType]]]])

其中kernelX和kernelY分別爲行卷積核和列卷積核。

import numpy as np
img = np.random.rand(1000,1000) 
pl.imshow(img)
#行卷積核,列卷積核
row = cv2.getGaussianKernel(7, -1) 
col = cv2.getGaussianKernel(5, -1)
#卷積核
kernel = np.dot(col[:], row[:].T) 

%time img2 = cv2.filter2D(img, -1, kernel) 
%time img3 = cv2.sepFilter2D(img, -1, row, col) 
print ("error={}".format(np.max(np.abs(img2[:] - img3[:]))))

在這裏插入圖片描述在這裏插入圖片描述
由於卷積計算很常用,因此OpenCV提供了一些高級函數來直接完成與某種特定卷積核的卷積計算。例如平均模糊blur()、高斯模糊GaussianBlur()、用於邊緣檢測的差分運算Sobel()和Laplacian()等。關於這些函數的用法請讀者自行參考OpenCV的文檔,這裏就不再舉例了。

3.形態學運算

3.1基本概念

形態學運算是針對二值圖像依據數學形態學(Mathematical Morphology)的集合論方法發展起來的圖像處理方法。
通常,形態學圖像處理表現爲一種鄰域運算形式,一種特殊定義的領域稱之爲“結構元素”,在每個像素位置它與二值圖像對應的區域進行特定的邏輯運算,邏輯運算的結果爲輸出圖像的響應像素。
形態學處理的核心就是定義結構元素,在OpenCV-Python中,可以使用其自帶的getStructuringElement函數,也可以直接使用NumPy的ndarray來定義一個結構元素。
(形象圖如下:)
在這裏插入圖片描述
簡單來講,形態學操作就是基於形狀的一系列圖像處理操作,通過將結構元素作用於輸入圖像來產生輸出圖像。

最基本的形態學操作有:腐蝕與膨脹(Erosion和Dilation)。
廣泛應用於:消除噪聲、分割(isolate)獨立的圖像元素以及連接(join)相鄰的元素和尋找圖像中的明顯的極大值區域或極小值區域。

膨脹:求局部最大值,把二值圖像各1像素連接成分的邊界擴大一層(填充邊緣或0像素內部的孔)
腐蝕:求局部最小值,把二值圖像各1像素連接成分的邊界點去掉從而縮小一層(可提取骨幹信息,去掉毛刺,去掉孤立的0像素)有一個1結果爲1,故小白點周圍是黑色,有一個白點可以去掉小白點,
開運算:先腐蝕再膨脹,可以去掉目標外的孤立點。
閉運算:先膨脹再腐蝕,可以去掉目標內的孔。
頂帽:原圖像與“開運算“的結果圖之差,用於背景提取。
黑帽:原圖像與“閉運算“的結果圖之差。
形態學梯度:實爲膨脹圖與腐蝕圖之差。突出高亮區域的外圍。

3.2主要函數

膨脹dilate(),腐蝕erode():兩者參數相同

dilate(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])

	src參數是原始圖像;
	kernel參數是結構元素,它指定針對哪些周圍像素進行計算;
	anchor參數指定錨點的位置,其默認值爲結構元素的中心;
	iterations參數指定處理次數

morphologyEx()使用膨脹和收縮實現一些更高級的形態學處理。
它比dilate()多了一個op參數,用於指定運算的類型。
在這裏插入圖片描述

3.3操作實現

將彩色圖像轉化爲二值圖像:必須灰度圖像過渡,注意在opencv中黑色是0,白色是255

import cv2
import matplotlib.pyplot as pl
img = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/LYF.jpg")
imgg=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#彩色圖像轉爲灰度圖
fig, axes = pl.subplots(1, 2, figsize=(7, 8))
#待處理的圖像,閾值,最大值,參數
imge1,imge2=cv2.threshold(imgg,80,255,cv2.THRESH_BINARY_INV)
#pl.imshow(imgg)
axes[0].imshow(imgg,cmap='gray')#顯示灰度圖
axes[1].imshow(imge2,cmap='binary')#顯示二值圖

在這裏插入圖片描述

*注意在opencv中黑色是0,白色是255

腐蝕,取局部極小值,故去掉了小黑點;膨脹取局部極大值,故去掉了小白點。
腐蝕、膨脹、開閉運算、形態學梯度代碼如下

pl.rcParams["font.family"] = "SimHei"#直接修改配置字典,設置默認字體
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))  # 橢圓結構
#[[0, 1, 0],
# [1, 1, 1],
# [0, 1, 0]]
#kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))  # 十字結構
#kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))  # 矩形結構
fig, axes = pl.subplots(2, 3, figsize=(11, 13))
a0,a1,a2,a3,a4,a5=axes[0,0],axes[0,1],axes[0,2],axes[1,0],axes[1,1],axes[1,2]
a0.set_title("二值原圖")
a0.imshow(imge2,cmap='binary')
"腐蝕(去白小點)"
img1 = cv2.erode(imge2, kernel)  # 腐蝕
a1.set_title("腐蝕去(小黑點)")
a1.imshow(img1,cmap='binary')
"膨脹(去黑小點)"
img2 = cv2.dilate(imge2, kernel)  # 膨脹
a2.set_title("膨脹去(小白點)")
a2.imshow(img2,cmap='binary')
"先腐蝕後膨脹叫開運算(因爲先腐蝕會分開物體,這樣容易記住),其作用是:分離物體,消除小區域。"
opening = cv2.morphologyEx(imge2, cv2.MORPH_OPEN, kernel)  # 開運算
a3.set_title("開運算--去除目標外的點")
a3.imshow(opening,cmap='binary')
"閉運算則相反:先膨脹後腐蝕(先膨脹會使白色的部分擴張,以至於消除閉合物體裏面的小黑洞,所以叫閉運算)"
closing = cv2.morphologyEx(imge2, cv2.MORPH_CLOSE, kernel)  # 閉運算
a4.set_title("閉運算--去除目標內的孔")
a4.imshow(closing,cmap='binary')
"形態學梯度:膨脹圖減去腐蝕圖,dilation - erosion,這樣會得到物體的輪廓"
gradient = cv2.morphologyEx(imge2, cv2.MORPH_GRADIENT, kernel)  # 形態學梯度
a5.set_title("物體的輪廓")
a5.imshow(gradient,cmap='binary')

在這裏插入圖片描述

pl.rcParams["font.family"] = "SimHei"#直接修改配置字典,設置默認字體
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))  # 橢圓結構
#kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))  # 十字結構
#kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))  # 矩形結構
fig,axes = pl.subplots(1, 3, figsize=(11, 13))
a0,a1,a2=axes[0],axes[1],axes[2]
a0.set_title("二值原圖")
a0.imshow(imge2,cmap='binary')
"頂帽:原圖減去開運算後的圖"
tophat = cv2.morphologyEx(imge2, cv2.MORPH_TOPHAT, kernel)
a1.set_title("頂帽目標外的點")
a1.imshow(tophat,cmap='binary')
"黑帽:閉運算減去原圖的圖"
blackhat = cv2.morphologyEx(imge2, cv2.MORPH_BLACKHAT, kernel)
a2.set_title("黑帽目標內的孔")
a2.imshow(blackhat,cmap='binary')

在這裏插入圖片描述

4.填充

區域填充實際上就是條件膨脹。填充函數floodFill()在圖像處理中經常用於標識或分離圖像中的某些特定部分
它的調用方式爲:

floodFill(image, mask, seedPoint, newVal[, loDiff[, upDiff[, flags]]])

image參數是需要填充的圖像;
seedPoint參數爲填充的起始點,我們稱之爲種子點;
newVal參數爲填充所使用的顏色值;
loDiff和upDiff參數是填充的下限和上限容差;
flags參數是填充的算法標誌。

填充從seedPoint指定的種子座標開始,圖像中與當前的填充區域顏色“相近”的點將被添加進填充區域,從而逐步擴大填充區域,直到沒有新的點能添加進填充區域爲止。
顏色相近的判斷方法有兩種:在這裏插入圖片描述
注意:

 當mask參數不爲None時,它是一個寬和高比image都大兩個像素的單通道8位圖像。image圖像中的像素  
 (x,y)與mask中(x+1,y+1)的對應。填充只針對mask中的值爲0的像素進行。進行填充之後,mask中所有
 被填充的像素將被賦值爲1。如果只希望修改mask,而不對原始圖像進行填充,可以開啓flags標誌中的
 FLOODFILL_MASK_ONLY。
 當mask參數爲空時,所有的填充操作在原圖上進行,會修改原圖,可見下面示例。
import cv2
import matplotlib.pyplot as pl
pl.rcParams["font.family"] = "SimHei"#直接修改配置字典,設置默認字體
img = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/LYF.jpg")
seed1 = 344, 188
seed2 = 187, 144
diff = (13, 13, 13)
fig, axes = pl.subplots(1, 3, figsize=(9, 4))
axes[0].set_title("原圖")
axes[0].imshow(img)
h, w = img.shape[:2]#(1008, 720, 3)  h=1008,w=720
mask = np.zeros((h+2, w+2), np.uint8)#mask大小(1010, 722),無符號八位整型,表示範圍是[0, 255]的整數
cv2.floodFill(img, mask, seed1, (0, 0, 0), diff, diff, cv2.FLOODFILL_MASK_ONLY)
axes[1].set_title("黑色填充")
axes[1].imshow(~mask, cmap="gray")
cv2.floodFill(img, None, seed2, (0, 255, 0), diff, diff)
axes[2].set_title("綠色填充")
axes[2].imshow(img)

在這裏插入圖片描述

5.去瑕疵

使用inpaint()可以從圖像上去除指定區域中的物體,可以用於去除圖像上的水印、劃痕、污漬等瑕疵。
它的調用參數如下:

inpaint(src, inpaintMask, inpaintRadius, flags[, dst])

src參數是原始圖像,
inpaintMask參數是大小和src相同的單通道8位圖像,其中不爲0的像素表示需要去除的區域。
dst參數用於保存處理結果。
inpaintRange參數是處理半徑,半徑越大處理時間越長,結果越平滑。
flags參數選擇inpaint的算法,目前有兩個候選算法:INPAINT_NS和INPIANT_TELEA。
import cv2
import matplotlib.pyplot as pl
pl.rcParams["font.family"] = "SimHei"#直接修改配置字典,設置默認字體
img = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/LYF.jpg")
imgg = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/LYF.jpg",0)
imge1,imge2=cv2.threshold(imgg,80,255,cv2.THRESH_BINARY_INV)#灰度圖轉二值圖
dst = cv2.inpaint(img, imge2, 3, cv2.INPAINT_TELEA)#基於快速行進算法
dst1 = cv2.inpaint(img, imge2, 3, cv2.INPAINT_NS)#基於流體動力學並使用了偏微分方程
fig, axes = pl.subplots(1, 3, figsize=(9, 4))
axes[0].set_title("原圖")
axes[0].imshow(img)
axes[1].set_title("基於快速行進算法")
axes[1].imshow(dst)
axes[2].set_title("基於流體動力學")
axes[2].imshow(dst1)

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章