訂閱
糾錯
加入自媒體

4種在生產(chǎn)中擾亂計算機(jī)視覺模型的方法

簡介

本文不是關(guān)于模型的質(zhì)量。它甚至不涉及擴(kuò)展、負(fù)載平衡和其他DevOp。它是關(guān)于一個更普遍但有時會遺漏的事情:處理不可預(yù)測的用戶輸入。

在訓(xùn)練模型時,數(shù)據(jù)科學(xué)家?guī)缀蹩偸怯幸粋受控的數(shù)據(jù)環(huán)境。這意味著使用已經(jīng)準(zhǔn)備好的數(shù)據(jù)集,或者有時間和資源手動收集、合并、清理和檢查數(shù)據(jù)。通過準(zhǔn)確地執(zhí)行此操作,可以提高基于這些數(shù)據(jù)訓(xùn)練的模型的質(zhì)量。

假設(shè)模型足夠好,可以部署到生產(chǎn)中。在最好的情況下,仍然可以控制環(huán)境(經(jīng)典的服務(wù)器端部署)。但即便如此,用戶也會從各種設(shè)備和來源上傳圖像。在邊緣部署的情況下,還有一層復(fù)雜性:無法控制環(huán)境。

在這兩種情況下,模型都應(yīng)該立即響應(yīng),開發(fā)人員沒有時間手動檢查數(shù)據(jù)。因此,至關(guān)重要的是:

· 隨時準(zhǔn)備數(shù)據(jù)

· 盡可能接近訓(xùn)練時間進(jìn)行預(yù)處理

否則,實(shí)際生產(chǎn)模型的質(zhì)量可能會比預(yù)期的低很多。這種情況之所以發(fā)生,是因?yàn)槿藗冊跀?shù)據(jù)中引入了偏見,而這是模型所沒有預(yù)料到的。

真實(shí)案例

在接下來的內(nèi)容中,將介紹一家公司開發(fā)計算機(jī)視覺模型時遇到的4個問題。在將其傳遞給算法(主要是神經(jīng)網(wǎng)絡(luò)),以下部分都與圖像處理有關(guān):

· 存儲在EXIF中的方向

· 非標(biāo)準(zhǔn)顏色配置文件

· 圖像庫中的差異

· 調(diào)整算法

對于每一個項目,都會提供一個案例研究和一段代碼,在生產(chǎn)中解決這個問題。

存儲在EXIF中的方向

如今,人們使用移動相機(jī)拍照(約91%,而且這個數(shù)字還在增長)。

通常情況下,移動設(shè)備會以預(yù)先確定的固定方向存儲圖像,而不管拍照時相機(jī)的實(shí)際位置是什么;謴(fù)初始方向所需的旋轉(zhuǎn)角度作為元信息存儲在EXIF中。

在移動設(shè)備或現(xiàn)代桌面軟件上觀看此類圖像可能沒問題,因?yàn)樗鼈兛梢蕴幚磉@些EXIF信息。但是以編程方式加載圖像時,許多庫讀取原始像素數(shù)據(jù)并忽略元信息。這會導(dǎo)致錯誤的圖像方向。在下面的示例中,我使用了PIL,它是用于圖像處理的最流行的Python庫之一。

input_image = "data/cup.jpg"

image = PIL.Image.open(input_image)

np_image_initial = np.a(chǎn)rray(image)

向神經(jīng)網(wǎng)絡(luò)發(fā)送這樣的圖像可能會產(chǎn)生完全隨機(jī)的結(jié)果。要解決這個問題,還需要讀取EXIF信息,并相應(yīng)地旋轉(zhuǎn)/鏡像圖像。

# get image EXIF

image_exif = image.getexif()

# retrieve orientation byte

orientation = image_exif.get(0x0112)

print(orientation)

# 6

# get the corresponding transformation

method = {

   2: PIL.Image.FLIP_LEFT_RIGHT,

   3: PIL.Image.ROTATE_180,

   4: PIL.Image.FLIP_TOP_BOTTOM,

   5: PIL.Image.TRANSPOSE,

   6: PIL.Image.ROTATE_270,

   7: PIL.Image.TRANSVERSE,

   8: PIL.Image.ROTATE_90,

}.get(orientation)


if method is not None:
   

  # replace original orientation

   image_exif[0x0112] = 1
   

   # apply transformation

   image = image.transpose(method)

np_image_correct = np.a(chǎn)rray(image)

PIL允許讀取EXIF元信息并對其進(jìn)行解析。它知道在哪里尋找方向。存儲必要信息的字節(jié)是0x0112,并且文檔說明了如何處理每個值,以及如何處理圖像來恢復(fù)初始方向。

使用EXIF代碼讀取并正確旋轉(zhuǎn)的杯子圖像

這個問題似乎已經(jīng)解決了,對嗎?嗯,還沒有。

讓我們試試另一張照片。這一次,我將使用現(xiàn)代的HEIF圖像格式,而不是舊的JPEG(請注意,需要一個特殊的PIL插件)。默認(rèn)情況下,iPhone以這種格式拍攝HDR圖片。

圖中顯示了站在門口的女孩的圖像及其元數(shù)據(jù)

一切看起來都一樣。讓我們試著用一種天真的方式從代碼中讀取它,而不看EXIF。

input_image = "data/person.heic"

image = PIL.Image.open(input_image)

np_image_initial = np.a(chǎn)rray(image)

圖像:

原始圖像方向正確!但EXIF建議將其逆時針旋轉(zhuǎn)90度!所以方位恢復(fù)代碼會失敗。

這是一個已知的問題,iPhone在HEIC拍攝時,由于某些不清楚的原因,iPhone存儲的原始像素是正確的,但在以這種格式拍照時,仍然在EXIF中保持傳感器方向。

因此,當(dāng)圖像在iPhone上以HEIC格式拍攝時,應(yīng)該修正算法并省略旋轉(zhuǎn)。

# get image EXIF

image_exif = image.getexif()

# get image format

image_format = image.format

# retrieve orientation byte

orientation = image_exif.get(0x0112)

print(orientation)

# 6

print(image_format)

# HEIF

# get the corresponding transformation

method = {

   2: PIL.Image.FLIP_LEFT_RIGHT,

   3: PIL.Image.ROTATE_180,

   4: PIL.Image.FLIP_TOP_BOTTOM,

   5: PIL.Image.TRANSPOSE,

   6: PIL.Image.ROTATE_270,

   7: PIL.Image.TRANSVERSE,

   8: PIL.Image.ROTATE_90,

}.get(orientation)


if method is not None:
   

  # replace original orientation

   image_exif[0x0112] = 1
   

   # apply transformation if not HEIF

   if image_format != "HEIF":

       image = image.transpmemoryviewe(method)

np_image_correct = np.a(chǎn)rray(image)

當(dāng)然,來自其他設(shè)備的圖像定向可能會有更多問題,而這段代碼可能并不詳盡。但它清楚地表明了一個人應(yīng)對用戶輸入的謹(jǐn)慎程度,以及即使沒有惡意意圖,用戶輸入的不可預(yù)測性。

非標(biāo)準(zhǔn)顏色配置文件

元信息隱藏了另一個挑戰(zhàn)。圖片可能會被拍攝并存儲在不同的顏色空間和顏色配置文件中。

有了顏色空間,它或多或少是清晰的。它定義了用于存儲每個像素的圖像顏色信息的通道。最常見的兩種顏色空間是RGB(紅-綠-藍(lán))和CMYK(青-品紅-黃-黑)。人們幾乎不會弄錯CMYK中的圖像,因?yàn)樗鼈冇胁煌瑪?shù)量的通道:RGB是3。

因此,由于輸入通道數(shù)量錯誤,將其發(fā)送到網(wǎng)絡(luò)會立即中斷。所以這種從CMYK到RGB的轉(zhuǎn)換很少被忘記。

顏色配置文件要復(fù)雜得多。這是輸入(攝像頭)或輸出(顯示)設(shè)備的特征。它說明了特定于設(shè)備的記錄或顯示顏色的方式。

RGB顏色空間有很多不同的配置文件:sRGB、Adobe RGB、Display P3等。每個配置文件定義了自己的方式,將原始RGB值映射到人眼感知的真實(shí)顏色。

這導(dǎo)致了一個事實(shí),即相同的原始RGB值可能意味著不同圖片中的不同顏色。要解決這個問題,需要仔細(xì)地將所有圖像的顏色配置文件轉(zhuǎn)換為一個選定的標(biāo)準(zhǔn)。

通常,它是一個sRGB配置文件,因?yàn)橛捎跉v史原因,它是所有網(wǎng)站的默認(rèn)顏色配置文件。

在iPhone上拍攝的照片通常有一個顯示P3顏色的配置文件。讓我們從代碼中讀取圖像,看看它在進(jìn)行顏色配置文件轉(zhuǎn)換和不進(jìn)行顏色配置文件轉(zhuǎn)換時是什么樣子。

input_image = "data/city.heic"

image_initial = PIL.Image.open(input_image)

# open default ICC profile which is sRGB

working_icc_profile = PIL.ImageCms.getOpenProfile(

   "data/SRGB.icc"



# read ICC profile from the image

image_icc_profile = PIL.ImageCms.getOpenProfile(

   io.BytesIO(image_initial.info["icc_profile"])



# convert an image to default ICC profile

image_converted = PIL.ImageCms.profileToProfile(

   im=image_initial,

   inputProfile=image_icc_profile,

   outputProfile=working_icc_profile,

   renderingIntent=PIL.ImageCms.INTENT_PERCEPTUAL,

   outputMode="RGB"

image_initial_np = np.a(chǎn)rray(image_initial)

image_converted_np = np.a(chǎn)rray(image_converted)

可以看到,通過轉(zhuǎn)換讀取的圖像更生動,更接近原始圖像。根據(jù)圖片和顏色配置,這種差異可能更大或更小。

發(fā)送到神經(jīng)網(wǎng)絡(luò)的顏色(即像素值)可能會影響最終質(zhì)量并破壞預(yù)測。因此,恰當(dāng)?shù)嘏c他們合作至關(guān)重要。

圖像庫中的差異

正確讀取圖像是非常重要的。但與此同時,我們應(yīng)該盡快做到這一點(diǎn)。因?yàn)樵谠朴嬎銜r代,時間就是金錢。此外,客戶不想等待太久。

這里的最終解決方案是切換到C/C++。有時,在產(chǎn)生式推理環(huán)境中,它可能完全有意義。但如果你想留在Python生態(tài)系統(tǒng)中,有很多選擇。每個圖像庫都有自己的功能和速度。

直到現(xiàn)在,我只使用了PIL模塊。為了進(jìn)行比較,我選擇了另外兩個流行的庫:OpenCV和Scikit image。

讓我們看看每個庫讀取不同大小的JPEG圖像的速度。

def read_cv2(file):
   

   image_cv2 = cv2.cvtColor(

       cv2.imread(

           file, 

           cv2.IMREAD_UNCHANGED

       ), 

       cv2.COLOR_BGR2RGB

   )
   

   return image_cv2

def read_pil(file):
   

   image_pil = PIL.Image.open(file)
   

   _ = np.a(chǎn)rray(image_pil)
   

   return image_pil

def read_skimage(file):
   

   image_skimage = skimage.io.imread(file)
   

   return image_skimage

num_repeats = 10

# read 37 JPEG images of different sizes and measure time

times_read_cv2 = measure_read_times(read_cv2, num_repeats)

times_read_pil = measure_read_times(read_pil, num_repeats)

times_read_skimage = measure_read_times(read_skimage, num_repeats)

對于小圖像,幾乎沒有區(qū)別。但對于大型的,OpenCV的速度大約是PIL和Scikit圖像的1.5倍。根據(jù)圖像內(nèi)容和格式(JPEG、PNG等),這種差異可能從1.4x到2.0x不等。但總的來說,OpenCV要快得多。

網(wǎng)絡(luò)上還有其他可靠的基準(zhǔn)給出了大致相同的數(shù)字。對于圖像書寫來說,這種差異可能更為顯著:OpenCV的速度要快4到10倍。

另一個非常常見的操作是調(diào)整大小。人們幾乎總是在將圖像發(fā)送到神經(jīng)網(wǎng)絡(luò)之前調(diào)整圖像的大小。這就是OpenCV真正閃耀的地方。

times_resize_cv2 = measure_time(

   f=lambda: cv2.resize(image_cv2, (1000, 1000), interpolation=cv2.INTER_LINEAR),

   num=20,

times_resize_pil = measure_time(

   f=lambda: image_pil.resize((1000, 1000), resample=PIL.Image.LINEAR),

   num=20,

times_resize_skimage = measure_time(

   f=lambda: skimage.transform.resize(image_skimage, (1000, 1000)),

   num=20,


print(f"cv2:       {np.mean(times_resize_cv2):.3f}s")

print(f"pil:       {np.mean(times_resize_pil):.3f}s")

print(f"skimage:   {np.mean(times_resize_skimage):.3f}s")

# cv2:       0.006s

# pil:       0.135s

# skimage:   4.531s

在這里,我拍攝了一張7360x4100的圖像,并將其調(diào)整為1000x1000。OpenCV比PIL快22倍,比Scikit image快755倍!

選擇正確的庫可以節(jié)省大量時間。需要注意的是,相同的調(diào)整大小算法可能會在不同的實(shí)現(xiàn)中產(chǎn)生不同的結(jié)果:

image_cv2_resized = cv2.resize(

   image_cv2, 

   (1000, 1000), 

   interpolation=cv2.INTER_LINEAR

image_pil_resized = image_pil.resize(

   (1000, 1000), 

   resample=PIL.Image.LINEAR

print(

   "Original images are the same:                    ", 

   (image_cv2 == np.a(chǎn)rray(image_pil)).a(chǎn)ll()

print(

   "Resized images are the same:                     ", 

   (image_cv2_resized == np.a(chǎn)rray(image_pil_resized)).a(chǎn)ll()

mae = np.a(chǎn)bs(image_cv2_resized.a(chǎn)stype(int) - 

            np.a(chǎn)rray(image_pil_resized, dtype=int)).mean()

print(

   "Mean Absolute Difference between resized images: ",

   f"{mae:.2f}"

# Original images are the same:                     True

# Resized images are the same:                      False

# Mean Absolute Difference between resized images:  5.37

在這里,人們可以注意到,我對OpenCV和PIL都使用線性插值進(jìn)行下采樣。原始圖像是相同的。但結(jié)果不同。差別非常顯著:每種像素顏色平均255個像素中有5個不同。

因此,如果在訓(xùn)練和推理過程中使用不同的庫來調(diào)整大小,可能會影響模型的質(zhì)量。所以我們應(yīng)該密切關(guān)注它。

調(diào)整算法

除了不同庫之間的速度差異外,即使在一個庫中,也有不同的調(diào)整大小算法。我們應(yīng)該選擇哪一個?至少,這取決于是否要減小圖像大。ㄏ虏蓸樱┗蛟黾訄D像大。ㄉ喜蓸樱。

有許多調(diào)整圖像大小的算法。它們產(chǎn)生的圖像質(zhì)量和速度不同。我只看這5個,它們足夠好、足夠快,并且在主流庫中得到支持。

下面提供的結(jié)果也與一些指南和示例一致。

image = cv2.cvtColor(

   cv2.imread(

       input_image, 

       cv2.IMREAD_UNCHANGED

   ), 

   cv2.COLOR_BGR2RGB


# define list of algorithms

algos = {

   "AREA": cv2.INTER_AREA,

   "NEAREST": cv2.INTER_NEAREST,

   "LINEAR": cv2.INTER_LINEAR,

   "CUBIC": cv2.INTER_CUBIC,

   "LANCZOS": cv2.INTER_LANCZOS4,


# draw original image

plt.imshow(image[1500:2500, 5000:6000])


for algo in algos:
   

  # downsample the original image with selected algorithm

   image_resized = cv2.resize(image, (736, 410), interpolation=algos[algo])
   

   # draw results

   plt.imshow(image_resized[150:250, 500:600])

對于下采樣,“area”算法看起來最好。它產(chǎn)生的噪音和偽影最少。事實(shí)上,它是OpenCV進(jìn)行下采樣的首選。

現(xiàn)在讓我們進(jìn)行上采樣——將采樣后的圖像恢復(fù)到原始大小,看看在這種情況下哪種算法最有效。

# create downsampled image with preferred "area" algorithm

image_downsampled = cv2.resize(image, (736, 410), interpolation=cv2.INTER_AREA)


# draw original image

plt.imshow(image[1500:2500, 5000:6000])

for algo in algos:
   

  # upsample the downsampled image back to the original size

   # with the selected resizing algorithm

   image_resized = cv2.resize(

       image_downsampled, 

       (7360, 4100), 

       interpolation=algos[algo]

   )
   

   # draw results

   plt.imshow(image_resized[1500:2500, 5000:6000])

對于上采樣,算法會產(chǎn)生更一致的結(jié)果。盡管如此,“cubic”插值看起來模糊程度最低,最接近原始(“l(fā)anczos”提供了類似的結(jié)果,但速度要慢得多)。

所以這里的最終結(jié)論是使用“area”插值進(jìn)行下采樣,使用“cubic”算法進(jìn)行上采樣。

請注意,正確的調(diào)整算法選擇在訓(xùn)練期間也很重要,因?yàn)樗梢蕴岣哒w圖像質(zhì)量。更重要的是,訓(xùn)練和推理階段的調(diào)整算法應(yīng)該是相同的,否則模型可能會出問題。

結(jié)論

這篇文章,描述了在計算機(jī)視覺領(lǐng)域工作多年期間多次遇到的真實(shí)案例和問題。如果處理不當(dāng),它們中的每一個都可能會顯著降低生產(chǎn)中的模型質(zhì)量。

感謝閱讀!

       原文標(biāo)題 : 4種在生產(chǎn)中擾亂計算機(jī)視覺模型的方法

聲明: 本文由入駐維科號的作者撰寫,觀點(diǎn)僅代表作者本人,不代表OFweek立場。如有侵權(quán)或其他問題,請聯(lián)系舉報。

發(fā)表評論

0條評論,0人參與

請輸入評論內(nèi)容...

請輸入評論/評論長度6~500個字

您提交的評論過于頻繁,請輸入驗(yàn)證碼繼續(xù)

  • 看不清,點(diǎn)擊換一張  刷新

暫無評論

暫無評論

人工智能 獵頭職位 更多
掃碼關(guān)注公眾號
OFweek人工智能網(wǎng)
獲取更多精彩內(nèi)容
文章糾錯
x
*文字標(biāo)題:
*糾錯內(nèi)容:
聯(lián)系郵箱:
*驗(yàn) 證 碼:

粵公網(wǎng)安備 44030502002758號