1. 程式人生 > >學習筆記:caffe2 教程記錄三

學習筆記:caffe2 教程記錄三

接著caffe2 教程記錄二,這個是第三篇

##3.影象載入與預處理

 影象載入和預處理:

在本教程中,我們將研究如何從本地檔案或URL載入影象,然後您可以在其他教程或示例中使用這些檔案。此外,我們將深入研究使用caffe2 和影象時所需的預處理型別。

mac osx 先決條件

如果您尚未安裝這些python 依賴,你現在需要這樣做。( 執行下面的命令,進行安裝)

sudo pip install scikit-image scipy matplotlib

fromfrom  __future____futur  import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

%matplotlib inline
import skimage
import skimage.io as io
import skimage.transform 
import sys
import numpy as np
import math
from matplotlib import pyplot
import matplotlib.image as mpimg
print("Required modules imported.")

測試圖片:

在下面的程式碼塊中,使用IMAGE_LOCATION載入您要測試的內容。 只需更改註釋標記即可完成每輪教程。 通過這種方式,您將瞭解各種影象格式會發生什麼,以及有關如何預處理它們的一些提示。 如果要嘗試自己的影象,請將其放在images資料夾中或使用遠端URL。 當您選擇遠端URL時,請輕鬆自己並嘗試查詢指向常見影象檔案型別和副檔名的URL,而不是某些長識別符號或查詢字串,這可能會破壞下一步。

顏色問題:

記住,當你從智慧手機攝像頭載入影象時,你可能會遇到顏色格式化問題。下面我們展示RGB和BGR之間如何翻轉可以影響影象的例子。這顯然會導致模型中的檢測失效。確保你傳遞的影象資料是你所想的!

caffe2 使用RGB 訂單:

由於OpenCV在Caffe中的傳統支援,以及它如何處理藍綠紅(BGR)順序的影象,而不是更常用的紅綠藍(RGB)順序,Caffe2還期望BGR順序。從長遠來看,這個決定在很多方面都有幫助,因為您使用了不同的計算機視覺實用程式和庫,但是它也可能是混淆的來源。

# You can load either local IMAGE_FILE or remote URL
# For Round 1 of this tutorial, try a local image.
IMAGE_LOCATION = 'images/cat.jpg'

# For Round 2 of this tutorial, try a URL image with a flower: 
# IMAGE_LOCATION = "https://cdn.pixabay.com/photo/2015/02/10/21/28/flower-631765_1280.jpg"
# IMAGE_LOCATION = "images/flower.jpg"

# For Round 3 of this tutorial, try another URL image with lots of people:
# IMAGE_LOCATION = "https://upload.wikimedia.org/wikipedia/commons/1/18/NASA_Astronaut_Group_15.jpg"
# IMAGE_LOCATION = "images/astronauts.jpg"

# For Round 4 of this tutorial, try a URL image with a portrait!
# IMAGE_LOCATION = "https://upload.wikimedia.org/wikipedia/commons/9/9a/Ducreux1.jpg"
# IMAGE_LOCATION = "images/Ducreux.jpg"

img = skimage.img_as_float(skimage.io.imread(IMAGE_LOCATION)).astype(np.float32)

# test color reading
# show the original image
pyplot.figure()
pyplot.subplot(1,2,1)
pyplot.imshow(img)
pyplot.axis('on')
pyplot.title('Original image = RGB')

# show the image in BGR - just doing RGB->BGR temporarily for display
imgBGR = img[:, :, (2, 1, 0)]
#pyplot.figure()
pyplot.subplot(1,2,2)
pyplot.imshow(imgBGR)
pyplot.axis('on')
pyplot.title('OpenCV, Caffe2 = BGR')

 

正如您在上面的示例中所看到的,順序的差異是很重要的。 在下面的程式碼塊中,我們將拍攝影象並轉換為BGR順序,以便Caffe適當地處理它。
但是,等等,有更多的色彩樂趣…

Caffe更喜歡CHW訂單:

現在你知道,什麼是CHW和HWC嗎,這兩種格式經常都會出現在影象處理中。

  • H: Height (高)
  • W: Width (寬)
  • C: Channel (渠道 顏色) 

深入研究影象資料的儲存方式是記憶體分配順序。您可能已經注意到,當我們第一次載入影象時,我們強制它通過一些有趣的轉換。這些是資料轉換,讓我們可以像使用立方體一樣使用影象。我們看到的是在立方體的頂部,操縱下面的層可以改變我們所看到的。我們可以修補它的基本屬性,如上所述,很容易交換顏色。

對於GPU處理,這是Caffe2擅長的,這個訂單需要是CHW。對於CPU處理,此順序通常為HWC。基本上,您將要使用CHW並確保步驟包含在影象管道中。將RGB調整為BGR,將其封裝為此“C”有效負載,然後調整HWC,“C”是您剛剛切換的相同顏色。

你可能會問為什麼!原因指向cuDNN,這有助於加速GPU的處理。它只使用CHW,我們總結說它更快。

鑑於這兩種轉變,您可能認為這已足夠,但事實並非如此。我們仍然需要調整大小和/或裁剪,並可能會檢視方向(旋轉)和映象等內容。

旋轉和映象: 

這個話題通常保留在來自智慧手機的影象上。一般來說,手機拍攝的照片很棒,但在拍攝照片的方式以及應該採用的方向方面做得非常的糟糕。然後是使用者會用手機做所有的事情,讓他們做設計師從未預料到的事情。相機-是的,因為通常有兩個相機,這兩個相機在畫素數和縱橫比上都採用不同大小的圖片,不僅如此,它們有時會將它們映象,有時也會以肖像和風景模式拍攝它們,有時它們不費力地告訴它們。他們在哪個模式。

在很多方面,這是您需要在預處理中評估的第一件事,然後檢視大小(下面描述),然後計算顏色情況。如果你正在開發iOS,那麼你很幸運,這將是比較容易的。如果你是一個超級黑客的嚮導開發人員,有lead-lined shorts,為Android開發,那麼至少你有lead-lined shorts.

Android市場的變化是驚人的和可怕的。在理想的情況下,您可以依賴來自任何相機的圖片中的EXIF資料,並使用它來確定方向和映象,並且您有一個簡單的case函式來處理轉換。沒有這樣的運氣,但你並不孤單。許多人來到你面前,為你受苦。

# Image came in sideways - it should be a portait image!
# How you detect this depends on the platform
# Could be a flag from the camera object
# Could be in the EXIF data
# ROTATED_IMAGE = "https://upload.wikimedia.org/wikipedia/commons/8/87/Cell_Phone_Tower_in_Ladakh_India_with_Buddhist_Prayer_Flags.jpg"
ROTATED_IMAGE = "images/cell-tower.jpg"
imgRotated = skimage.img_as_float(skimage.io.imread(ROTATED_IMAGE)).astype(np.float32)
pyplot.figure()
pyplot.imshow(imgRotated)
pyplot.axis('on')
pyplot.title('Rotated image')

# Image came in flipped or mirrored - text is backwards!
# Again detection depends on the platform
# This one is intended to be read by drivers in their rear-view mirror
# MIRROR_IMAGE = "https://upload.wikimedia.org/wikipedia/commons/2/27/Mirror_image_sign_to_be_read_by_drivers_who_are_backing_up_-b.JPG"
MIRROR_IMAGE = "images/mirror-image.jpg"
imgMirror = skimage.img_as_float(skimage.io.imread(MIRROR_IMAGE)).astype(np.float32)
pyplot.figure()
pyplot.imshow(imgMirror)
pyplot.axis('on')
pyplot.title('Mirror image')

 

 所以你可以看到我們遇到了一些問題。 如果我們正在探測地點,地標或物體,那麼側身的細胞塔就不好了。 如果我們檢測文字並進行自動語言翻譯,那麼映象文字就不好了。 但是,嘿,也許你想製作一個可以同時檢測英語的模型。 這將是非常棒的,但不適用於本教程!

讓我們把這些babies into 轉換成caffe2能識別的,我們周圍的標準檢測模型可以檢測到的。此外,這個小技巧可能會拯救你,例如,例如,你真的必須檢測細胞塔,但沒有找到EXIF資料:那麼你將迴圈每一次旋轉,每次翻轉,產生這張照片的許多衍生物 並執行它們。 當檢測的置信度百分比足夠高時,Bam !,你找到了你需要的方向和偷偷摸摸的細胞塔。

總之,對於示列程式碼:

# Run me to flip the image back and forth
imgMirror = np.fliplr(imgMirror)
pyplot.figure()
pyplot.imshow(imgMirror)
pyplot.axis('off')
pyplot.title('Mirror image')

 

# Run me to rotate the image 90 degrees
imgRotated = np.rot90(imgRotated, 3)
pyplot.figure()
pyplot.imshow(imgRotated)
pyplot.axis('off')
pyplot.title('Rotated image')

 


# Model is expecting 224 x 224, so resize/crop needed.# Model 
# First, let's resize the image to 256*256
orig_h, orig_w, _ = img.shape
print("Original image's shape is {}x{}".format(orig_h, orig_w))
input_height, input_width = 224, 224
print("Model's input shape is {}x{}".format(input_height, input_width))
img256 = skimage.transform.resize(img, (256, 256))

# Plot original and resized images for comparison
f, axarr = pyplot.subplots(1,2)
axarr[0].imshow(img)
axarr[0].set_title("Original Image (" + str(orig_h) + "x" + str(orig_w) + ")")
axarr[0].axis('on')
axarr[1].imshow(img256)
axarr[1].axis('on')
axarr[1].set_title('Resized image to 256x256')
pyplot.tight_layout()

print("New image shape:" + str(img256.shape))

圖片大小:

預處理的一部分是調整大小。 由於我們不會進入這裡的原因,Caffe2預處理中的影象應該是方形的。 另外,為了提高效能,應將它們調整為標準高度和寬度,通常小於原始來源。 在下面的示例中,我們將調整為256 x 256畫素,但您可能會注意到input_height和input_width設定為224 x 224,然後用於指定裁剪。 這是幾個基於影象的模型所期望的。 他們接受了大小為224 x 224的影象訓練,為了使模型能夠正確識別您投射的可疑影象,這些影象也應該是224 x 224。 

注意,調整大小會使影象失真一點點。在處理過程中認識到這種影響是很重要的,因為它可以對模型的結果產生影響。花和動物可以稍微伸展或擠壓,但面部特徵可能不會。

當原始影象的尺寸與所需的尺寸成比例地精確時,可能會發生這種情況。在這個特定的例子中,最好是調整大小到224x224,而不麻煩裁剪。讓我們嘗試重新縮放影象和保持縱橫比的另一種策略。

重新縮放:

如果你設想肖像影象與風景影象,你就會知道有很多東西可以通過調整大小來處理,不然會搞砸。 重新縮放假設您正在鎖定寬高比以防止影象失真。 在這種情況下,我們將影象縮小到與模型輸入大小匹配的最短邊。

在我們這裡的示例中,模型大小為224 x 224.當您在1920x1080中檢視顯示器時,它的寬度比高度更長,如果將其縮小到224,則在用完之前就會超出高度。 寬度,所以......

風景:限制高度調整大小
縱向:限制按寬度調整大小

print("Original image shape:" + str(img.shape) + " and remember it should be in H, W, C!")
print("Model's input shape is {}x{}".format(input_height, input_width))
aspect = img.shape[1]/float(img.shape[0])
print("Orginal aspect ratio: " + str(aspect))
if(aspect>1):
    # landscape orientation - wide image
    res = int(aspect * input_height)
    imgScaled = skimage.transform.resize(img, (input_height, res))
if(aspect<1):
    # portrait orientation - tall image
    res = int(input_width/aspect)
    imgScaled = skimage.transform.resize(img, (res, input_width))
if(aspect == 1):
    imgScaled = skimage.transform.resize(img, (input_height, input_width))
pyplot.figure()
pyplot.imshow(imgScaled)
pyplot.axis('on')
pyplot.title('Rescaled image')
print("New image shape:" + str(imgScaled.shape) + " in HWC")

此時,只有一個維度設定為模型輸入所需的維度。 我們仍然需要裁剪一邊做一個正方形。

 

裁剪:

事實上,我們可以倒退並決定做一箇中心作物。 所以我們不是縮小到最小的,我們可以至少在一邊,我們從中間拿出一大塊。 如果我們在沒有縮放的情況下完成了這項操作,那麼我們最終只能使用花朵踏板的一部分,因此我們仍需要對影象進行一些調整。

下面我們將嘗試一些裁剪策略:

  1. 只要從中間抓取你需要的精確尺寸!
  2. 調整到一個非常接近的正方形然後從中間抓取。
  3. 使用重新縮放的影象並抓住中間。
# Compare the images and cropping strategies
# Try a center crop on the original for giggles
print("Original image shape:" + str(img.shape) + " and remember it should be in H, W, C!")
def crop_center(img,cropx,cropy):
    y,x,c = img.shape
    startx = x//2-(cropx//2)
    starty = y//2-(cropy//2)    
    return img[starty:starty+cropy,startx:startx+cropx]
# yes, the function above should match resize and take a tuple...

pyplot.figure()
# Original image
imgCenter = crop_center(img,224,224)
pyplot.subplot(1,3,1)
pyplot.imshow(imgCenter)
pyplot.axis('on')
pyplot.title('Original')

# Now let's see what this does on the distorted image
img256Center = crop_center(img256,224,224)
pyplot.subplot(1,3,2)
pyplot.imshow(img256Center)
pyplot.axis('on')
pyplot.title('Squeezed')

# Scaled image
imgScaledCenter = crop_center(imgScaled,224,224)
pyplot.subplot(1,3,3)
pyplot.imshow(imgScaledCenter)
pyplot.axis('on')
pyplot.title('Scaled')

pyplot.tight_layout()

 正如你所看到的那樣,除了最後一個之外,效果還不太好。中間的一個也可能是好的,但你不會知道,直到你嘗試模型和測試大量的候選影象。在這一點上,我們可以看到我們的差異,把它分成兩半,從兩邊去除一些畫素。然而,這確實有一個缺點,因為一個偏離中心的主題會被剪輯。
如果你現在已經運行了幾次教程並且在第3輪,你會發現一個相當大的問題。你錯過了astronauts!你還可以從第2輪看到花的問題。截切後缺少的東西,可能會給你帶來麻煩。可以這樣想想:如果不知道正在使用的模型是如何準備的,那麼就不知道如何,所以要使影象符合要求,因此請注意測試結果!如果模型使用了許多不同的高寬比影象,並且只是將它們壓縮成一個正方形,那麼隨著時間推移和大量樣本,它“學習”了什麼是壓縮的並且可以匹配的。但是,如果您正在尋找像面部特徵和地標之類的細節,或者任何影象中的細微差別元素,那麼這可能是危險的,並且容易出錯。

沒有進一步策略?

另一種策略是使用真實資料重新調整到最佳尺寸,然後使用您可以在模型中安全忽略的資訊填充影象的其餘部分。 因為你在這裡經歷了足夠的經驗,我們將把它儲存到另一個教程中!

Upscaling(倍增):

當你想要執行的影象是“微小”的時候,你會怎麼做?在我們的例子中,我們已經準備了輸入影象,規格為224x224。請考慮下面的128X128影象。

現在我們不是在談論超解析度或CSI效應,我們可以拍攝模糊的ATM照片,犯罪人員脖子上的紋身, 儘管如此,深度學習已經提供了一些進展 (some advances,如果您正在及時閱讀本文(3/1/17之前),那麼可以去看看(check this out)。我們想要做的很簡單,但是,就像裁剪一樣,它確實有各種你應該考慮的策略。

最基本的方法是從一個小方塊到一個更大的方塊,並使用defauls skimage為您提供的預設功能。 這個調整大小的方法預設插值順序引數為1,如果你甚至關心,這個引數恰好是雙線性的,但值得一提,因為這些可能是你以後需要修改問題的微調旋鈕,比如奇怪的視覺偽像, 可以在影象 upscaling中引入。

imgTiny = "images/Cellsx128.png"
imgTiny = skimage.img_as_float(skimage.io.imread(imgTiny)).astype(np.float32)
print("Original image shape: ", imgTiny.shape)
imgTiny224 = skimage.transform.resize(imgTiny, (224, 224))
print("Upscaled image shape: ", imgTiny224.shape)
# Plot original
pyplot.figure()
pyplot.subplot(1, 2, 1)
pyplot.imshow(imgTiny)
pyplot.axis('on')
pyplot.title('128x128')
# Plot upscaled
pyplot.subplot(1, 2, 2)
pyplot.imshow(imgTiny224)
pyplot.axis('on')
pyplot.title('224x224')

太棒了,效果不錯 您可以在形狀輸出中看到(128,128,4)並且您已收到(224,224,4)。 等一下!4? 到目前為止,在每個例子中,形狀的最後一個值是3! 當我們使用png檔案時,我們進入了一個新的現實; 一個可以透明的地方。 第4個值描述不透明度或透明度,具體取決於您是否為玻璃半空型別。 無論如何,我們可以很好的處理,但要注意這個數字。

重要的是要知道,在我們對影象進行進一步操作之前,如果對影象的當前格式進行簡單的重取樣,那麼它的資料順序和整體有效負載可能會使資料和影象變得混亂。 請記住,它目前是一個數據立方體,現在還有更多的資料,而不僅僅是紅色,綠色和藍色(以及不透明度)。 根據您決定調整大小時,您必須考慮額外的資料。

我們打破一下吧! 將影象切換為CHW後,嘗試放大影象。

imgTiny = "images/Cellsx128.png"
imgTiny = skimage.img_as_float(skimage.io.imread(imgTiny)).astype(np.float32)
print("Image shape before HWC --> CHW conversion: ", imgTiny.shape)
# swapping the axes to go from HWC to CHW
# uncomment the next line and run this block!
imgTiny = imgTiny.swapaxes(1, 2).swapaxes(0, 1)
print("Image shape after HWC --> CHW conversion: ", imgTiny.shape)
imgTiny224 = skimage.transform.resize(imgTiny, (224, 224))
print("Image shape after resize: ", imgTiny224.shape)
# we know this is going to go wrong, so...
try:
    # Plot original
    pyplot.figure()
    pyplot.subplot(1, 2, 1)
    pyplot.imshow(imgTiny)
    pyplot.axis('on')
    pyplot.title('128x128')
except:
    print("Here come bad things!")
    # hands up if you want to see the error (uncomment next line)
    #raise

 失敗了,對吧,如果你讓上面的程式碼塊交換軸,然後調整影象大小,你會看到這個輸出:

Image shape after resize: (224, 224, 128)

現在你有128個,你應該還有4, 糟糕。 讓我們在下面的程式碼塊中恢復並嘗試其他方法。 我們將展示一個示例,其中影象小於您的輸入規範,而不是正方形。 就像它可能來自一個只能在矩形帶中拍攝影象的新顯微鏡。

imgTiny = "images/Cellsx128.png"
imgTiny = skimage.img_as_float(skimage.io.imread(imgTiny)).astype(np.float32)
imgTinySlice = crop_center(imgTiny, 128, 56)
# Plot original
pyplot.figure()
pyplot.subplot(2, 1, 1)
pyplot.imshow(imgTiny)
pyplot.axis('on')
pyplot.title('Original')
# Plot slice
pyplot.figure()
pyplot.subplot(2, 2, 1)
pyplot.imshow(imgTinySlice)
pyplot.axis('on')
pyplot.title('128x56')
# Upscale?
print("Slice image shape: ", imgTinySlice.shape)
imgTiny224 = skimage.transform.resize(imgTinySlice, (224, 224))
print("Upscaled slice image shape: ", imgTiny224.shape)
# Plot upscaled
pyplot.subplot(2, 2, 2)
pyplot.imshow(imgTiny224)
pyplot.axis('on')
pyplot.title('224x224')

好吧,這對於升級 如何失敗的一個例子來說有點延伸。 得到它? 伸展? 然而,這可能是一種生死攸關的失敗。 如果正常細胞是圓形的並且患病的細胞被拉長並彎曲怎麼辦? 鐮狀細胞性貧血例如:

在這種情況下,你會怎麼做? 這實際上取決於模型以及它是如何訓練的。 在某些情況下,可以將影象的其餘部分填充為白色,或者可能是黑色,或者可能是噪聲,或者甚至可以使用png和透明度併為影象設定遮罩,以便模型忽略透明區域。 看看你能想出多少有趣的事情,你也可以取得醫學上的突破!

讓我們繼續我們已經提到的最後一步,即將影象輸入調整為BGR順序。 Caffe2還有另一個特點,即批量術語。 我們已經談過CHW了。 對於NCHW中的影象數量,這是N.

最終預處理和批處理術語:

在下面的最後一步中,我們將把影象的資料順序切換到BGR,將其填入Color列,然後對列進行重新排序以進行GPU處理(HCW - > CHW),然後向影象新增第四維(N) 跟蹤影象的數量。 從理論上講,您可以繼續為資料新增維度,但Caffe2需要這個維度,因為它會向Caffe傳達此批次中預期的影象數量。 我們將其設定為一(1)表示此批次中只有一張影象進入Caffe。 請注意,在我們檢查img.shape的最終輸出中,順序是完全不同的。 我們為影象數添加了N,並改變了順序:N,C,H,W

# This next line helps with being able to rerun this section
# if you want to try the outputs of the different crop strategies above
# swap out imgScaled with img (original) or img256 (squeezed)
imgCropped = crop_center(imgScaled,224,224)
print("Image shape before HWC --> CHW conversion: ", imgCropped.shape)
# (1) Since Caffe expects CHW order and the current image is HWC,
#     we will need to change the order.
imgCropped = imgCropped.swapaxes(1, 2).swapaxes(0, 1)
print("Image shape after HWC --> CHW conversion: ", imgCropped.shape)

pyplot.figure()
for i in range(3):
    # For some reason, pyplot subplot follows Matlab's indexing
    # convention (starting with 1). Well, we'll just follow it...
    pyplot.subplot(1, 3, i+1)
    pyplot.imshow(imgCropped[i], cmap=pyplot.cm.gray)
    pyplot.axis('off')
    pyplot.title('RGB channel %d' % (i+1))

# (2) Caffe uses a BGR order due to legacy OpenCV issues, so we
#     will change RGB to BGR.
imgCropped = imgCropped[(2, 1, 0), :, :]
print("Image shape after BGR conversion: ", imgCropped.shape)

# for discussion later - not helpful at this point
# (3) (Optional) We will subtract the mean image. Note that skimage loads
#     image in the [0, 1] range so we multiply the pixel values
#     first to get them into [0, 255].
#mean_file = os.path.join(CAFFE_ROOT, 'python/caffe/imagenet/ilsvrc_2012_mean.npy')
#mean = np.load(mean_file).mean(1).mean(1)
#img = img * 255 - mean[:, np.newaxis, np.newaxis]

pyplot.figure()
for i in range(3):
    # For some reason, pyplot subplot follows Matlab's indexing
    # convention (starting with 1). Well, we'll just follow it...
    pyplot.subplot(1, 3, i+1)
    pyplot.imshow(imgCropped[i], cmap=pyplot.cm.gray)
    pyplot.axis('off')
    pyplot.title('BGR channel %d' % (i+1))
# (4) Finally, since caffe2 expect the input to have a batch term
#     so we can feed in multiple images, we will simply prepend a
#     batch dimension of size 1. Also, we will make sure image is
#     of type np.float32.
imgCropped = imgCropped[np.newaxis, :, :, :].astype(np.float32)
print('Final input shape is:', imgCropped.shape)

在上面的輸出中,您應該注意這些變化:
HWC到CHW之前和之後的變化。 3,這是移動到開頭的顏色通道的數量。
在上面的圖片中,您可以看到顏色順序也已切換。 RGB成為BGR。 藍色和紅色切換位置。
最終的輸入形狀,意味著對影象的最後一次更改是將批處理欄位新增到開頭,所以現在你有(1,3,224,224)用於:

  • 1 image in the batch(一張圖片在批處理中), 
  • 3 color channels (in BGR)   3種顏色通道(BGR),
  • 224 height, 224高度。
  • 224 width.224寬度。