圖像教程
譯者:飛龍
協(xié)議:CC BY-NC-SA 4.0
啟動(dòng)命令
首先,讓我們啟動(dòng) IPython。 它是 Python 標(biāo)準(zhǔn)提示符的最好的改進(jìn),它與 Matplotlib 配合得相當(dāng)不錯(cuò)。 在 shell 或 IPython Notebook 上都可以啟動(dòng) IPython。
隨著 IPython 啟動(dòng),我們現(xiàn)在需要連接到 GUI 事件循環(huán)。 它告訴 IPython 在哪里(以及如何顯示)繪圖。 要連接到 GUI 循環(huán),請(qǐng)?jiān)?IPython 提示符處執(zhí)行%matplotlib魔法。 在 IPython 的 GUI 事件循環(huán)文檔中有更多的細(xì)節(jié)。
如果使用 IPython Notebook,可以使用相同的命令,但人們通常以特定參數(shù)使用%matplotlib:
In [1]: %matplotlib inline
這將打開內(nèi)聯(lián)繪圖,繪圖圖形將顯示在筆記本中。 這對(duì)交互性有很重要的影響。 對(duì)于內(nèi)聯(lián)繪圖,在單元格下方的單元格中輸出繪圖的命令不會(huì)影響繪圖。 例如,從創(chuàng)建繪圖的單元格下面的單元格更改顏色表是不可能的。 但是,對(duì)于其他后端,例如 qt4,它們會(huì)打開一個(gè)單獨(dú)的窗口,那些創(chuàng)建繪圖的單元格下方的單元格將改變繪圖 - 它是一個(gè)內(nèi)存中的活對(duì)象。
本教程將使用matplotlib的命令式繪圖接口pyplot。 該接口維護(hù)全局狀態(tài),并且可用于簡單快速地嘗試各種繪圖設(shè)置。 另一種是面向?qū)ο蟮慕涌冢@也非常強(qiáng)大,一般更適合大型應(yīng)用程序的開發(fā)。 如果你想了解面向?qū)ο蠼涌冢?a target="_blank" rel="nofollow">使用上的常見問題是一個(gè)用于起步的不錯(cuò)的頁面。 現(xiàn)在,讓我們繼續(xù)使用命令式方式:
In [2]: import matplotlib.pyplot as plt
In [3]: import matplotlib.image as mpimg
In [4]: import numpy as np
將圖像數(shù)據(jù)導(dǎo)入到 NumPy 數(shù)組
加載圖像數(shù)據(jù)由 Pillow 庫提供支持。 本來,matplotlib只支持 PNG 圖像。 如果本機(jī)讀取失敗,下面顯示的命令會(huì)回退到 Pillow。
此示例中使用的圖像是 PNG 文件,但是請(qǐng)記住你自己的數(shù)據(jù)的 Pillow 要求。
下面是我們要擺弄的圖片:

它是一個(gè) 24 位 RGB PNG 圖像(每個(gè) R,G,B 為 8 位)。 根據(jù)你獲取數(shù)據(jù)的位置,你最有可能遇到的其他類型的圖像是 RGBA 圖像,擁有透明度或單通道灰度(亮度)的圖像。 你可以右鍵單擊它,選擇Save image as(另存為)為本教程的剩余部分下載到你的計(jì)算機(jī)。
現(xiàn)在我們開始...
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 已將每個(gè)通道的8位數(shù)據(jù)重新定標(biāo)為 0.0 和 1.0 之間的浮點(diǎn)數(shù)。 作為旁注,Pillow 可以使用的唯一數(shù)據(jù)類型是uint8。 Matplotlib 繪圖可以處理float32和uint8,但是對(duì)于除 PNG 之外的任何格式的圖像,讀取/寫入僅限于uint8數(shù)據(jù)。 為什么是 8 位呢? 大多數(shù)顯示器只能渲染每通道 8 位的顏色漸變。 為什么他們只能渲染每通道 8 位呢? 因?yàn)檫@會(huì)使所有人的眼睛可以看到。 更多信息請(qǐng)見(從攝影的角度):Luminous Landscape 位深度教程。
每個(gè)內(nèi)部列表表示一個(gè)像素。 這里,對(duì)于 RGB 圖像,有 3 個(gè)值。 由于它是一個(gè)黑白圖像,R,G 和 B 都是類似的。 RGBA(其中 A 是阿爾法或透明度)對(duì)于每個(gè)內(nèi)部列表具有 4 個(gè)值,而且簡單亮度圖像僅具有一個(gè)值(因此僅是二維數(shù)組,而不是三維數(shù)組)。 對(duì)于 RGB 和 RGBA 圖像,matplotlib支持float32和uint8數(shù)據(jù)類型。 對(duì)于灰度,matplotlib只支持float32。 如果你的數(shù)組數(shù)據(jù)不符合這些描述之一,則需要重新縮放它。
將 NumPy 數(shù)組繪制為圖像
所以,你將數(shù)據(jù)保存在一個(gè)numpy數(shù)組(通過導(dǎo)入它,或生成它)。 讓我們渲染它吧。 在 Matplotlib 中,這是使用imshow()函數(shù)執(zhí)行的。 這里我們將抓取plot對(duì)象。 這個(gè)對(duì)象提供了一個(gè)簡單的方法來從提示符處理繪圖。
In [6]: imgplot = plt.imshow(img)

你也可以繪制任何 NumPy 數(shù)組。
對(duì)圖像繪圖應(yīng)用偽彩色方案
偽彩色可以是一個(gè)有用的工具,用于增強(qiáng)對(duì)比度和更易于可視化你的數(shù)據(jù)。 這在使用投影儀對(duì)你的數(shù)據(jù)進(jìn)行演示時(shí)尤其有用 - 它們的對(duì)比度通常很差。
偽彩色僅與單通道,灰度,亮度圖像相關(guān)。 我們目前有一個(gè)RGB圖像。 由于R,G 和 B 都是相似的(見上面或你的數(shù)據(jù)),我們可以只選擇一個(gè)通道的數(shù)據(jù):
In [7]: lum_img = img[:,:,0]
這是數(shù)組切片,更多信息請(qǐng)見NumPy 教程。
In [8]: plt.imshow(lum_img)

現(xiàn)在,亮度(2D,無顏色)圖像應(yīng)用了默認(rèn)顏色表(也稱為查找表,LUT)。 默認(rèn)值稱為jet。 有很多其他方案可以選擇。
In [9]: plt.imshow(lum_img, cmap="hot")

請(qǐng)注意,你還可以使用set_cmap()方法更改現(xiàn)有繪圖對(duì)象上的顏色:
In [10]: imgplot = plt.imshow(lum_img)
In [11]: imgplot.set_cmap('spectral')

注
但是,請(qǐng)記住,在帶有內(nèi)聯(lián)后端的 IPython notebook 中,你不能對(duì)已經(jīng)渲染的繪圖進(jìn)行更改。 如果你在一個(gè)單元格中創(chuàng)建了
imgplot,你不能在以后的單元格中調(diào)用set_cmap(),并且改變前面的繪圖。 請(qǐng)確保你在相同單元格中一起輸入這些命令。plt命令不會(huì)更改先前單元格的繪圖。
有許多可選的其它顏色表,請(qǐng)見顏色表的列表和圖像。
顏色刻度參考
了解顏色代表什么值對(duì)我們很有幫助。 我們可以通過添加顏色條來做到這一點(diǎn)。
In [12]: imgplot = plt.imshow(lum_img)
In [13]: plt.colorbar()

這會(huì)為你現(xiàn)有的圖形添加一個(gè)顏色條。 如果你更改并切換到不同的顏色映射,則不會(huì)自動(dòng)更改 - 你必須重新創(chuàng)建繪圖,并再次添加顏色條。
檢查特定數(shù)據(jù)范圍
有時(shí),你想要增強(qiáng)圖像的對(duì)比度,或者擴(kuò)大特定區(qū)域的對(duì)比度,同時(shí)犧牲變化不大,或者無所謂的顏色細(xì)節(jié)。 找到有趣區(qū)域的最好工具是直方圖。 要?jiǎng)?chuàng)建我們的圖像數(shù)據(jù)的直方圖,我們使用hist()函數(shù)。
In [14]: plt.hist(lum_img.ravel(), bins=256, range=(0.0, 1.0), fc='k', ec='k')

通常,圖像的『有趣』部分在峰值附近,你可以通過剪切峰值上方和/或下方的區(qū)域獲得額外的對(duì)比度。 在我們的直方圖中,看起來最大值處沒有太多有用的信息(圖像中有很多不是白色的東西)。 讓我們調(diào)整上限,以便我們有效地『放大』直方圖的一部分。 我們通過將clim參數(shù)傳遞給imshow來實(shí)現(xiàn)。 你也可以通過對(duì)圖像繪圖對(duì)象調(diào)用set_clim()方法來做到這一點(diǎn),但要確保你在使用 IPython Notebook 的時(shí)候,和plot命令在相同的單元格中執(zhí)行 - 它不會(huì)改變之前單元格的圖。
In [15]: imgplot = plt.imshow(lum_img, clim=(0.0, 0.7))

數(shù)組插值方案
插值根據(jù)不同的數(shù)學(xué)方案計(jì)算像素『應(yīng)有』的顏色或值。 發(fā)生這種情況的一個(gè)常見的場景是調(diào)整圖像的大小。 像素的數(shù)量會(huì)發(fā)生變化,但你想要相同的信息。 由于像素是離散的,因此存在缺失的空間。 插值就是填補(bǔ)這個(gè)空間的方式。 這就是當(dāng)你放大圖像時(shí),你的圖像有時(shí)會(huì)出來看起來像素化的原因。 當(dāng)原始圖像和擴(kuò)展圖像之間的差異較大時(shí),效果更加明顯。 讓我們加載我們的圖像并縮小它。 我們實(shí)際上正在丟棄像素,只保留少數(shù)幾個(gè)像素。 現(xiàn)在,當(dāng)我們繪制它時(shí),數(shù)據(jù)被放大為你屏幕的大小。 由于舊的像素不再存在,計(jì)算機(jī)必須繪制像素來填充那個(gè)空間。
我們將使用用來加載圖像的 Pillow 庫來調(diào)整圖像大小。
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)

這里我們使用默認(rèn)插值,雙線性,因?yàn)槲覀儧]有向imshow()提供任何插值參數(shù)。
讓我們?cè)囋囈恍┢渌臇|西:
最鄰近
In [20]: imgplot = plt.imshow(img, interpolation="nearest")

雙立方
In [21]: imgplot = plt.imshow(img, interpolation="bicubic")

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