5.21. 縮放、翻轉與裁切¶
前面各小節處理的像素都維持在它們一開始所在的位置。transform 系列則改變了這一點。縮放會將每個輸入像素送到不同的輸出位置,可能一次送往數個輸出位置(放大時),或送往一個與其他數個輸入像素共用的位置(縮小時)。翻轉與旋轉透過不同的映射做相同的事情。裁切則保留輸入像素的某個矩形子集,並捨棄其餘部分。
image 模組透過三個方法來呈現這個系列,它們共用大部分的引數與大部分的行為:
copy()——產生影像的一份副本,可能經過縮放、裁切或重新定向。crop()——與copy相同的操作,但預期應用程式會從來源中挑選出一個子矩形。scale()——同樣的操作,但預期應用程式會調整結果的大小。
這三者共用相同的引數與相同的轉換機制;差別在於結果預設落在哪裡。copy() 會產生一張新影像,而 crop() 與 scale() 則就地修改來源。
5.21.2. 插值:AREA、BILINEAR、BICUBIC¶
當縮放將每個輸出像素送到一個無法與任何單一輸入像素對齊的位置時,該方法必須決定要寫入什麼值。有三個旗標控制其方式:
image.BILINEAR 在最鄰近的四個輸入像素之間,依其與輸出位置的距離加權進行插值。結果比最鄰近法更平滑,在對角線上不會出現可見的鋸齒,但額外的運算成本約為最鄰近法的四倍。對於大多數放大工作以及任何非整數縮放倍率而言,這是正確的選擇。
image.BICUBIC 在最鄰近的十六個輸入像素之間使用三次曲線進行插值,能產生更為平滑的結果,但代價是又增加了更多運算。對於有此需求且對成本敏感的應用而言,這是最佳品質;對於 IDE 僅會顯示的即時影格而言,這額外的運算量很少值得。
image.AREA 對落在輸出像素覆蓋範圍內的每個輸入像素取平均——這是縮小的正確演算法。雙線性與雙三次都是插值器:它們估算來源像素之間的值,這正是放大所需要的,但在縮小時每個輸出像素涵蓋許多來源像素,而插值器只讀取其中最鄰近的少數幾個——它略過的細節會以混疊的形式出現。image.AREA 則改為將每個被涵蓋的像素都納入平均之中。
不帶任何 hint 時的預設縮放演算法是最鄰近法,當來源已處於目的地的像素解析度時,它是最便宜也最正確的答案。
5.21.3. 定向:翻轉與旋轉¶
定向旗標是一小組布林轉換,它們彼此之間以及與插值旗標之間都能自由組合:
image.VFLIP將影像垂直翻轉(上方變成下方)。image.HMIRROR將它水平鏡像(左方變成右方)。image.TRANSPOSE交換 x 軸與 y 軸(列變成欄)。
大多數旋轉都來自組合這三者。模組也提供了具名的快捷方式:
image.ROTATE_90(=VFLIP | TRANSPOSE)image.ROTATE_180(=HMIRROR | VFLIP)image.ROTATE_270(=HMIRROR | TRANSPOSE)
在程式碼中:
img.copy(hint=image.ROTATE_90, copy_to_fb=True)
5.21.4. 長寬比處理¶
當來源的長寬比與其要被繪入的矩形不相符時,有三個旗標決定如何處理這個不相符:
image.SCALE_ASPECT_KEEP 保留來源的長寬比並對結果進行信箱式留邊——將來源縮放到剛好可放進目的地內,並以空白(零值)像素填滿目的地的其餘部分。當保持來源不變形比填滿整個輸出更重要時,這是正確的選擇。
image.SCALE_ASPECT_EXPAND 保留來源的長寬比並對它進行裁切——將來源縮放到填滿目的地,超出目的地的部分則被切除。當填滿整個輸出比看見來源的每一部分更重要時,這是正確的選擇。
image.SCALE_ASPECT_IGNORE 忽略長寬比並將來源拉伸以填滿目的地,接受由此引入的任何變形。當應用程式已將該變形納入考量時——例如,當目的地的尺寸實際上並非同一場景的某個矩形時——這是正確的選擇。
預設(未設定任何長寬比旗標)與 SCALE_ASPECT_IGNORE 相同:拉伸以填滿。在意長寬比的應用程式會明確指定這三者之一。
5.21.5. 何時選用哪一個¶
大多數縮放使用 scale(),並搭配一組 x_scale / y_scale 以及一個插值 hint:
img.scale(x_scale=0.5, y_scale=0.5, hint=image.AREA)
大多數旋轉使用相同的呼叫,搭配 hint=image.ROTATE_90 或類似旗標。
裁切使用 crop(),並搭配一個非預設的 roi:
img.crop(roi=(40, 30, 200, 150))
當來源必須在操作後留存下來時——例如擷取一個參考影格、為一個即將被破壞性處理的影格製作縮圖——copy() 會將結果產生為一張新影像,並保持來源不受影響:
thumbnail = img.copy(x_scale=0.25, y_scale=0.25, hint=image.AREA)
那個預設值正是這三個名稱背後真正的差異:scale 與 crop 就地轉換,copy 則進行配置。結果放置關鍵字則彌合了這個落差:在 scale 或 crop 上使用 copy=True 會將結果配置為一個獨立的堆積緩衝區而非覆寫來源,而在這三者中任一者上使用 copy_to_fb=True 則會將它放入影格緩衝區供 IDE 預覽。