天天看點

python opencv圖像處理(七)

圖像平滑(濾波)處理

這裡先介紹一下圖像的一些問題

每一幅圖像都包含某種程度的噪聲,噪聲可以了解為由一種或者多種原因造成的灰階值的随機變化,如由光子通量的随機性造成的噪聲等等。

而圖像平滑技術或者是圖像濾波技術就是用來處理圖像上的噪聲,其中,能夠具備邊緣保持作用的圖像平滑處理,成為了大家關注的重點。

本文會介紹 OpenCV 中提供的圖像平滑的 4 個算法:

  • 均值濾波
  • 方框濾波
  • 高斯濾波
  • 中值濾波

先給之前的小姐姐做一個噪聲處理吧

代碼如下:

import cv2 as cv
import numpy as np

# 讀取圖檔
img = cv.imread("data.jpg", cv.IMREAD_UNCHANGED)
rows, cols, chn = img.shape

# 加噪聲
for i in range(5000):
    x = np.random.randint(0, rows)
    y = np.random.randint(0, cols)
    img[x, y, :] = 255

cv.imshow("noise", img)

# 圖像儲存
cv.imwrite("maliao_noise.jpg", img)

# 等待顯示
cv.waitKey()
cv.destroyAllWindows()      

在這裡随機将一些像素值改問255,白色

python opencv圖像處理(七)

2D圖像卷積

在介紹濾波之前先簡單介紹下 2D 圖像卷積,圖像卷積其實就是圖像過濾。

圖像過濾的時候可以使用各種低通濾波器( LPF ),高通濾波器( HPF )等對圖像進行過濾。

低通濾波器( LPF )有助于消除噪聲,但是會使圖像模糊。

高通濾波器( HPF )有助于在圖像中找到邊緣。

OpenCV 為我們提供了一個函數 filter2D() 來将核心與圖像進行卷積。

我們嘗試對圖像進行平均濾波, 5 x 5 平均濾波器核心如下:

python opencv圖像處理(七)

具體操作如下:

我們保持這個核心在一個像素上,将所有低于這個核心的 25 個像素相加,取其平均值,然後用新的平均值替換中心像素。它将對圖像中的所有像素繼續此操作,完整的示例代碼如下:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

# 讀取圖檔
img = cv.imread("data.jpg", cv.IMREAD_UNCHANGED)
rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)

kernel = np.ones((5,5),np.float32)/25

dst = cv.filter2D(rgb_img, -1, kernel)

titles = ['Source Image', 'filter2D Image']
images = [rgb_img, dst]

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()      
python opencv圖像處理(七)

均值濾波

均值濾波是指任意一點的像素值,都是周圍 N * M 個像素值的均值。

其實均值濾波和上面的那個圖像卷積的示例,做了同樣的事情,我隻是用 filter2D() 這個方法手動完成了均值濾波,實際上 OpenCV 為我們提供了專門的均值濾波的方法,前面圖像卷積沒有看明白的同學,可以再一遍均值濾波,我盡量把這個事情整的明白的。

還是來畫個圖吧:

python opencv圖像處理(七)

中間那個紅色的方框裡面的值,是周圍 25 個格子區域中的像素的和去除以 25 ,這個公式是下面這樣的:

python opencv圖像處理(七)

上面這個 5 * 5 的矩陣稱為核,針對原始圖像内的像素點,采用核進行處理,得到結果圖像。

這個核我們可以自定義大小,比如 5 * 5 ,3 * 3 , 10 * 10 等等,具體定義多大完全看療效。

OpenCV 為我提供了 blur() 方法用作實作均值濾波,原函數如下:

def blur(src, ksize, dst=None, anchor=None, borderType=None)      
  • kSize:核心參數,其實就是圖檔進行卷積的時候相乘的那個矩陣,具體的卷積是如何算的,網上有很多,我這裡就不介紹了,所得到的圖像是模糊的,而且圖像其實是按照原來的比例缺少了(原圖像-核心參數+1)^2個單元格。
  • anchor: Point 類型,即錨點,有預設值 Point(-1, -1) ,當坐标為負值,就表示取核的中心。
  • borderType: Int 類型,用于推斷圖像外部像素的某種邊界模式,有預設值 BORDER_DEFAULT 。

接下來是均值濾波的示例代碼:

import cv2 as cv
import matplotlib.pyplot as plt

# 讀取圖檔
img = cv.imread("data.jpg", cv.IMREAD_UNCHANGED)
rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)

# 均值濾波
blur_img = cv.blur(rgb_img, (3, 3))
# blur_img = cv.blur(img, (5, 5))
# blur_img = cv.blur(img, (10, 10))
# blur_img = cv.blur(img, (20, 20))

titles = ['Source Image', 'Blur Image']
images = [rgb_img, blur_img]

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()      
python opencv圖像處理(七)

這個降噪的效果好像沒有前面 2D 卷積的那個降噪效果好,但是圖像更為清晰,因為我在這個示例中使用了更小的核 3 3 的核,順便我也試了下大核,比如代碼中注釋掉的 10 10 的核或者 20 * 20 的核,實時證明,核越大降噪效果越好,但是相反的是圖像會越模糊。

方框濾波

方框濾波和均值濾波核基本一緻,其中的差別是需不需要進行歸一化處理。

什麼是歸一化處理等下再說,我們先看方框濾波的原函數:

def boxFilter(src, ddepth, ksize, dst=None, anchor=None, normalize=None, borderType=None)      
  • src: 原始圖像。
  • ddepth: Int 類型,目标圖像深度,通常用 -1 表示與原始圖像一緻。 kSize: 核心參數。
  • dst:輸出與 src 大小和類型相同的圖像。
  • anchor: Point 類型,即錨點,有預設值 Point(-1, -1) 。
  • normalize: Int 類型,表示是否對目标圖像進行歸一化處理。

當 normalize 為 true 時,需要執行均值化處理。

當 normalize 為 false 時,不進行均值化處理,實際上是求周圍各像素的和,很容易發生溢出,溢出時均為白色,對應像素值為 255 。

完整示例代碼如下:

import cv2 as cv
import matplotlib.pyplot as plt

# 讀取圖檔
img = cv.imread('data.jpg')
source = cv.cvtColor(img, cv.COLOR_BGR2RGB)

# 方框濾波
result = cv.boxFilter(source, -1, (5, 5), normalize = 1)

# 顯示圖形
titles = ['Source Image', 'BoxFilter Image']
images = [source, result]

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()      
python opencv圖像處理(七)

高斯濾波

為了克服簡單局部平均法的弊端(圖像模糊),目前已提出許多保持邊緣、細節的局部平滑算法。它們的出發點都集中在如何選擇鄰域的大小、形狀和方向、參數加平均及鄰域各店的權重系數等。

在高斯濾波的方法中,實際上是把卷積核換成了高斯核,那麼什麼是高斯核呢?

簡單來講就是方框還是那個方框,原來每個方框裡面的權是相等的,大家最後取平均,現在變成了高斯分布的,方框中心的那個權值最大,其餘方框根據距離中心元素的距離遞減,構成一個高斯小山包,這樣取到的值就變成了權重平均。

下圖是所示的是 3 3 和 5 5 領域的高斯核。

python opencv圖像處理(七)

高斯濾波是在 OpenCV 中是由 GaussianBlur() 方法進行實作的,它的原函數如下:

def GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None)      
  • sigmaX: 表示 X 方向方差。

這裡需要注意的是 ksize 核大小,在高斯核當中,核 (N, N) 必須是奇數, X 方向方差主要控制權重。

完整的示例代碼如下

import cv2 as cv
import matplotlib.pyplot as plt

# 讀取圖檔
img = cv.imread('data.jpg')
source = cv.cvtColor(img, cv.COLOR_BGR2RGB)

# 方框濾波
result = cv.GaussianBlur(source, (3, 3), 0)

# 顯示圖形
titles = ['Source Image', 'GaussianBlur Image']
images = [source, result]

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()      
python opencv圖像處理(七)

中值濾波

在使用鄰域平均法去噪的同時也使得邊界變得模糊。

而中值濾波是非線性的圖像處理方法,在去噪的同時可以兼顧到邊界資訊的保留。

中值濾波具體的做法是選一個含有奇數點的視窗 W ,将這個視窗在圖像上掃描,把視窗中所含的像素點按灰階級的升或降序排列,取位于中間的灰階值來代替該點的灰階值。

下圖是一個一維的視窗的濾波過程:

python opencv圖像處理(七)

在 OpenCV 中,主要是通過調用 medianBlur() 來實作中值濾波,它的原函數如下:

def medianBlur(src, ksize, dst=None)      

示例代碼如下:

import cv2 as cv
import matplotlib.pyplot as plt

# 讀取圖檔
img = cv.imread('data.jpg')
source = cv.cvtColor(img, cv.COLOR_BGR2RGB)

# 方框濾波
result = cv.medianBlur(source, 3)

# 顯示圖形
titles = ['Source Image', 'medianBlur Image']
images = [source, result]

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

繼續閱讀