pytorch transform/tensor 基本操作

pytorch官方文檔
transform源碼解讀
莫煩pytorch

1 .transform

??torchvision.transforms是pytorch中的圖像預處理包
??有很多圖像預處理方法, 今天從圖像維度出發(fā), 講一些我們經(jīng)常會用到的

  • Resize, 把給定的圖片resize到target size
  • Normalize, Normalized an tensor image with mean and standard deviation
  • ToTensor, convert a PIL image to tensor (H, W, C) in range [0,255] to a torch.Tensor(C, H, W) in the range [0.0, 1.0]
  • ToPILImage, convert a tensor to PIL image
  • CenterCrop, 在圖片的中間區(qū)域進行crop
  • RandomCrop, 在一個隨機位置進行crop
  • FiveCrop, 把圖像裁剪為四個角和一個中心
  • TenCrop, five crop+flip 1->10

1.1 ToTensor & ToPILImage

??從Numpy到Tensor的轉(zhuǎn)換有兩種方法, 可以用torch.from_numpy(ndarray), 也可以用ToTensor()(ndarray), 這兩種有什么區(qū)別呢?

np_data = np.random.random((260, 194, 3))
# np_data size (height, width, channel)
torch_data = torch.from_numpy(np_data)  # numpy -> tensor 1
# torch_data size [260, 194, 3]
torch2array = torch_data.numpy()
# torch2array size[260, 194, 3]
tensor_data = ToTensor()(np_data)    # numpy -> tensor 2
#tensor_data size[3, 260, 194]
tensor2array = tensor_data.numpy()
#tensor2array size[3, 260, 194]

??可以看出, 如果用torch.from_numpy()的方法, 轉(zhuǎn)換的tensor和ndarray是相同的shape, (height, width, channel)的shape進, 仍然變成(height, width, channel)的shape
??但是, 如果是用ToTensor()的方式, 那就是 (height, width, channel)的shape進, (channel, height, width)的shape出
??不同于numpy->tensor有兩種做法, 從tensor->numpy卻只有一種, 就是tensor.numpy(), 而這種做法的shape前后也是不會變化的, 所以我們看到如果按照ToTensor() -> tensor.numpy()的方式將np轉(zhuǎn)成tensor再轉(zhuǎn)為np, 那shape和一開始的np是不同的, 就是(height, width, channel)會變成(channel, height, width)

??下面我們舉一個實際的讀圖轉(zhuǎn)換的例子

# PIL -> np -> tensor -> PIL
PIL_img = Image.open('test_pic.jpeg')
#PIL_img size [194, 260, 3], range [0,255]
np_img = np.asarray(PIL_img)
# np_img size [260, 194, 3], range [0,255]
tensor_from_np = ToTensor()(np_img)
# tensor_img size [3, 260, 194], range [0,1]
tensor_from_PIL = ToTensor()(PIL_img)
# tensor_img size [3, 260, 194], range [0,1]
PIL_from_tensor = ToPILImage()(tensor_from_np)
# PIL_from_tensor size [194, 260, 3], range[0,255]
PIL_from_tensor.save('img_from_tensor.bmp')

# tensor -> numpy -> PIL
np_from_tensor = tensor_from_np.numpy()
# np_from_tensor size [3, 260, 194], range [0, 1]
np_from_tensor *= 255
# np_from_tensor size [3, 260, 194], range [0, 255]
np_from_tensor = np.transpose(np_from_tensor, (1, 2, 0))
# np_from_tensor size [260, 194, 3], range [0, 255]
PIL_from_np = Image.fromarray(np.uint8(np_from_tensor))
# PIL_from_np size [194, 260, 3], range [0, 255]
PIL_from_np.save('img_from_np.bmp')

??為了說明PIL/np/tensor相互轉(zhuǎn)換之間產(chǎn)生的shape和range的變化, 我們這個例子稍微復雜了一點, 關(guān)鍵就是三點

  • 用ToTensor()轉(zhuǎn)換, 不管input的是PIL還是np, 只要是uint8格式的, 都會直接轉(zhuǎn)成[0,1]的float32格式, 所以我們看到的tensor_from_np和tensor_from_PIL是一樣的, 而ToPILImage是將tensor轉(zhuǎn)為PIL Image, 自動又會把float32的[0,1]轉(zhuǎn)回uint8
  • 如果是先把tensor轉(zhuǎn)為np, 再轉(zhuǎn)為PIL Image, 那就有上面我們提到的np_img和tensor_from_np.numpy()的shape和range都是不同的, 因為.numpy()的過程就是把tensor格式的數(shù)據(jù)復制到np格式的數(shù)據(jù), 重要的是, np_from_tensor和tensor_from_np共享了同一段內(nèi)存, 所以我們可以看到在更改np_from_tensor的值的時候, tensor_from_np也在變, 所以要進行[0,1]->[0,255] + transpose更改shape + float32->uint8的過程改為和np_img的shape和range都一樣, 這個時候再用Image.fromarray()才能還原成和PIL_img相同的圖像
  • ToPILImage除了可以做tensor->PIL, 還可以做np->PIL, 就是將shape為(C,H,W)的Tensor或shape為(H,W,C)的numpy.ndarray轉(zhuǎn)換成PIL.Image, 但注意, 輸入是np的話它不會把[0,1]float32自動轉(zhuǎn)成uint8, 所以用法就變成和Image.fromarray()一樣了
#before np_from_tensor
tensor_from_np2 = tensor_from_np.clone()
...
...
...
np_from_tensor2 = tensor_from_np2.numpy()
np_from_tensor2 *= 255
np_from_tensor2 = np.transpose(np_from_tensor2, (1, 2, 0))
PIL_from_np2 = ToPILImage()(np.uint8(np_from_tensor2))
PIL_from_np2.save('img_from_np2.bmp')

Note:
??這個np.transpose()函數(shù)很有用, opencv的圖像shape和np也不一樣, 也需要用這個函數(shù)來轉(zhuǎn)換維度
??上面提到.numpy()的做法是讓tensor和np.ndarray()共享內(nèi)存, 所以如果我們要用tensor原來的數(shù)據(jù), 那就要在對np_from_tensor處理之前, 先用.clone()的方式復制出一個副本來

1.2 Resize

torchvision.transforms.Resize(size, interpolation=2)
??這個resize的函數(shù)很簡單, 用的就是PIL圖像處理的內(nèi)核, 要注意的是, 這個size的格式是(height, width), 也就是說加入要用這個函數(shù)2倍downsample一個輸入PIL Image圖像, 應該是這樣

input_image = Image.open('image path')
Resize((input_image.size[1], input_image.size[0]))(input_image)
#or
Resize((input_image.height, input_image.width))(input_image)

??推薦用第二種形式, 比較清楚地體現(xiàn)PIL Image和tensor的維度差別

1.3 Crop

??torchvision.transforms里有很多crop的方法, 選幾個用的多的說一下

  • CenterCrop, 在圖片的中間區(qū)域進行crop
  • RandomCrop, 在一個隨機位置進行crop
  • FiveCrop, 把圖像裁剪為四個角和一個中心
  • TenCrop, 在FiveCrop的基礎(chǔ)上宫峦,再將輸入圖像進行水平或豎直翻轉(zhuǎn),然后再進行FiveCrop操作,這樣一張輸入圖像就能得到10張crop結(jié)果
Crop Description
CenterCrop(size) 將給定的PIL.Image進行中心切割耍群,得到給定的size伶授,size可以是tuple(target_height, target_width)擦耀。size也可以是一個Integer施符,在這種情況下,切出來的圖片的形狀是正方形慈迈。
RandomCrop(szie, padding=0) 切割中心點的位置隨機選取若贮。size可以是tuple也可以是Integer
FiveCrop(size) 把圖像裁剪為四個角和一個中心, 1張圖生成5張圖
TenCrop(size, vertical_flip=False) FiveCrop的基礎(chǔ)上,再將輸入圖像進行水平或豎直翻轉(zhuǎn)痒留,然后再進行FiveCrop操作谴麦,這樣一張輸入圖像就能得到10張crop結(jié)果
RandomSizedCrop(size, interpolation=2) 先將給定的PIL.Image隨機切,然后再resize成給定的size大小

1.4 Normalize

1.5 Compose

??transforms.Compose()就是把以上提到的多個步驟整合到一起, 按順序執(zhí)行

transforms.Compose([
transforms.CenterCrop(10),
transforms.ToTensor(),
])
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末伸头,一起剝皮案震驚了整個濱河市匾效,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恤磷,老刑警劉巖面哼,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異扫步,居然都是意外死亡魔策,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門河胎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闯袒,“玉大人,你說我怎么就攤上這事游岳≌遥” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵胚迫,是天一觀的道長喷户。 經(jīng)常有香客問我,道長访锻,這世上最難降的妖魔是什么褪尝? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮朗若,結(jié)果婚禮上恼五,老公的妹妹穿的比我還像新娘昌罩。我一直安慰自己哭懈,他們只是感情好,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布茎用。 她就那樣靜靜地躺著遣总,像睡著了一般睬罗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上旭斥,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天容达,我揣著相機與錄音,去河邊找鬼垂券。 笑死花盐,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的菇爪。 我是一名探鬼主播算芯,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼凳宙!你這毒婦竟也來了熙揍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤氏涩,失蹤者是張志新(化名)和其女友劉穎届囚,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體是尖,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡意系,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了饺汹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昔字。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖首繁,靈堂內(nèi)的尸體忽然破棺而出作郭,到底是詐尸還是另有隱情,我是刑警寧澤弦疮,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布夹攒,位于F島的核電站,受9級特大地震影響胁塞,放射性物質(zhì)發(fā)生泄漏咏尝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一啸罢、第九天 我趴在偏房一處隱蔽的房頂上張望箫津。 院中可真熱鬧,春花似錦庆尘、人聲如沸鳄梅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蕾总。三九已至粥航,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間生百,已是汗流浹背递雀。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蚀浆,地道東北人缀程。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像市俊,于是被迫代替她去往敵國和親杠输。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容