關(guān)于圖像處理和Python深度學(xué)習(xí)的教程:第一部分
介紹
在這篇文章中,我們將學(xué)習(xí)如何執(zhí)行圖像處理。在整篇文章中,我們使用到的庫是Scikit Image。
基礎(chǔ)知識
1、什么是圖像?
圖像數(shù)據(jù)可能是文本之后最常見的數(shù)據(jù)。那么,電腦如何理解你在埃菲爾鐵塔前的自拍呢?
它使用一個稱為像素的小正方形網(wǎng)格。像素覆蓋一個小區(qū)域,并具有表示顏色的值。圖像中的像素越多,其質(zhì)量越高,存儲所需的內(nèi)存越多。
就是這樣。圖像處理主要是處理這些單獨的像素(有時是像素組),以便計算機(jī)視覺算法可以從中提取更多信息。
2、NumPy和Skimage的圖像基礎(chǔ)
在Matplotlib和Skimage中,圖像都作為NumPy ndarray加載。
from skimage.io import imread # pip install scikit-image
image = imread("images/colorful_scenery.jpg")
>>> type(image)
numpy.ndarray
NumPy數(shù)組帶來靈活性、速度和力量。圖像處理也不例外。
Ndarrays可以輕松檢索圖像的一般詳細(xì)信息,例如圖像的尺寸:
>>> image.shape
(853, 1280, 3)
>>> image.ndim
3
# The number of pixels
>>> image.size # 853 * 1280 * 3
3275520
我們的圖像高度為853像素,寬度為1280像素。第三維表示RGB(紅、綠、藍(lán))顏色通道的值。最常見的圖像格式是3D。
你可以通過常規(guī)NumPy索引檢索單個像素值。下面,我們嘗試索引圖像以檢索三個顏色通道中的每一個通道:
red = image[:, :, 0]
compare(image, red, "Red Channel of the Image", cmap_type="Reds_r")
green = image[:, :, 1]
compare(image, green, "Green Channel of the Image", "Greens_r")
blue = image[:, :, 2]
compare(image, blue, "Blue Channel of the Image", "Blues_r")
0表示紅色,1表示綠色,2表示藍(lán)色通道-非常簡單。
創(chuàng)建了兩個函數(shù),show和compare,它們顯示一個圖像或并排顯示其中兩個進(jìn)行比較。在整個教程中,我們將廣泛使用這兩個函數(shù)。
按照約定,ndarray的第三維用于顏色通道,但并不總是遵循此約定。Skimage通常提供參數(shù)來指定這種行為。
圖像與通常的Matplotlib繪圖不同。它們的原點不位于左下角,而是位于左上角的位置(0,0)。
>>> show(image, axis=True)
當(dāng)我們在Matplotlib中繪制圖像時,軸表示像素的順序,但我們通常會隱藏它們。
3、常見轉(zhuǎn)換
我們將要執(zhí)行的最常見的圖像轉(zhuǎn)換是將彩色圖像轉(zhuǎn)換為灰度。許多圖像處理算法需要灰度圖像。因為顏色不是圖片的定義特征,沒有它,計算機(jī)仍然可以提取足夠的信息。
from skimage.color import rgb2gray
image = imread("images/grayscale_example.jpg")
# Convert image to grayscale
gray = rgb2gray(image)
compare(image, gray, "Grayscale Image")
>>> gray.shape
(853, 1280)
當(dāng)將圖像轉(zhuǎn)換為灰度時,它們會丟失其第三維度-顏色通道。相反,圖像數(shù)組中的每個單元格現(xiàn)在表示uint8類型的整數(shù)。它們的范圍從0到255,提供256種灰度。
你還可以使用np.flipud或者np.fliplr之類的NumPy函數(shù),隨心所欲地以任何方式操縱圖像。
kitten = imread("images/horizontal_flip.jpg")
horizontal_flipped = np.fliplr(kitten)
compare(kitten, horizontal_flipped, "Horizontally Flipped Image")
ball = imread("images/upside_down.jpg")
vertically_flipped = np.flipud(ball)
compare(ball, vertically_flipped, "Vertically Flipped Image")
在“顏色”模塊中,你可以找到許多其他變換函數(shù)來處理圖像中的顏色。
4、顏色通道直方圖
有時,查看每個顏色通道的強(qiáng)度有助于了解顏色分布。我們可以通過切片每個顏色通道并繪制它們的直方圖來實現(xiàn)這一點。以下是執(zhí)行此操作的函數(shù):
def plot_with_h(yuǎn)ist_channel(image, channel):
channels = ["red", "green", "blue"]
channel_idx = channels.index(channel)
color = channels[channel_idx]
extracted_channel = image[:, :, channel_idx]
fig, (ax1, ax2) = plt.subplots(
ncols=2, figsize=(18, 6)
)
ax1.imshow(image)
ax1.a(chǎn)xis("off")
ax2.hist(extracted_channel.ravel(), bins=256, color=color)
ax2.set_title(f"{channels[channel_idx]} histogram")
除了Matplotlib的一些細(xì)節(jié)之外,你還應(yīng)該注意hist函數(shù)的調(diào)用。提取顏色通道及其數(shù)組后,我們將其展平為1D數(shù)組,并將其傳遞給hist函數(shù)。
bin數(shù)量應(yīng)該是256個,每個像素值對應(yīng)一個-0表示黑色,255表示完全白色。
讓我們使用彩色風(fēng)景圖像:
colorful_scenery = imread("images/colorful_scenery.jpg")
plot_with_h(yuǎn)ist_channel(colorful_scenery, "red")
>>> plot_with_h(yuǎn)ist_channel(colorful_scenery, "green")
>>> plot_with_h(yuǎn)ist_channel(colorful_scenery, "blue")
還可以使用直方圖在將圖像轉(zhuǎn)換為灰度后找出圖像中的亮度:
gray_color_scenery = rgb2gray(colorful_scenery)
plt.hist(gray_color_scenery.ravel(), bins=256);
大多數(shù)像素的值較低,因為景物圖像較暗。
我們將在以下部分探討直方圖的更多應(yīng)用。
過濾器
1、手動閾值
現(xiàn)在,我們來看看有趣的東西——過濾圖像。我們將學(xué)習(xí)的第一個操作是閾值化。讓我們加載一個示例圖像:
stag = imread("images/binary_example.jpg")
>>> show(stag)
閾值分割在圖像分割、目標(biāo)檢測、邊緣或輪廓提取等方面有著廣泛的應(yīng)用,它主要用于區(qū)分圖像的背景和前景。
閾值處理在高對比度灰度圖像上效果最好:
# Convert to graysacle
stag_gray = rgb2gray(stag)
>>> show(stag_gray)
我們將從基本的手動閾值設(shè)置開始,然后轉(zhuǎn)到自動閾值設(shè)置。
首先,我們查看灰度圖像中所有像素的平均值:
>>> stag_gray.mean()
0.20056262759859955
請注意,通過將所有灰度圖像的值除以256,上述灰度圖像的像素在0和1之間歸一化。
我們得到的平均值為0.2,這為我們提供了可能要使用的閾值的初步想法。
現(xiàn)在,我們使用這個閾值來進(jìn)行掩碼操作。如果像素值低于閾值,否則其值將變?yōu)?-黑色或1-白色。換句話說,我們得到一個黑白二值圖像:
# Set threshold
threshold = 0.35
# Binarize
binary_image = stag_gray > threshold
compare(stag, binary_image, "Binary image")
在這個版本中,我們可以更清楚地區(qū)分鹿的輪廓。我們可以反轉(zhuǎn)遮罩,使背景變?yōu)榘咨?/p>
inverted_binary = stag_gray <= threshold
>>> compare(stag, inverted_binary, "Binary image inverted")
2、閾值-全局
雖然嘗試不同的閾值并觀察它們對圖像的影響可能很有趣,但我們通常使用比我們的眼球估計更穩(wěn)健的算法來執(zhí)行閾值分割。
有很多閾值算法,所以可能很難選擇一種。在這種情況下,skimage具有try_all_threshold函數(shù),該函數(shù)在給定的灰度圖像上運行七種閾值算法。讓我們加載一個示例并進(jìn)行轉(zhuǎn)換:
flower = imread("images/global_threshold_ex.jpg")
flower_gray = rgb2gray(flower)
compare(flower, flower_gray)
我們將看看是否可以使用閾值優(yōu)化郁金香的特征:
from skimage.filters import try_all_threshold
fig, ax = try_all_threshold(
flower_gray, figsize=(10, 8), verbose=False
)
正如你所看到的,一些算法在這張圖像上工作得更好,而其他算法則很糟糕。otsu算法看起來更好,所以我們將繼續(xù)使用它。
在這一點上,我想提請你注意郁金香的原始圖像:
>>> show(flower)
圖像背景不均勻,因為有太多光線從后面的窗口射進(jìn)來。我們可以通過繪制灰色郁金香的直方圖來證實這一點:
>>> plt.hist(flower_gray.ravel(), bins=256);
正如預(yù)期的那樣,大多數(shù)像素的值都位于直方圖的遠(yuǎn)端,這證實了它們大部分都是明亮的。
為什么這很重要?根據(jù)圖像的亮度,閾值算法的性能也會發(fā)生變化。因此,閾值算法通常有兩種類型:
全局-適用于具有均勻、統(tǒng)一背景的照片
局部-用于不同圖片區(qū)域中具有不同亮度級別的圖像。
郁金香圖像屬于第二類,因為右側(cè)部分比另一半亮得多,使其背景不均勻。我們不能在其上使用全局閾值算法,這就是為什么try_all_threshold中所有算法的性能都很差的原因。
稍后我們將回到郁金香示例和局部閾值。現(xiàn)在,我們將加載另一個亮度更精確的實例,并嘗試自動設(shè)置閾值:
spiral = imread("images/otsu_example.jpg")
spiral_gray = rgb2gray(spiral)
compare(spiral, spiral_gray)
我們將在Skimage中使用通用的全局閾值算法threshold_otsu:
from skimage.filters import threshold_otsu
# Find optimal threshold with `threshold_otsu`
threshold = threshold_otsu(spiral_gray)
# Binarize
binary_spiral = spiral_gray > threshold
compare(spiral, binary_spiral, "Binarized Image w. Otsu Thresholding")
它工作得更好!
3、閾值-局部
現(xiàn)在,我們將使用局部閾值算法。
局部算法不關(guān)注整個圖像,而是關(guān)注像素鄰域,以解釋不同區(qū)域的亮度不均勻。skimage中常見的局部算法為threshold_local函數(shù):
from skimage.filters import threshold_local
local_thresh = threshold_local(flower_gray, block_size=3, offset=0.0002)
binary_flower = flower_gray > local_thresh
compare(flower, binary_flower, "Tresholded flower image")
你必須使用offset參數(shù)來找到符合你需要的最佳圖像。offset是從局部像素鄰域的平均值中減去的常數(shù)。該“像素鄰域”由local_threshold中的block_size參數(shù)確定,該參數(shù)表示算法在每個方向上圍繞每個點查看的像素數(shù)。
顯然,同時調(diào)整offset和block_size是一個缺點,但局部閾值是唯一比手動或全局閾值產(chǎn)生更好結(jié)果的選項。
讓我們再舉一個例子:
from skimage.filters import threshold_local
handwriting = imread("images/chalk_writing.jpg")
handwriting_gray = rgb2gray(handwriting)
# Find optimal threshold using local
local_thresh = threshold_local(handwriting_gray, offset=0.0003)
# Binarize
binary_h(yuǎn)andwriting = handwriting_gray > local_thresh
compare(handwriting, binary_h(yuǎn)andwriting,
"Binarized image with local thresholding")
正如你所看到的,經(jīng)過閾值處理后,黑板上的筆跡更加精細(xì)。
4、邊緣檢測
邊緣檢測在很多方面都很有用,例如識別對象、從中提取特征、對其進(jìn)行計數(shù)等等。
我們將從基本的Sobel濾波器開始,它在灰度圖像中查找對象的邊緣。我們將加載一張硬幣圖片,并對其使用Sobel濾波器:
from skimage.filters import sobel
coins = imread("images/coins_2.jpg")
coins_gray = rgb2gray(coins)
coins_edge = sobel(coins_gray)
compare(coins, coins_edge, "Images of coins with edges detected")
sobel很直截了當(dāng);你只需在灰色圖像上調(diào)用它即可獲得如上所述的輸出。我們將在后面的部分中看到Sobel的更復(fù)雜版本。
5、平滑
另一種圖像過濾技術(shù)是平滑。許多像下面的雞一樣的圖像可能包含隨機(jī)噪聲,而對ML和DL算法沒有任何有價值的信息。
例如,雞周圍的毛發(fā)會給圖像添加噪聲,這可能會使ML模型的注意力偏離主要對象本身。在這種情況下,我們使用平滑來模糊噪聲或邊緣并降低對比度。
chickens = imread("images/chickens.jpg")
>>> show(chickens)
高斯平滑是最流行和最強(qiáng)大的平滑技術(shù)之一:
from skimage.filters import gaussian
smoothed = gaussian(chickens, multichannel=True, sigma=2)
compare(chickens, smoothed, "An image smoothed with Gaussian smoothing")
你可以通過調(diào)整sigma參數(shù)來控制模糊的效果。如果你正在處理RGB圖像,請不要忘記將multichannel設(shè)置為True。
如果圖像分辨率太高,平滑效果可能肉眼看不到,但仍然有效。
原文標(biāo)題 : 關(guān)于圖像處理和Python深度學(xué)習(xí)的教程:第一部分
請輸入評論內(nèi)容...
請輸入評論/評論長度6~500個字
最新活動更多
-
即日-12.26立即報名>>> 【在線會議】村田用于AR/VR設(shè)計開發(fā)解決方案
-
1月8日火熱報名中>> Allegro助力汽車電氣化和底盤解決方案優(yōu)化在線研討會
-
即日-1.14火熱報名中>> OFweek2025中國智造CIO在線峰會
-
即日-1.24立即參與>>> 【限時免費】安森美:Treo 平臺帶來出色的精密模擬
-
即日-2025.8.1立即下載>> 《2024智能制造產(chǎn)業(yè)高端化、智能化、綠色化發(fā)展藍(lán)皮書》
-
精彩回顧立即查看>> 【線下會議】OFweek 2024(第九屆)物聯(lián)網(wǎng)產(chǎn)業(yè)大會
推薦專題
- 高級軟件工程師 廣東省/深圳市
- 自動化高級工程師 廣東省/深圳市
- 光器件研發(fā)工程師 福建省/福州市
- 銷售總監(jiān)(光器件) 北京市/海淀區(qū)
- 激光器高級銷售經(jīng)理 上海市/虹口區(qū)
- 光器件物理工程師 北京市/海淀區(qū)
- 激光研發(fā)工程師 北京市/昌平區(qū)
- 技術(shù)專家 廣東省/江門市
- 封裝工程師 北京市/海淀區(qū)
- 結(jié)構(gòu)工程師 廣東省/深圳市
OFweek人工智能網(wǎng)
獲取更多精彩內(nèi)容