作者:Muhammad Junaid Khalid
翻譯:老齊
與本文相關的圖書推薦:《數據準備和特徵工程》
在本文中,將學習如何使用Python語言進行圖像處理,我們不會局限於一個單獨的庫或框架,然而,有一個庫的使用率將會是最高的,那就是OpenCV。我們一開始會討論一些圖像處理,然後繼續探討不同的應用/場景,也就是圖像處理的用武之地。開始吧!
什麼是圖像處理?在深入研究圖像處理的方法之前,重要的是要了解什麼是圖像處理,特別是這項技術在處理大量圖片方面的角色。圖像處理完整的說法是「數字圖像處理」,經常使用圖像處理的領域是「計算機視覺」。對這兩個術語不要混淆,圖像處理算法和計算機視覺(CV)算法都以圖像為輸入,然而,在圖像處理中,輸出也是圖像,而在計算機視覺中,輸出可以是關於圖像的一些特徵或信息。
為什麼需要圖像處理?我們收集或生成的數據大部分是原始數據,也就是說,由於一些可能的原因,這些數據不適合直接用於應用程式。因此,我們需要首先分析它,執行必要的預處理,然後使用它——特別推薦《數據準備和特徵工程》,此書即為這方面最佳讀物。
例如,我們正在嘗試構建一個關於貓的分類器。我們的程序會把一個圖像作為輸入,然後告訴我們這個圖像是否包含一隻貓。構建這個分類器的第一步是收集數百張含有貓的圖片。一個常見的問題是,收集的所有圖片的大小都不相同,因此在將它們提供給模型進行訓練之前,需要調整它們的大小或者把它們進行預處理,使尺寸符合標準。
為什麼圖像處理對於任何計算機視覺應用序都是必不可少的?以上提到的只是眾多原因之一。
預備知識為了輕鬆地學習本文內容,你需要已經具備如下知識。
首先,應該具備一定的程式語言技能,本文使用的是Python語言,如果尚未掌握此語言,推薦閱讀《跟老齊學Python:輕鬆入門》或《Python大學實用教程》。
其次,你應該了解什麼是機器學習以及它的基本工作原理。因為在本文中我們將使用一些機器學習算法來進行圖像處理。
另外,如果你之前接觸過或掌握了OpenCV的基本知識,也會有所幫助。但這不是必需的。
還有,你一定要了解圖像在內存中究竟是如何表示的。每幅圖像都由一組像素表示,即像素值矩陣。對於灰度圖像,像素值的範圍是0到255,它們表示該像素的強度。例如,如果你有一個20×20維的圖像,它將由一個20x20的矩陣表示(像素值總共是400)。
如果你正在處理彩色圖像,你應該知道它有三個通道——紅、綠、藍(RGB)。因此,一個彩色圖像有三個這樣的矩陣。
安裝注意: 由於我們將通過Python使用OpenCV,所以你必須會實用它,前面推薦了關於Python的書籍。下面依次說明在不同作業系統中OpenCV的安裝方法:
$ pip install opencv-python$ brew install opencv3 --with-contrib --with-python3$ sudo apt-get install libopencv-dev python-opencv要檢查是否安裝成功,請在Python交互模式中運行以下命令:
import cv2
必備的基礎知識在進行圖像處理之前,要先做一些準備。
在本文中,我們將使用以下圖像:
注意: 為了在本文中顯示該圖像,對其進行了縮放,但是我們使用的圖像原始大小約為1180x786。
你可能注意到圖像現在是彩色的,這意味著它由三個顏色通道表示,即紅色、綠色和藍色。我們將把圖像轉換成灰度,並使用下面的代碼將圖像分割成單獨的通道。
找到圖像細節使用imread()函數加載圖像後,我們可以得到關於它的一些簡單屬性,比如像素的數量和尺寸:
import cv2
img = cv2.imread('rose.jpg')
print("Image Properties")
print("- Number of Pixels: " + str(img.size))
print("- Shape/Dimensions: " + str(img.shape))Output:
Image Properties
- Number of Pixels: 2782440
- Shape/Dimensions: (1180, 786, 3)
將圖像分割成單獨的通道現在,我們將使用OpenCV將圖像分割成紅色、綠色和藍色的部分,並顯示它們:
from google.colab.patches import cv2_imshow
blue, green, red = cv2.split(img) # Split the image into its channels
img_gs = cv2.imread('rose.jpg', cv2.IMREAD_GRAYSCALE) # Convert image to grayscale
cv2_imshow(red) # Display the red channel in the image
cv2_imshow(blue) # Display the red channel in the image
cv2_imshow(green) # Display the red channel in the image
cv2_imshow(img_gs) # Display the grayscale version of image為了簡單起見,我們只顯示灰度圖像。
圖像閾值
閾值的概念非常簡單。正如上面在圖像表示中所討論的,像素值可以是0到255之間的任何值。假設我們想要將一幅圖像轉二值化,即指定一個像素值為0或1。為此,我們可以設置閾值。例如,如果閾值(T)為125,那麼所有大於125的像素將被賦值為1,所有小於或等於該值的像素將被賦值為0。下面,我們通過代碼來更好地理解它。
將下面的圖像用上述方法進行轉換:
import cv2
# Read image
img = cv2.imread('image.png', 0)
# Perform binary thresholding on the image with T = 125
r, threshold = cv2.threshold(img, 125, 255, cv2.THRESH_BINARY)
cv2_imshow(threshold)輸出:
正如你所看到的, 二值化之後,出現了兩個區域,即黑色區域(像素值0)和白色區域(像素值1)。原來, 我們設置的閾值正好在圖像的中間,這就是為什麼黑白值在那裡被分割。
應用1:去除圖像中的噪聲現在你已經對圖像處理的概念和用途有了基本的了解,接下來讓我們來了解一下它的一些具體應用。
在大多數情況下,我們收集的原始數據有噪聲,也就是說,不需要的特徵使圖像很難被感知。雖然這些圖像可以直接用於特徵抽取,但是算法的準確性會受到很大的影響。這就是為什麼在將圖像傳遞給算法以獲得更好的精度之前,要對圖像進行處理的原因。
有許多不同類型的噪聲,如高斯噪聲,椒鹽噪聲等。我們可以通過應用濾波器來去除圖像中的噪聲,或者至少將其影響降到最低。在濾波器方面也有很多選擇,每一個濾波器都有不同的優點。因此,對於特定類型的噪聲來說,總有一個是最好的。
為了更好地理解這一點,我們將在上面的玫瑰色圖像的灰度版本中添加「鹽和胡椒粉」噪聲,然後嘗試使用不同的濾波器去除圖像中的噪聲,看看哪一個最適合這種類型。
import numpy as np
# Adding salt & pepper noise to an image
def salt_pepper(prob):
# Extract image dimensions
row, col = img_gs.shape
# Declare salt & pepper noise ratio
s_vs_p = 0.5
output = np.copy(img_gs)
# Apply salt noise on each pixel individually
num_salt = np.ceil(prob * img_gs.size * s_vs_p)
coords = [np.random.randint(0, i - 1, int(num_salt))
for i in img_gs.shape]
output[coords] = 1
# Apply pepper noise on each pixel individually
num_pepper = np.ceil(prob * img_gs.size * (1. - s_vs_p))
coords = [np.random.randint(0, i - 1, int(num_pepper))
for i in img_gs.shape]
output[coords] = 0
cv2_imshow(output)
return output
# Call salt & pepper function with probability = 0.5
# on the grayscale image of rose
sp_05 = salt_pepper(0.5)
# Store the resultant image as 'sp_05.jpg'
cv2.imwrite('sp_05.jpg', sp_05)好的,我們已經把噪聲添加到玫瑰圖像,這是它現在的樣子:
讓我們現在應用不同的濾波器,並記下觀察結果,即每個濾波器降噪的效果。
銳化濾波器# Create our sharpening kernel, the sum of all values must equal to one for uniformity
kernel_sharpening = np.array([[-1,-1,-1],
[-1, 9,-1],
[-1,-1,-1]])
# Applying the sharpening kernel to the grayscale image & displaying it.
print("\n\n--- Effects on S&P Noise Image with Probability 0.5 ---\n\n")
# Applying filter on image with salt & pepper noise
sharpened_img = cv2.filter2D(sp_05, -1, kernel_sharpening)
cv2_imshow(sharpened_img)在有椒鹽噪聲的圖像上應用濾波器得到的圖像如下所示。通過與原始灰度圖的對比,我們可以看出,它把圖像調得太亮了,也無法突出玫瑰上的亮點。因此,我們可以得出結論,銳化濾波器並不能去除噪聲。
銳化濾波器輸出:
中值濾波器
from scipy.ndimage import maximum_filter, minimum_filter
def midpoint(img):
maxf = maximum_filter(img, (3, 3))
minf = minimum_filter(img, (3, 3))
midpoint = (maxf + minf) / 2
cv2_imshow(midpoint)
print("\n\n---Effects on S&P Noise Image with Probability 0.5---\n\n")
midpoint(sp_05)在有噪聲的圖像上應用中值濾波器,得到的圖像如下所示。通過與原始灰度圖像的對比,我們可以看出,與上面的核方法一樣,圖像的亮度調高了很多,然而,它能夠突出玫瑰上的亮斑(即噪聲)。因此,我們可以說,中值濾波器是比銳化濾波器更好的選擇,但它仍然不能完全恢復原始圖像。
中值濾波器輸出:
逆諧波均值濾波器
注意: 對這些濾波器的工作原理的闡述,超出了本文範疇,讀者可以在網上搜索,我們還是從應用的層面來研究。
def contraharmonic_mean(img, size, Q):
num = np.power(img, Q + 1)
denom = np.power(img, Q)
kernel = np.full(size, 1.0)
result = cv2.filter2D(num, -1, kernel) / cv2.filter2D(denom, -1, kernel)
return result
print("\n\n--- Effects on S&P Noise Image with Probability 0.5 ---\n\n")
cv2_imshow(contraharmonic_mean(sp_05, (3,3), 0.5))在有椒鹽噪聲的圖像上應用逆諧波均值濾波器(https://en.wikipedia.org/wiki/Contraharmonic_mean)得到的圖像如下圖所示。通過與原始灰度圖像的對比,我們可以看到:它幾乎完美再現了原始圖像。它的強度或亮度級別與原圖是相同的,它突出了玫瑰上的亮點。因此,我們可以得出結論,逆諧波均值濾波器在處理椒鹽噪聲方面是非常有效的。
逆諧波均值濾波器輸出:
現在我們已經找到了最佳濾波器,它可以有效地把有噪聲的圖像恢復到原始圖像。我們可以繼續下一個應用了。
2:使用Canny算子進行邊緣檢測到目前為止,我們使用的玫瑰圖像的背景是不變的,也就是黑色的,因此,我們將把這個應用用於不同的圖像,以更好地展示算法的功效。原因是,如果背景是恆定的,邊緣檢測任務就變得相當簡單,這不是我們所希望的。
在本文開始部分,我們提到了一個關於貓的分類器。現在我們延用這個例子,看看圖像處理如何在其中扮演一個完整的角色。
在分類算法中,首先掃描圖像尋找「對象」。也就是說,當你輸入一幅圖像時,算法會找到圖像中的所有對象,然後將它們與你試圖尋找的對象進行特徵比較。對於貓分類器,它會將在圖像中找到的所有對象與貓圖像的特徵進行比較,如果找到匹配項,它會告訴我們輸入圖像中包含了一隻貓。
對於這個貓分類器,僅以一張貓的圖像為例,以下是我們將要使用的圖像:
import cv2
import numpy as np
from matplotlib import pyplot as plt
# Declaring the output graph's size
plt.figure(figsize=(16, 16))
# Convert image to grayscale
img_gs = cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)
cv2.imwrite('gs.jpg', img_gs)
# Apply canny edge detector algorithm on the image to find edges
edges = cv2.Canny(img_gs, 100,200)
# Plot the original image against the edges
plt.subplot(121), plt.imshow(img_gs)
plt.title('Original Gray Scale Image')
plt.subplot(122), plt.imshow(edges)
plt.title('Edge Image')
# Display the two images
plt.show()邊緣檢測輸出:
正如你所看到的,圖像中包含對象的部分(在本例中是一隻貓)已經通過邊緣檢測用虛線標出或分隔開。現在你一定想知道,什麼是邊緣檢測的Canny算子,它是怎麼工作的?現在就討論一下。
要理解上述內容,需要討論三個關鍵步驟。首先,它對圖像進行降噪,降噪方式與前面討論的方式類似。其次,它使用每個像素的一階導數來找到邊緣。這背後的邏輯是,在邊緣存在的地方,會有一個突然的強度變化,導致一階導數值達到峰值,從而使該像素成為「邊緣像素」。
最後,進行滯後閾值化;上面我們說過,在一個邊緣的一階導數值會有一個峰值,但是我們沒有說:這個峰值需要有多高,才能被歸類為一個邊緣——這叫做閾值!在本文的前面,我們討論了什麼是簡單的閾值。遲滯閾值法是在此基礎上的一種改進,它利用兩個閾值來代替一個閾值。這背後的原因是,如果閾值過高,我們可能會錯過一些真正的邊緣(真負例),如果閾值過低,我們會得到很多被歸類為邊緣的點,而實際上不是邊緣(假正例)。一個閾值設置為高,一個設置為低,將所有高於「高閾值」的點標識為邊緣,然後對所有高於「低閾值」但低於「高閾值」的點進行評估;邊緣上的點確定之後,與邊緣點靠近或相鄰的點也被確定為邊緣,其餘的點被丟棄。
這些是Canny算子用於識別圖像邊緣的基本概念/方法。
譯者註: Canny算子是澳洲計算機科學家約翰·坎尼(John F. Canny)於1986年開發出來的一個多級邊緣檢測算法,其目標是找到一個最優的邊緣.
結論在本文中,我們學習了如何在不同的平臺(如Windows、MacOS和Linux)上安裝OpenCV,以及如何驗證安裝成功。OpenCV是Python中最流行的圖像處理庫。
接著我們討論了什麼是圖像處理,以及它在機器學習的計算機視覺領域中的應用。我們討論了一些常見的噪聲類型,以及如何使用不同的濾波器將噪聲從圖像中去除,以便在應用中使用這些圖像。
此外,我們還了解了圖像處理如何在高端應用(如:對象檢測或分類)中發揮不可或缺的作用。請注意,這篇文章只是冰山一角,數字圖像處理還有更多的內容,不可能在一篇短文中全部涵蓋。請關注微信公眾號「老齊教室」,這裡還會刊發有關圖像處理的文章。
原文連結:https://stackabuse.com/introduction-to-image-processing-in-python-with-opencv/
★搜索技術問答的公眾號:老齊教室
」★在公眾號中回覆:老齊,可查看所有文章、書籍、課程。
」覺得好看,就點這裡👇👇👇