コードを書いていて目にする機会の多いリサイズ。あまり深く考えずに使ってきましたので、調べなおしてみました。
PyTorchでリサイズするのには、interpolateもしくはUpsampleが使えます。interpolateはtorch.nn.functionalに、Upsampleはtorch.nnに入っています。
まずは、interpolateから使い方と補間モードについてまとめていくことにします。
構文
interpolateは下のような構文で記載します。sizeとscale_factorはいずれも出力画像サイズを示すのに使用し、どちらか一方のみを記載します。両方記載されていた場合はエラーを返します。
- input: 入力テンソル
- size: 出力サイズ(タプル)
- scale_factor: 拡大(縮小)率
- mode: 補間モード
F.interpolate(input, size=None, scale_factor=None, mode='nearest')
modeは以下から選択します。4階テンソルで指定できるのは※のついたものになります。
- nearest※
- linear
- bilinear※
- bicubic※
- trilinear
- area※
動作確認
データの準備
まずは動作確認に使うテンソルを作成します。インクリメントな値を格納した1x1x4x4のテンソルを準備しました。
x = torch.arange(0, 16, dtype=torch.float32).view(-1, 1, 4, 4) # tensor([[[[ 0., 1., 2., 3.], # [ 4., 5., 6., 7.], # [ 8., 9., 10., 11.], # [12., 13., 14., 15.]]]])
アップサンプリング
まずは2倍サイズに拡大してみます。
- nearest
scaled_x = F.interpolate(x, size=None, scale_factor=2, mode='nearest') # tensor([[[[ 0., 0., 1., 1., 2., 2., 3., 3.], # [ 0., 0., 1., 1., 2., 2., 3., 3.], # [ 4., 4., 5., 5., 6., 6., 7., 7.], # [ 4., 4., 5., 5., 6., 6., 7., 7.], # [ 8., 8., 9., 9., 10., 10., 11., 11.], # [ 8., 8., 9., 9., 10., 10., 11., 11.], # [12., 12., 13., 13., 14., 14., 15., 15.], # [12., 12., 13., 13., 14., 14., 15., 15.]]]])
- bilinear
scaled_x = F.interpolate(x, size=None, scale_factor=2, mode='bilinear') # tensor([[[[ 0.0000, 0.2500, 0.7500, 1.2500, 1.7500, 2.2500, 2.7500, # 3.0000], # [ 1.0000, 1.2500, 1.7500, 2.2500, 2.7500, 3.2500, 3.7500, # 4.0000], # [ 3.0000, 3.2500, 3.7500, 4.2500, 4.7500, 5.2500, 5.7500, # 6.0000], # [ 5.0000, 5.2500, 5.7500, 6.2500, 6.7500, 7.2500, 7.7500, # 8.0000], # [ 7.0000, 7.2500, 7.7500, 8.2500, 8.7500, 9.2500, 9.7500, # 10.0000], # [ 9.0000, 9.2500, 9.7500, 10.2500, 10.7500, 11.2500, 11.7500, # 12.0000], # [11.0000, 11.2500, 11.7500, 12.2500, 12.7500, 13.2500, 13.7500, # 14.0000], # [12.0000, 12.2500, 12.7500, 13.2500, 13.7500, 14.2500, 14.7500, # 15.0000]]]])
- bicubic
scaled_x = F.interpolate(x, size=None, scale_factor=2, mode='bicubic') # tensor([[[[-0.5273, -0.2305, 0.2461, 0.8750, 1.2812, 1.9102, 2.3867, # 2.6836], # [ 0.6602, 0.9570, 1.4336, 2.0625, 2.4688, 3.0977, 3.5742, # 3.8711], # [ 2.5664, 2.8633, 3.3398, 3.9688, 4.3750, 5.0039, 5.4805, # 5.7773], # [ 5.0820, 5.3789, 5.8555, 6.4844, 6.8906, 7.5195, 7.9961, # 8.2930], # [ 6.7070, 7.0039, 7.4805, 8.1094, 8.5156, 9.1445, 9.6211, # 9.9180], # [ 9.2227, 9.5195, 9.9961, 10.6250, 11.0312, 11.6602, 12.1367, # 12.4336], # [11.1289, 11.4258, 11.9023, 12.5312, 12.9375, 13.5664, 14.0430, # 14.3398], # [12.3164, 12.6133, 13.0898, 13.7188, 14.1250, 14.7539, 15.2305, # 15.5273]]]])
- area
アップサンプリングの場合、areaの結果はnearestと変わらない感じです。
scaled_x = F.interpolate(x, size=None, scale_factor=2, mode='area') # tensor([[[[ 0., 0., 1., 1., 2., 2., 3., 3.], # [ 0., 0., 1., 1., 2., 2., 3., 3.], # [ 4., 4., 5., 5., 6., 6., 7., 7.], # [ 4., 4., 5., 5., 6., 6., 7., 7.], # [ 8., 8., 9., 9., 10., 10., 11., 11.], # [ 8., 8., 9., 9., 10., 10., 11., 11.], # [12., 12., 13., 13., 14., 14., 15., 15.], # [12., 12., 13., 13., 14., 14., 15., 15.]]]])
ダウンサンプリング
- nearest
scaled_x = F.interpolate(x, size=None, scale_factor=1/2, mode='nearest') # tensor([[[[ 0., 2.], # [ 8., 10.]]]])
- bilinear
scaled_x = F.interpolate(x, size=None, scale_factor=1/2, mode='bilinear') # tensor([[[[ 2.5000, 4.5000], # [10.5000, 12.5000]]]])
- bicubic
scaled_x = F.interpolate(x, size=None, scale_factor=1/2, mode='bicubic') # tensor([[[[ 2.0312, 4.2188], # [10.7812, 12.9688]]]])
- area
scaled_x = F.interpolate(x, size=None, scale_factor=1/2, mode='area') # tensor([[[[ 2.5000, 4.5000], # [10.5000, 12.5000]]]])
areaとbilinear
ダウンサンプリングの場合、areaとbilinearの結果が変わっているように見えなかったので、サイズを少し変更して結果を確認してみました。
こうしてみると、けっこう値が変わっています。mode=’area’はモアレを抑制することができるみたいで、そのためでしょうか、それぞれの値の分布がbilinearよりも狭い範囲に収まっています。
具体的なareaの処理の中身についてはまだ理解できていませんのでどこかでまたがんばります。
x = torch.arange(0, 100, dtype=torch.float32).view(-1, 1, int(10, 10)) # tensor([[[[ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.], # [10., 11., 12., 13., 14., 15., 16., 17., 18., 19.], # [20., 21., 22., 23., 24., 25., 26., 27., 28., 29.], # [30., 31., 32., 33., 34., 35., 36., 37., 38., 39.], # [40., 41., 42., 43., 44., 45., 46., 47., 48., 49.], # [50., 51., 52., 53., 54., 55., 56., 57., 58., 59.], # [60., 61., 62., 63., 64., 65., 66., 67., 68., 69.], # [70., 71., 72., 73., 74., 75., 76., 77., 78., 79.], # [80., 81., 82., 83., 84., 85., 86., 87., 88., 89.], # [90., 91., 92., 93., 94., 95., 96., 97., 98., 99.]]]])
- bilinear
scaled_x = F.interpolate(x, size=(3, 3), mode='bilinear') # tensor([[[[12.8333, 16.1667, 19.5000], # [46.1667, 49.5000, 52.8333], # [79.5000, 82.8333, 86.1667]]]])
- area
scaled_x = F.interpolate(x, size=(3, 3), mode='area') # tensor([[[[16.5000, 19.5000, 22.5000], # [46.5000, 49.5000, 52.5000], # [76.5000, 79.5000, 82.5000]]]])