Caffe2 圖像預處理(Image Pre-Processing)[6]


學習如何使得圖像符合預訓練模型的需求,或者用其他數據集的圖像來測試自己的模型。
- 調整大小
- 縮放
- HWC和CHW,數據通道交換
- RGB和BGR,顏色通道的交換
- Caffe2的圖像預處理
Ipython Notebook的教程在這里獲取
在這一節中,我們將會展示如何從本地文件或網絡鏈接載入一個圖像,並能用於其他的教程和例子。當然,我們將繼續深入多種預處理,這些預處理都是使用Caffe2時非常有必要的的。

Mac OSx Prerequisites

首先,確保你有Python的這些模塊。

sudo pip install scikit-image scipy matplotlib

然后,我們開始載入這些模塊

%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.")

Test an Image

在下面的代碼塊中,用 IMAGE_LOCATION去載入你想要測試的圖像。改變其內容,並重新看看整個教程,你會看到對於不同的圖片格式會有不同的處理。如果你想嘗試自己的圖像,把它改為你的圖像路徑或者遠程URL。當你使用遠程URL時,必須確保這個URL指向一個普通的圖像文件類型和后綴,一些長的表示符或者字符串可能會導致程序中斷。


Color Issues

記住,如果你載入的圖像來自智能手機,那么你可能會遇到圖像顏色格式問題。在下面我們將會展示在RGB和BGR對一張圖像的影響。確保圖像數據和你想象中的一致。

Caffe Uses BGR Order

Caffe使用了OpenCV,而OpenCV處理圖像是Blue-Green-Red (BGR) 形式的。而不是通用的RGB形式,所以在Caffe2中,圖像的格式也是BGR。從長遠來看,這種做法在很多方面是有益的,當你使用不同的計算機和庫。但是這也是困惑的起源。

# 你可以載入本地圖片或者遠程連接

# 第一種方案,使用本地圖像
#IMAGE_LOCATION = 'images/cat.jpg'
# 第二種線路使用網絡圖像,圖像是一朵花
IMAGE_LOCATION = "https://cdn.pixabay.com/photo/2015/02/10/21/28/flower-631765_1280.jpg"
#第三種線路使用網絡圖像,網絡圖像有很多人
#IMAGE_LOCATION = "https://upload.wikimedia.org/wikipedia/commons/1/18/NASA_Astronaut_Group_15.jpg"
# 第四種使用一個網絡圖像,是一個豎圖
#IMAGE_LOCATION = "https://upload.wikimedia.org/wikipedia/commons/9/9a/Ducreux1.jpg"

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

# 顯示原始圖像
pyplot.figure()
pyplot.subplot(1,2,1)
pyplot.imshow(img)
pyplot.axis('on')
pyplot.title('Original image = RGB')

#交換顏色通道並顯示交換后的的BGR圖像
imgBGR = img[:, :, (2, 1, 0)]
pyplot.subplot(1,2,2)
pyplot.imshow(imgBGR)
pyplot.axis('on')
pyplot.title('OpenCV, Caffe2 = BGR')


由上面的例子中,你可以看到,不同的順序是相當重要的。接下來的代碼塊中,我們將會圖像轉換為BGR順序,這樣Caffe2才能正確處理它。
不,稍等。關於顏色還有些有趣的東西。

Caffe Prefers CHW Order

什么是CHW?還有HWC。這兩個都是來源於圖像處理。
- H:Height
- W:Width
- C:Channel
深入了解圖像在內存分配中的順序。你可能注意到,當我們第一次載入圖像時,我們進行了一些有趣的轉換。這些數據轉換就像把一幅圖像當做一個魔方來玩。我們看到的是魔方的頂層,操作下面的層,可以改變看到的東西。

在GPU下,Caffe2需要的圖像數據是CHW,在CPU下,一般需要的順序是HWC。基本上,你需要CHW的順序,並確保轉換為CHW這步包含在你的圖像預處理。把RGB轉換為BGR,然后把HWC轉換為CHW。這里的C就轉換后的BGR。你可能會問,為什么呢?原因在於,在GPU上使用cuDNN庫能獲得非常大的加速,而cuDNN只使用CHW。總的來說,這樣做能更快。

有了上面兩步,你可能會覺得夠了吧?不,你還是太年輕了。我們還需要resize(調整大小),crop(剪切),可能還需要些旋轉和鏡像。


Rotation and Mirroring

來自智能手機的相片普遍存在着旋轉或者鏡像,有時,我們可以通過照片中的EXIF信息進行修正。但是並不是都這么幸運。

Library for Handling Mobile Images

下面展示的是旋轉圖像和鏡像圖像

# 對於這樣的圖像如何知道它是豎屏模式?
ROTATED_IMAGE = "https://upload.wikimedia.org/wikipedia/commons/8/87/Cell_Phone_Tower_in_Ladakh_India_with_Buddhist_Prayer_Flags.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')

#這種圖像是給司機用后視鏡看的
MIRROR_IMAGE = "https://upload.wikimedia.org/wikipedia/commons/2/27/Mirror_image_sign_to_be_read_by_drivers_who_are_backing_up_-b.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')

旋轉
鏡像
然我們做一些變換。同時,這些技巧可能能夠幫到你,例如,你無法獲取圖像的EXIF信息,那么你可以對圖像進行旋轉,翻轉,從而產生很多副本,對於這些圖像,用你的模型全部跑一遍。當檢測的置信度足夠高時,找到了你需要的方向。
代碼如下:

#下面代碼實現圖像的左右翻轉
imgMirror = np.fliplr(imgMirror)
pyplot.figure()
pyplot.imshow(imgMirror)
pyplot.axis('off')
pyplot.title('Mirror image')

鏡像

#逆時針旋轉90度
imgRotated = np.rot90(imgRotated)
pyplot.figure()
pyplot.imshow(imgRotated)
pyplot.axis('off')
pyplot.title('Rotated image')

旋轉


Sizing

下面的例子先將圖像resize到256x256大小,然后從中剪切出224x224大小,因為網絡的輸入大小是224x224。

#模型的輸入是224x224大小,因此需要resize或者crop
# (1) Resize 圖像 256*256, 然后剪切中心部分
input_height, input_width = 224, 224
print("Model's input shape is %dx%d") % (input_height, input_width)
img256 = skimage.transform.resize(img, (256, 256))
pyplot.figure()
pyplot.imshow(img256)
pyplot.axis('on')
pyplot.title('Resized image to 256x256')
print("New image shape:" + str(img256.shape))

輸出

Model's input shape is 224x224
New image shape:(256, 256, 3)

resize
注意resize有可能在一定程度上扭曲圖像。你在測試時必須考慮這個問題,因為這會影響到你的模型輸出的結果。花和動物被拉長或者壓縮一點可能不會太大問題。但是面部特征就不一定行了。現在嘗試另一種縮放圖像的策略,並保持圖像的比例不變。

Rescaling

保持圖像的比例關系,並將最小的一邊縮放到和網絡的輸入大小一致。在我們的例子中,網絡的輸入是224x224。

  • 橫向(Landscape):限制高度進行resize
  • 縱向(Portrait):限制寬度進行resize
print("Original image shape:" + str(img.shape) + " and remember it should be in H, W, C!")
print("Model's input shape is %dx%d") % (input_height, input_width)
aspect = img.shape[1]/float(img.shape[0])#寬/高
print("Orginal aspect ratio: " + str(aspect))
if(aspect>1):
    # 橫向 - 寬圖像
    res = int(aspect * input_height)#譯者認為這里應該為input_width
    imgScaled = skimage.transform.resize(img, (input_width, res))#譯者認為這里應該為input_height
if(aspect<1):
    # 豎向 - 高圖像
    res = int(input_width/aspect)#譯者認為這里應該為input_height
    imgScaled = skimage.transform.resize(img, (res, input_height))#譯者認為這里應該為input_width
if(aspect == 1):
    imgScaled = skimage.transform.resize(img, (input_width, input_height))#譯者認為這里應該為 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")

輸出

Original image shape:(751, 1280, 3) and remember it should be in H, W, C!
Model's input shape is 224x224
Orginal aspect ratio: 1.70439414115
New image shape:(224, 381, 3) in HWC

image.png

Cropping

這里有很多策略可以使用。我們可以比例不變的將圖像縮小到一邊大小符合網絡的輸入,然后從圖像中間剪切出一塊。但是如果不進行縮放,可能只能剪切到圖像中花的一部分。所以我們還是需要縮放。
下面我們提供三種剪切的策略:

  1. 直接從圖像中間取出你需要的大小的patch
  2. resize到一個很接近網絡輸入大小的正方形,然后從中間抓取
  3. 保持圖像比例不變的縮放,然后從中間截取一部分
# 傻瓜式的從中間剪切
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)   #python中//表示取結果的整數
    starty = y//2-(cropy//2)    
    return img[starty:starty+cropy,startx:startx+cropx]

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

# 從256x256的變形圖像中剪切中間的224x224
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')
Original image shape:(751, 1280, 3) and remember it should be in H, W, C!

注意:內存上保存始終是H,W,C
圖像輸出:
三種剪切方法
看起來好像最后一個比較好。第二種方法也不差,不過,這和很難說,要在你的模型上進行大批量測試才知道。如果你的模型在訓練時使用不同比例的圖像,並且直接將他們壓縮到一個正方形,那么久而久之,你的模型將從壓縮圖像上學到那些物體被壓縮時的樣子,所以也能做出判斷。但是如果你的模型專注於細節,比如面部特征,特征點,或者一些非常細微的元素,那么圖像信息的丟失和變形將會帶來非常大的誤差。

更好的策略
更好的方法是,把你的圖像縮放到最接近真實數據,然后在圖像邊緣填補信息,填補的信息不能對你的模型產生影響,也就是你的模型會忽略掉這些信息。這個方法,我們會在另外一個教程中給出,因為,這個教程已經講了不少了。

Upscaling

如果你想要跑的圖像很小,怎么辦?在我們的例子中,我們網絡的輸入是224x224,但是如果遇到下面的128x128的圖像大小呢?

最常用的方法就是,用skimage的工具把一個小的正方形圖像變到一個大的正方形圖像。resize的默認參數是1,對應着使用雙線性插值。

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')
Original image shape:  (128, 128, 4)
Upscaled image shape:  (224, 224, 4)


看到沒,輸出是 (224, 224, 4)。等等,為什么是4?前面所有例子都是3。當我們使用一個png文件時,它是由四個通道的。第四個通道代表的是‘模糊度’或者‘透明度’.無論怎么樣,我們仍然能很好地處理它,不過,要留意這個通道數。現在讓我們先轉換成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
#交換坐標系HWC to CHW
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
try:
    pyplot.figure()
    pyplot.subplot(1, 2, 1)
    pyplot.imshow(imgTiny)#交換順序后無法顯示
    pyplot.axis('on')
    pyplot.title('128x128')
except:
    print "Here come bad things!"
    # 如果你想看到錯誤,反注釋掉下面一行
    #raise


什么都沒顯示,對吧,因為存儲順序調換了。但是通道數仍然是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')
Slice image shape:  (56, 128, 4)
Upscaled slice image shape:  (224, 224, 4)

通道數沒變。


這是一個非常嚴重的錯誤,例如正常的細胞都是接近圓形,而病變細胞則是鐮刀形的。在這種情況下,你怎么辦?這很依賴於你的模型和你的模型是如何訓練出來的。在某些情況下,可以通過給圖像填充白色或者黑色的,或者噪聲的邊緣解決這個問題。

下面我們繼續討論,我們已經說過BGR和CHW的問題了,但是在caffe2中還需要考慮一個就是batch term,也就是N,圖像的個數。

Final Preprocessing and the Batch Term
# 如果你想嘗試不同策略的剪切
# 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)HWC->CHW
imgCropped = imgCropped.swapaxes(1, 2).swapaxes(0, 1)
print "Image shape after HWC --> CHW conversion: ", imgCropped.shape

pyplot.figure()
for i in range(3):
    # pyplot  subplot 索引和MATLAB的一樣,從1開始
    pyplot.subplot(1, 3, i+1)
    pyplot.imshow(imgCropped[i])
    pyplot.axis('off')
    pyplot.title('RGB channel %d' % (i+1))

# (2) RGB->BGR
imgCropped = imgCropped[(2, 1, 0), :, :]
print "Image shape after BGR conversion: ", imgCropped.shape
# 以下代碼后面用到,現在沒用
# (3) 減均值,由於skimage 讀取的圖像在[0,1]之間,所以我們需要乘以255,使像素范圍回到[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):
    pyplot.subplot(1, 3, i+1)
    pyplot.imshow(imgCropped[i])
    pyplot.axis('off')
    pyplot.title('BGR channel %d' % (i+1))
# (4)最后由於Caffe2要求輸入要有一個batch ,所以我們可以一次傳遞多張圖像,我們僅僅讓batch size=1,還要保證數據類型是 np.float32
imgCropped = imgCropped[np.newaxis, :, :, :].astype(np.float32)
print 'Final input shape is:', imgCropped.shape

輸出:

Image shape before HWC --> CHW conversion:  (224, 224, 3)
Image shape after HWC --> CHW conversion:  (3, 224, 224)
Image shape after BGR conversion:  (3, 224, 224)
Final input shape is: (1, 3, 224, 224)

RGB
BGR
在上面的輸出中,你應該注意到如下變化:

  1. HWC->CHW,圖像的通道數3,由最后移到了前面
  2. RGB->BGR,藍色和紅色分量進行了交換
  3. 輸入數據的最后形狀,在前面添加了batch size。所以數據格式是(1, 3, 224, 224)
    • 1是圖像的個數
    • 3是圖像的通道數
    • 224是高
    • 224是寬

這一教程到此結束。轉載請注明出處:http://www.jianshu.com/c/cf07b31bb5f2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM