1. 程式人生 > >Python 影象處理 OpenCV (9):影象處理形態學開運算、閉運算以及梯度運算

Python 影象處理 OpenCV (9):影象處理形態學開運算、閉運算以及梯度運算

![](https://cdn.geekdigging.com/opencv/opencv_header.png) 前文傳送門: [「Python 影象處理 OpenCV (1):入門」](https://www.geekdigging.com/2020/05/17/5513454552/) [「Python 影象處理 OpenCV (2):畫素處理與 Numpy 操作以及 Matplotlib 顯示影象」](https://www.geekdigging.com/2020/05/18/4936041986/) [「Python 影象處理 OpenCV (3):影象屬性、影象感興趣 ROI 區域及通道處理」](https://www.geekdigging.com/2020/05/19/1227329671/) [「Python 影象處理 OpenCV (4):影象算數運算以及修改顏色空間」](https://www.geekdigging.com/2020/05/21/1757913240/) [「Python 影象處理 OpenCV (5):影象的幾何變換」](https://www.geekdigging.com/2020/05/23/4331122737/) [「Python 影象處理 OpenCV (6):影象的閾值處理」](https://www.geekdigging.com/2020/06/03/6651375581/) [「Python 影象處理 OpenCV (7):影象平滑(濾波)處理」](https://www.geekdigging.com/2020/06/06/8676263283/) [「Python 影象處理 OpenCV (8):影象腐蝕與影象膨脹」](https://www.geekdigging.com/2020/06/08/5731186312/) ## 引言 前面介紹了影象形態學的兩種基礎演算法,影象腐蝕和影象膨脹,本篇接著介紹影象形態學中的開運算、閉運算以及梯度運算。 由於內容的連貫性,請先閱讀前文[「Python 影象處理 OpenCV (8):影象腐蝕與影象膨脹」](https://www.geekdigging.com/2020/06/08/5731186312/),瞭解清楚影象的腐蝕與膨脹基礎原理。 不然真的沒辦法理解開運算和閉運算。 第一件事情還是給影象增加噪聲,思路沿用之前加噪聲的思路,使用 Numpy 給圖片新增黑白兩種噪聲點,程式碼如下: ```python import cv2 as cv import numpy as np import matplotlib.pyplot as plt # 讀取圖片 img = cv.imread("demo.png", cv.IMREAD_UNCHANGED) source = cv.cvtColor(img, cv.COLOR_BGR2RGB) rows, cols, chn = source.shape # 加噪聲-白點噪聲 for i in range(500): x = np.random.randint(0, rows) y = np.random.randint(0, cols) source[x, y, :] = 255 # 影象儲存 白點噪聲影象 cv.imwrite("demo_noise_white.jpg", source) print("白點噪聲新增完成") # 重新讀取影象 img1 = cv.imread("demo.png", cv.IMREAD_UNCHANGED) source1 = cv.cvtColor(img1, cv.COLOR_BGR2RGB) # 加噪聲-黑點噪聲 for i in range(1000): x = np.random.randint(0, rows) y = np.random.randint(0, cols) source1[x, y, :] = 0 # 影象儲存 黑點噪聲影象 cv.imwrite("demo_noise_black.jpg", source1) print("黑點噪聲新增完成") # 顯示結果 titles = ['White Img','Black Img'] images = [source, source1] # matplotlib 繪圖 for i in range(2): plt.subplot(1, 2, i+1), plt.imshow(images[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show() ``` ![](https://cdn.geekdigging.com/opencv/09/noise_result.png) ## 形態學開運算 影象開運算實際上是一個組合運算,開運算是影象先進行腐蝕,再進行膨脹的運算。 影象被腐蝕後,去除了噪聲,但是也壓縮了影象;接著對腐蝕過的影象進行膨脹處理,使得剛才在腐蝕過程中被壓縮的影象得以恢復原狀。 下面是一個影象開運算的流程圖: ![](https://cdn.geekdigging.com/opencv/09/open_liucheng.png) 開運算的一些特性: * 開運算能夠除去孤立的小點,毛刺和小橋,而總的位置和形狀不便。 * 開運算是一個基於幾何運算的濾波器。 * 結構元素大小的不同將導致濾波效果的不同。 * 不同的結構元素的選擇導致了不同的分割,即提取出不同的特徵。 我們先不管開運算 OpenCV 為我們提供的函式是什麼,先使用前面介紹過的影象腐蝕與膨脹處理看下結果: ```python import cv2 as cv import numpy as np import matplotlib.pyplot as plt # 讀取圖片 source = cv.imread("demo_noise_white.jpg", cv.IMREAD_GRAYSCALE) # 設定卷積核 kernel = np.ones((5, 5),np.uint8) # 影象腐蝕 erode_img = cv.erode(source, kernel) # 影象膨脹 dilate_result = cv.dilate(erode_img, kernel) # 顯示結果 titles = ['Source Img','Erode Img','Dilate Img'] images = [source, erode_img, dilate_result] # matplotlib 繪圖 for i in range(3): plt.subplot(1, 3, i+1), plt.imshow(images[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show() ``` ![](https://cdn.geekdigging.com/opencv/09/open_result_1.png) 可以看到降噪的效果還是不錯的。 接著看 OpenCV 為開運算提供的函式。 影象開運算主要使用到的函式是 `morphologyEx()` 它是形態學擴充套件的一組函式,而其中的 `cv.MORPH_OPEN` 對應的是開運算。 使用時語法如下: ```python dst = cv.morphologyEx(src, cv.MORPH_OPEN, kernel) ``` * src: 原圖形 * cv2.MORPH_OPEN: 表示開運算 * kernel: 卷積核 我們再使用 `morphologyEx()` 函式去重新實現下剛才的影象開運算,看下和之前的結果有啥區別: ```python import cv2 as cv import numpy as np import matplotlib.pyplot as plt # 讀取圖片 source = cv.imread("demo_noise_white.jpg", cv.IMREAD_GRAYSCALE) # 設定卷積核 kernel = np.ones((5, 5),np.uint8) #影象開運算 dst = cv.morphologyEx(source, cv.MORPH_OPEN, kernel) # 顯示結果 titles = ['Source Img','Dst Img'] images = [source, dst] # matplotlib 繪圖 for i in range(2): plt.subplot(1, 2, i+1), plt.imshow(images[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show() ``` ![](https://cdn.geekdigging.com/opencv/09/open_result_2.png) 至少從肉眼的角度上看不出來和之前的方式有啥區別,實際上也沒啥區別。 ## 形態學閉運算 與開運算相反的是閉運算,閉運算是影象先膨脹,後腐蝕,它有助於關閉前景物體內部的小孔,或物體上的小黑點。 先看下影象閉運算的流程圖: ![](https://cdn.geekdigging.com/opencv/09/close_liucheng.png) 閉運算的一些特性: * 閉運算能夠填平小湖(即小孔),彌合小裂縫,而總的位置和形狀不變。 * 閉運算是通過填充影象的凹角來濾波影象的。 * 結構元素大小的不同將導致濾波效果的不同。 * 不同結構元素的選擇導致了不同的分割。 首先還是用 `dilate()` 和 `erode()` 函式實現一下影象閉運算,程式碼如下: ```python import cv2 as cv import numpy as np import matplotlib.pyplot as plt # 讀取圖片 source = cv.imread("demo_noise_black.jpg", cv.IMREAD_GRAYSCALE) # 設定卷積核 kernel = np.ones((5, 5),np.uint8) # 影象膨脹 dilate_result = cv.dilate(source, kernel) # 影象腐蝕 erode_img = cv.erode(dilate_result, kernel) # 顯示結果 titles = ['Source Img','Dilate Img','Erode Img'] images = [source, dilate_result, erode_img] # matplotlib 繪圖 for i in range(3): plt.subplot(1, 3, i+1), plt.imshow(images[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show() ``` ![](https://cdn.geekdigging.com/opencv/09/close_result_1.png) 如果想要使用形態學擴充套件的函式 `morphologyEx()` 則需要把裡面的引數換成 `MORPH_CLOSE` ,同樣,既然是形態學擴充套件函式,那麼影象腐蝕和影象膨脹也有對應的引數: * 影象腐蝕: `MORPH_ERODE` * 影象膨脹: `MORPH_DILATE` 接著還是使用 `MORPH_CLOSE` 引數來實現下影象的閉運算: ```python import cv2 as cv import numpy as np import matplotlib.pyplot as plt # 讀取圖片 source = cv.imread("demo_noise_black.jpg", cv.IMREAD_GRAYSCALE) # 設定卷積核 kernel = np.ones((5, 5),np.uint8) # 影象閉運算 dst = cv.morphologyEx(source, cv.MORPH_CLOSE, kernel) # 顯示結果 titles = ['Source Img','Dst Img'] images = [source, dst] # matplotlib 繪圖 for i in range(2): plt.subplot(1, 2, i+1), plt.imshow(images[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show() ``` ![](https://cdn.geekdigging.com/opencv/09/close_result_2.png) ## 形態學梯度運算 影象形態學的梯度運算和前面的開運算閉運算是一樣的,都是組合函式。 梯度運算實際上是影象膨脹減去影象腐蝕後的結果,最終我們得到的是一個類似於影象輪廓的圖形。 ![](https://cdn.geekdigging.com/opencv/09/gradient_result_1.png) 梯度運算在 `morphologyEx()` 函式中的引數是 `MORPH_GRADIENT` ,示例程式碼如下: ```python import cv2 as cv import numpy as np import matplotlib.pyplot as plt # 讀取圖片 source = cv.imread("demo.png", cv.IMREAD_GRAYSCALE) # 設定卷積核 kernel = np.ones((5, 5), np.uint8) # 影象梯度運算 dst = cv.morphologyEx(source, cv.MORPH_GRADIENT, kernel) # 顯示結果 titles = ['Source Img','Dst Img'] images = [source, dst] # matplotlib 繪圖 for i in range(2): plt.subplot(1, 2, i+1), plt.imshow(images[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show() ``` ![](https://cdn.geekdigging.com/opencv/09/gradient_result.png) ## 示例程式碼 如果有需要獲取原始碼的同學可以在公眾號回覆「OpenCV」進行獲取。 ## 參考 http://www.woshicver.com/ https://blog.csdn.net/Eastmount/article/details/83651172 https://blog.csdn.net/hanshanbuleng/article/details/