為什麼我的 CV 模型不好用?沒想到原因竟如此簡單……

AIBigbull2050發表於2019-10-17

選自Medium

作者:Adam Geitgey

機器之心編譯

參與:Panda

計算機視覺模型表現不佳的原因有很多,比如架構設計缺陷、資料集代表性不足、超引數選擇失誤等。但有一個很簡單的原因卻常常被人們忽略:影像的方向。機器學習專家 Adam Geitgey 近日釋出了一篇文章探討了這一簡單卻又讓很多人頭痛的問題,並分享了他為解決這一問題編寫的自動影像旋轉程式。

我寫過很多有關計算機視覺和機器學習專案的內容,比如目標識別系統和人臉識別專案。我有一個開源的 Python 人臉識別軟體庫,算得上是 GitHub 上最受歡迎的十大機器學習庫之一。這也意味著我常常收到關於 Python 和計算機視覺方面的新人提問。

為什麼我的 CV 模型不好用?沒想到原因竟如此簡單……

以我的經驗,有一個技術問題比其它任何問題都更容易讓人受挫——倒不是複雜的理論問題或昂貴 GPU 的問題。人們基本上沒意識到,幾乎所有人都是以側向方式將影像載入記憶體的,而計算機在檢測側向影像中的目標或人臉時的能力可沒那麼出色。

數位相機如何自動旋轉影像


當你在拍攝照片時,相機會感知你向哪邊傾斜。當你在另一個程式中檢視照片時,它們會以正確的方向顯示。


為什麼我的 CV 模型不好用?沒想到原因竟如此簡單……


但棘手的問題在於, 你的相機實際上並沒有在儲存到磁碟中的檔案中旋轉影像資料。因為數位相機中的影像感測器是逐行讀取的,最終彙整合連續的畫素資訊流。這能讓相機更輕鬆地儲存畫素資料,因為不管相機的姿勢如何,畫素資料總是以同樣的順序儲存的。

為什麼我的 CV 模型不好用?沒想到原因竟如此簡單……

實際上,照片能否以正確的方向顯示完全取決於影像檢視器應用。相機在儲存影像資料的同時還會儲存有關每張圖片的後設資料——相機設定、位置資料以及理所應當的相機的旋轉角度。影像檢視器應當使用這種資訊來正確地顯示影像。

影像後設資料最常見的格式是 Exif(Exchangeable image file forma「可交換影像檔案格式」的縮寫)。Exif 格式的後設資料放在相機儲存的 jpeg 檔案中。你不能直接從影像本身讀到這種 Exif 資料,但可以使用任何知道如何讀取這一資料的程式進行讀取。

下面是使用 Exiftool 讀取的上面的鵝照片的 Exif 後設資料:

為什麼我的 CV 模型不好用?沒想到原因竟如此簡單……

注意 Orientation(方向)這個資料元素。它能指示影像檢視器程式,在螢幕上顯示影像之前將圖順時針旋轉 90 度。如果程式忘記這麼做,影像就會側向顯示。


為什麼我的 CV 模型不好用?沒想到原因竟如此簡單……

為什麼這讓很多 Python 計算機視覺應用表現不佳?


Exif 後設資料並非 jpeg 檔案格式的原生部分。在 TIFF 檔案格式使用了這種後設資料之後,jpeg 檔案格式才加入這種後設資料。其保持了與老一代影像檢視器的後向相容性,但這也意味著某些程式根本沒有費心去解析 Exif 資料。

numpy、scipy、TensorFlow、Keras 等大多數用於處理影像資料的 Python 庫都將自己視為研究通用資料陣列的人的科學工具。所以它們不在乎消費者層面的問題,比如「影像自動旋轉」——即使現在的所有相機拍照需要這種操作。

這差不多意味著,你用任意 Python 庫載入影像時,都會得到未經旋轉的原始影像資料。現在猜猜看,當你將側向的或倒向的影像輸入人臉識別或目標檢測模型會怎樣?因為你提供了錯誤的資料,檢測器會提示失敗。

你可能認為這個問題僅限於新手或學生寫的 Python 指令碼,但事實並非如此。即使谷歌的旗艦級 Vision API 演示也沒能正確地處理 Exif 方向:


為什麼我的 CV 模型不好用?沒想到原因竟如此簡單……


谷歌的 Vision API 演示無法旋轉標準的手機拍攝的縱向影像。

儘管谷歌的視覺技術能成功地檢測出側向影像中存在一些動物,但它僅提供了一個不具體的「Animal(動物)」標籤。這是因為模型檢測側向的鵝要比檢測正向的鵝要困難得多。如果在輸入之前先正確地旋轉一下,則谷歌 Vision API 會得到如下的結果:


為什麼我的 CV 模型不好用?沒想到原因竟如此簡單……


當影像方向正確時,谷歌的檢測結果要具體得多——不僅能正確給出「Goose(鵝)」標籤,而且置信度分數要高得多,這就好多了。

如果你能如本演示中的那樣看到影像是側向的,那麼這個問題要明顯得多。但問題就在於你一般看不到。如今計算機上的一般程式都會以正確旋轉後的形式顯示影像,而不是按照它實際在磁碟上儲存的側向資料的形式。所以當你想了解你的模型不能起效的原因而檢視影像時,影像檢視器會以正確的方向顯示,讓你無從瞭解你的模型效果差的原因。


為什麼我的 CV 模型不好用?沒想到原因竟如此簡單……


Mac 上的 Finder 總是顯示應用了 Exif 旋轉後的影像,這樣就沒法看到檔案中的影像資料實際上是側向的。

這不可避免地導致人們在 GitHub 上報告問題,說他們使用的開源專案根本不行或模型不夠準確。但事情的本質非常簡單——他們輸入了側向甚至顛倒的影像!

解決這個問題


解決方案是,每當你用 Python 程式載入影像時,都執行一次 Exif 方向後設資料檢查,並在有需要時進行旋轉。做起來很簡單,不過在網上很難找到能為所有方向正確執行旋轉的示例程式碼。

下面是為任意影像應用正確的方向後再將其載入 numpy 陣列的程式碼:

import PIL.Image

import PIL.ImageOps
import numpy as np
def exif_transpose(img):
if not img:
return img
exif_orientation_tag = 274
# Check for EXIF data (only present on some files)
if hasattr(img, "_getexif") and isinstance(img._getexif(), dict) and exif_orientation_tag in img._getexif():
exif_data = img._getexif()
orientation = exif_data[exif_orientation_tag]
# Handle EXIF Orientation
if orientation == 1:
# Normal image - nothing to do!
pass
elif orientation == 2:
# Mirrored left to right
img = img.transpose(PIL.Image.FLIP_LEFT_RIGHT)
elif orientation == 3:
# Rotated 180 degrees
img = img.rotate(180)
elif orientation == 4:
# Mirrored top to bottom
img = img.rotate(180).transpose(PIL.Image.FLIP_LEFT_RIGHT)
elif orientation == 5:
# Mirrored along top-left diagonal
img = img.rotate(-90, expand=True).transpose(PIL.Image.FLIP_LEFT_RIGHT)
elif orientation == 6:
# Rotated 90 degrees
img = img.rotate(-90, expand=True)
elif orientation == 7:
# Mirrored along top-right diagonal
img = img.rotate(90, expand=True).transpose(PIL.Image.FLIP_LEFT_RIGHT)
elif orientation == 8:
# Rotated 270 degrees
img = img.rotate(90, expand=True)
return img
def load_image_file(file, mode='RGB'):
# Load the image with PIL
img = PIL.Image.open(file)
if hasattr(PIL.ImageOps, 'exif_transpose'):
# Very recent versions of PIL can do exit transpose internally
img = PIL.ImageOps.exif_transpose(img)
else:
# Otherwise, do the exif transpose ourselves
img = exif_transpose(img)
img = img.convert(mode)
return np.array(img)


之後,你可以將這個影像資料陣列傳遞給需要的所有標準 Python 機器學習庫,比如 Keras 和 TensorFlow。

因為這個問題很常見,所以我將其製作成了一個 pip 庫,名為 image_to_numpy,你可以這樣安裝它:


pip3 install image_to_numpy

你可以在任何 Python 程式中使用它來實現正確的影像載入,比如:

import matplotlib.pyplot as plt

import image_to_numpy# Load your image file
img = image_to_numpy.load_image_file("my_file.jpg")# Show it on the screen (or whatever you want to do)
plt.imshow(img)
plt.show()

更多資訊可檢視自述檔案。


原文連結:




來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69946223/viewspace-2660342/,如需轉載,請註明出處,否則將追究法律責任。

相關文章