做者:Muhammad Junaid Khalidpython
翻譯:老齊算法
與本文相關的圖書推薦:《數據準備和特徵工程》編程
在本文中,將學習如何使用Python語言進行圖像處理,咱們不會侷限於一個單獨的庫或框架,然而,有一個庫的使用率將會是最高的,那就是OpenCV。咱們一開始會討論一些圖像處理,而後繼續探討不一樣的應用/場景,也就是圖像處理的用武之地。開始吧!bash
在深刻研究圖像處理的方法以前,重要的是要了解什麼是圖像處理,特別是這項技術在處理大量圖片方面的角色。圖像處理完整的說法是「數字圖像處理」,常用圖像處理的領域是「計算機視覺」。對這兩個術語不要混淆,圖像處理算法和計算機視覺(CV)算法都以圖像爲輸入,然而,在圖像處理中,輸出也是圖像,而在計算機視覺中,輸出能夠是關於圖像的一些特徵或信息。微信
咱們收集或生成的數據大部分是原始數據,也就是說,因爲一些可能的緣由,這些數據不適合直接用於應用程序。所以,咱們須要首先分析它,執行必要的預處理,而後使用它——特別推薦《數據準備和特徵工程》,此書即爲這方面最佳讀物。markdown
例如,咱們正在嘗試構建一個關於貓的分類器。咱們的程序會把一個圖像做爲輸入,而後告訴咱們這個圖像是否包含一隻貓。構建這個分類器的第一步是收集數百張含有貓的圖片。一個常見的問題是,收集的全部圖片的大小都不相同,所以在將它們提供給模型進行訓練以前,須要調整它們的大小或者把它們進行預處理,使尺寸符合標準。框架
爲何圖像處理對於任何計算機視覺應用序都是必不可少的?以上提到的只是衆多緣由之一。dom
爲了輕鬆地學習本文內容,你須要已經具有以下知識。機器學習
首先,應該具有必定的編程語言技能,本文使用的是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)。原來, 咱們設置的閾值正好在圖像的中間,這就是爲何黑白值在那裏被分割。
如今你已經對圖像處理的概念和用途有了基本的瞭解,接下來讓咱們來了解一下它的一些具體應用。
在大多數狀況下,咱們收集的原始數據有噪聲,也就是說,不須要的特徵使圖像很難被感知。雖然這些圖像能夠直接用於特徵抽取,可是算法的準確性會受到很大的影響。這就是爲何在將圖像傳遞給算法以得到更好的精度以前,要對圖像進行處理的緣由。
有許多不一樣類型的噪聲,如高斯噪聲,椒鹽噪聲等。咱們能夠經過應用濾波器來去除圖像中的噪聲,或者至少將其影響降到最低。在濾波器方面也有不少選擇,每個濾波器都有不一樣的優勢。所以,對於特定類型的噪聲來講,總有一個是最好的。
爲了更好地理解這一點,咱們將在上面的玫瑰色圖像的灰度版本中添加「鹽和胡椒粉」噪聲,而後嘗試使用不一樣的濾波器去除圖像中的噪聲,看看哪個最適合這種類型。
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)) 複製代碼
在有椒鹽噪聲的圖像上應用逆諧波均值濾波器(en.wikipedia.org/wiki/Contra…)獲得的圖像以下圖所示。經過與原始灰度圖像的對比,咱們能夠看到:它幾乎完美再現了原始圖像。它的強度或亮度級別與原圖是相同的,它突出了玫瑰上的亮點。所以,咱們能夠得出結論,逆諧波均值濾波器在處理椒鹽噪聲方面是很是有效的。
逆諧波均值濾波器輸出:
如今咱們已經找到了最佳濾波器,它能夠有效地把有噪聲的圖像恢復到原始圖像。咱們能夠繼續下一個應用了。
到目前爲止,咱們使用的玫瑰圖像的背景是不變的,也就是黑色的,所以,咱們將把這個應用用於不一樣的圖像,以更好地展現算法的功效。緣由是,若是背景是恆定的,邊緣檢測任務就變得至關簡單,這不是咱們所但願的。
在本文開始部分,咱們提到了一個關於貓的分類器。如今咱們延用這個例子,看看圖像處理如何在其中扮演一個完整的角色。
在分類算法中,首先掃描圖像尋找「對象」。也就是說,當你輸入一幅圖像時,算法會找到圖像中的全部對象,而後將它們與你試圖尋找的對象進行特徵比較。對於貓分類器,它會將在圖像中找到的全部對象與貓圖像的特徵進行比較,若是找到匹配項,它會告訴咱們輸入圖像中包含了一隻貓。
對於這個貓分類器,僅以一張貓的圖像爲例,如下是咱們將要使用的圖像:
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中最流行的圖像處理庫。
接着咱們討論了什麼是圖像處理,以及它在機器學習的計算機視覺領域中的應用。咱們討論了一些常見的噪聲類型,以及如何使用不一樣的濾波器將噪聲從圖像中去除,以便在應用中使用這些圖像。
此外,咱們還了解了圖像處理如何在高端應用(如:對象檢測或分類)中發揮不可或缺的做用。請注意,這篇文章只是冰山一角,數字圖像處理還有更多的內容,不可能在一篇短文中所有涵蓋。請關注微信公衆號「老齊教室」,這裏還會刊發有關圖像處理的文章。
原文連接:stackabuse.com/introductio…
搜索技術問答的公衆號:老齊教室
在公衆號中回覆:老齊,可查看全部文章、書籍、課程。