圖像教程
原文: Image tutorial 譯者: 飛龍 協定: CC BY-NC-SA 4.0
啟動指令
首先,讓我們啟動 IPython。 它是 Python 标準提示符的最好的改進,它與 Matplotlib 配合得相當不錯。 在 shell 或 IPython Notebook 上都可以啟動 IPython。
随着 IPython 啟動,我們現在需要連接配接到 GUI 事件循環。 它告訴 IPython 在哪裡(以及如何顯示)繪圖。 要連接配接到 GUI 循環,請在 IPython 提示符處執行
%matplotlib
魔法。 在
IPython 的 GUI 事件循環文檔中有更多的細節。
如果使用 IPython Notebook,可以使用相同的指令,但人們通常以特定參數使用
%matplotlib
:
In [1]: %matplotlib inline
這将打開内聯繪圖,繪圖圖形将顯示在筆記本中。 這對互動性有很重要的影響。 對于内聯繪圖,在單元格下方的單元格中輸出繪圖的指令不會影響繪圖。 例如,從建立繪圖的單元格下面的單元格更改顔色表是不可能的。 但是,對于其他後端,例如 qt4,它們會打開一個單獨的視窗,那些建立繪圖的單元格下方的單元格将改變繪圖 - 它是一個記憶體中的活對象。
本教程将使用
matplotlib
的指令式繪圖接口
pyplot
。 該接口維護全局狀态,并且可用于簡單快速地嘗試各種繪圖設定。 另一種是面向對象的接口,這也非常強大,一般更适合大型應用程式的開發。 如果你想了解面向對象接口,
使用上的常見問題是一個用于起步的不錯的頁面。 現在,讓我們繼續使用指令式方式:
In [2]: import matplotlib.pyplot as plt
In [3]: import matplotlib.image as mpimg
In [4]: import numpy as np
将圖像資料導入到 NumPy 數組
加載圖像資料由 Pillow 庫提供支援。 本來,
matplotlib
隻支援 PNG 圖像。 如果本機讀取失敗,下面顯示的指令會回退到 Pillow。
此示例中使用的圖像是 PNG 檔案,但是請記住你自己的資料的 Pillow 要求。
下面是我們要擺弄的圖檔:
它是一個 24 位 RGB PNG 圖像(每個 R,G,B 為 8 位)。 根據你擷取資料的位置,你最有可能遇到的其他類型的圖像是 RGBA 圖像,擁有透明度或單通道灰階(亮度)的圖像。 你可以右鍵單擊它,選擇
Save image as
(另存為)為本教程的剩餘部分下載下傳到你的計算機。
現在我們開始…
In [5]: img=mpimg.imread('stinkbug.png')
Out[5]:
array([[[ 0.40784314, 0.40784314, 0.40784314],
[ 0.40784314, 0.40784314, 0.40784314],
[ 0.40784314, 0.40784314, 0.40784314],
...,
[ 0.42745098, 0.42745098, 0.42745098],
[ 0.42745098, 0.42745098, 0.42745098],
[ 0.42745098, 0.42745098, 0.42745098]],
...,
[[ 0.44313726, 0.44313726, 0.44313726],
[ 0.4509804 , 0.4509804 , 0.4509804 ],
[ 0.4509804 , 0.4509804 , 0.4509804 ],
...,
[ 0.44705883, 0.44705883, 0.44705883],
[ 0.44705883, 0.44705883, 0.44705883],
[ 0.44313726, 0.44313726, 0.44313726]]], dtype=float32)
注意這裡的
dtype
-
float32
。 Matplotlib 已将每個通道的8位資料重新定标為 0.0 和 1.0 之間的浮點數。 作為旁注,Pillow 可以使用的唯一資料類型是
uint8
。 Matplotlib 繪圖可以處理
float32
和
uint8
,但是對于除 PNG 之外的任何格式的圖像,讀取/寫入僅限于
uint8
資料。 為什麼是 8 位呢? 大多數顯示器隻能渲染每通道 8 位的顔色漸變。 為什麼他們隻能渲染每通道 8 位呢? 因為這會使所有人的眼睛可以看到。 更多資訊請見(從攝影的角度):
Luminous Landscape 位深度教程。
每個内部清單表示一個像素。 這裡,對于 RGB 圖像,有 3 個值。 由于它是一個黑白圖像,R,G 和 B 都是類似的。 RGBA(其中 A 是阿爾法或透明度)對于每個内部清單具有 4 個值,而且簡單亮度圖像僅具有一個值(是以僅是二維數組,而不是三維數組)。 對于 RGB 和 RGBA 圖像,
matplotlib
支援
float32
uint8
資料類型。 對于灰階,
matplotlib
隻支援
float32
。 如果你的數組資料不符合這些描述之一,則需要重新縮放它。
将 NumPy 數組繪制為圖像
是以,你将資料儲存在一個
numpy
數組(通過導入它,或生成它)。 讓我們渲染它吧。 在 Matplotlib 中,這是使用
imshow()
函數執行的。 這裡我們将抓取
plot
對象。 這個對象提供了一個簡單的方法來從提示符處理繪圖。
In [6]: imgplot = plt.imshow(img)
你也可以繪制任何 NumPy 數組。
對圖像繪圖應用僞彩色方案
僞彩色可以是一個有用的工具,用于增強對比度和更易于可視化你的資料。 這在使用投影儀對你的資料進行示範時尤其有用 - 它們的對比度通常很差。
僞彩色僅與單通道,灰階,亮度圖像相關。 我們目前有一個RGB圖像。 由于R,G 和 B 都是相似的(見上面或你的資料),我們可以隻選擇一個通道的資料:
In [7]: lum_img = img[:,:,0]
這是數組切片,更多資訊請見
NumPy 教程In [8]: plt.imshow(lum_img)
現在,亮度(2D,無顔色)圖像應用了預設顔色表(也稱為查找表,LUT)。 預設值稱為
jet
。 有很多其他方案可以選擇。
In [9]: plt.imshow(lum_img, cmap="hot")
請注意,你還可以使用
set_cmap()
方法更改現有繪圖對象上的顔色:
In [10]: imgplot = plt.imshow(lum_img)
In [11]: imgplot.set_cmap('spectral')
注
但是,請記住,在帶有内聯後端的 IPython notebook 中,你不能對已經渲染的繪圖進行更改。 如果你在一個單元格中建立了
,你不能在以後的單元格中調用
imgplot
,并且改變前面的繪圖。 請確定你在相同單元格中一起輸入這些指令。
set_cmap()
指令不會更改先前單元格的繪圖。
plt
有許多可選的其它顔色表,請見
顔色表的清單和圖像顔色刻度參考
了解顔色代表什麼值對我們很有幫助。 我們可以通過添加顔色條來做到這一點。
In [12]: imgplot = plt.imshow(lum_img)
In [13]: plt.colorbar()
這會為你現有的圖形添加一個顔色條。 如果你更改并切換到不同的顔色映射,則不會自動更改 - 你必須重新建立繪圖,并再次添加顔色條。
檢查特定資料範圍
有時,你想要增強圖像的對比度,或者擴大特定區域的對比度,同時犧牲變化不大,或者無所謂的顔色細節。 找到有趣區域的最好工具是直方圖。 要建立我們的圖像資料的直方圖,我們使用
hist()
函數。
In [14]: plt.hist(lum_img.ravel(), bins=256, range=(0.0, 1.0), fc='k', ec='k')
通常,圖像的『有趣』部分在峰值附近,你可以通過剪切峰值上方和/或下方的區域獲得額外的對比度。 在我們的直方圖中,看起來最大值處沒有太多有用的資訊(圖像中有很多不是白色的東西)。 讓我們調整上限,以便我們有效地『放大』直方圖的一部分。 我們通過将
clim
參數傳遞給
imshow
來實作。 你也可以通過對圖像繪圖對象調用
set_clim()
方法來做到這一點,但要確定你在使用 IPython Notebook 的時候,和
plot
指令在相同的單元格中執行 - 它不會改變之前單元格的圖。
In [15]: imgplot = plt.imshow(lum_img, clim=(0.0, 0.7))
數組插值方案
插值根據不同的數學方案計算像素『應有』的顔色或值。 發生這種情況的一個常見的場景是調整圖像的大小。 像素的數量會發生變化,但你想要相同的資訊。 由于像素是離散的,是以存在缺失的空間。 插值就是填補這個空間的方式。 這就是當你放大圖像時,你的圖像有時會出來看起來像素化的原因。 當原始圖像和擴充圖像之間的差異較大時,效果更加明顯。 讓我們加載我們的圖像并縮小它。 我們實際上正在丢棄像素,隻保留少數幾個像素。 現在,當我們繪制它時,資料被放大為你螢幕的大小。 由于舊的像素不再存在,計算機必須繪制像素來填充那個空間。
我們将使用用來加載圖像的 Pillow 庫來調整圖像大小。
In [16]: from PIL import Image
In [17]: img = Image.open('../_static/stinkbug.png')
In [18]: img.thumbnail((64, 64), Image.ANTIALIAS) # resizes image in-place
In [19]: imgplot = plt.imshow(img)
這裡我們使用預設插值,雙線性,因為我們沒有向
imshow()
提供任何插值參數。
讓我們試試一些其它的東西:
最鄰近
In [20]: imgplot = plt.imshow(img, interpolation="nearest")
雙立方
In [21]: imgplot = plt.imshow(img, interpolation="bicubic")
雙立方插值通常用于放大照片 - 人們傾向于模糊而不是過度像素化。