天天看點

Matplotlib 中文使用者指南 3.2 圖像教程圖像教程

圖像教程

原文: 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 要求。

下面是我們要擺弄的圖檔:

Matplotlib 中文使用者指南 3.2 圖像教程圖像教程

它是一個 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)           
Matplotlib 中文使用者指南 3.2 圖像教程圖像教程

你也可以繪制任何 NumPy 數組。

對圖像繪圖應用僞彩色方案

僞彩色可以是一個有用的工具,用于增強對比度和更易于可視化你的資料。 這在使用投影儀對你的資料進行示範時尤其有用 - 它們的對比度通常很差。

僞彩色僅與單通道,灰階,亮度圖像相關。 我們目前有一個RGB圖像。 由于R,G 和 B 都是相似的(見上面或你的資料),我們可以隻選擇一個通道的資料:

In [7]: lum_img = img[:,:,0]           

這是數組切片,更多資訊請見

NumPy 教程
In [8]: plt.imshow(lum_img)           
Matplotlib 中文使用者指南 3.2 圖像教程圖像教程

現在,亮度(2D,無顔色)圖像應用了預設顔色表(也稱為查找表,LUT)。 預設值稱為

jet

。 有很多其他方案可以選擇。

In [9]: plt.imshow(lum_img, cmap="hot")           
Matplotlib 中文使用者指南 3.2 圖像教程圖像教程

請注意,你還可以使用

set_cmap()

方法更改現有繪圖對象上的顔色:

In [10]: imgplot = plt.imshow(lum_img)
In [11]: imgplot.set_cmap('spectral')           
Matplotlib 中文使用者指南 3.2 圖像教程圖像教程

但是,請記住,在帶有内聯後端的 IPython notebook 中,你不能對已經渲染的繪圖進行更改。 如果你在一個單元格中建立了

imgplot

,你不能在以後的單元格中調用

set_cmap()

,并且改變前面的繪圖。 請確定你在相同單元格中一起輸入這些指令。

plt

指令不會更改先前單元格的繪圖。

有許多可選的其它顔色表,請見

顔色表的清單和圖像

顔色刻度參考

了解顔色代表什麼值對我們很有幫助。 我們可以通過添加顔色條來做到這一點。

In [12]: imgplot = plt.imshow(lum_img)
In [13]: plt.colorbar()           
Matplotlib 中文使用者指南 3.2 圖像教程圖像教程

這會為你現有的圖形添加一個顔色條。 如果你更改并切換到不同的顔色映射,則不會自動更改 - 你必須重新建立繪圖,并再次添加顔色條。

檢查特定資料範圍

有時,你想要增強圖像的對比度,或者擴大特定區域的對比度,同時犧牲變化不大,或者無所謂的顔色細節。 找到有趣區域的最好工具是直方圖。 要建立我們的圖像資料的直方圖,我們使用

hist()

函數。

In [14]: plt.hist(lum_img.ravel(), bins=256, range=(0.0, 1.0), fc='k', ec='k')           
Matplotlib 中文使用者指南 3.2 圖像教程圖像教程

通常,圖像的『有趣』部分在峰值附近,你可以通過剪切峰值上方和/或下方的區域獲得額外的對比度。 在我們的直方圖中,看起來最大值處沒有太多有用的資訊(圖像中有很多不是白色的東西)。 讓我們調整上限,以便我們有效地『放大』直方圖的一部分。 我們通過将

clim

參數傳遞給

imshow

來實作。 你也可以通過對圖像繪圖對象調用

set_clim()

方法來做到這一點,但要確定你在使用 IPython Notebook 的時候,和

plot

指令在相同的單元格中執行 - 它不會改變之前單元格的圖。

In [15]: imgplot = plt.imshow(lum_img, clim=(0.0, 0.7))           
Matplotlib 中文使用者指南 3.2 圖像教程圖像教程

數組插值方案

插值根據不同的數學方案計算像素『應有』的顔色或值。 發生這種情況的一個常見的場景是調整圖像的大小。 像素的數量會發生變化,但你想要相同的資訊。 由于像素是離散的,是以存在缺失的空間。 插值就是填補這個空間的方式。 這就是當你放大圖像時,你的圖像有時會出來看起來像素化的原因。 當原始圖像和擴充圖像之間的差異較大時,效果更加明顯。 讓我們加載我們的圖像并縮小它。 我們實際上正在丢棄像素,隻保留少數幾個像素。 現在,當我們繪制它時,資料被放大為你螢幕的大小。 由于舊的像素不再存在,計算機必須繪制像素來填充那個空間。

我們将使用用來加載圖像的 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)           
Matplotlib 中文使用者指南 3.2 圖像教程圖像教程

這裡我們使用預設插值,雙線性,因為我們沒有向

imshow()

提供任何插值參數。

讓我們試試一些其它的東西:

最鄰近

In [20]: imgplot = plt.imshow(img, interpolation="nearest")           
Matplotlib 中文使用者指南 3.2 圖像教程圖像教程

雙立方

In [21]: imgplot = plt.imshow(img, interpolation="bicubic")           
Matplotlib 中文使用者指南 3.2 圖像教程圖像教程

雙立方插值通常用于放大照片 - 人們傾向于模糊而不是過度像素化。