filename—queue = tf. train. string_input_producer(file tist, shuffte=True) tf. WhoteFileReader() reader value reader. read(filename_queue) tf. image. decode—jpeg(value, channels=channels) 1 mage lr_images ground_truths for i in range(batch—size) : tf. random—crop( cropped—image image, (input—size, input—size, channets)) lr_image = tf. image . resize—images( cropped—image' (input—size / / scate, input_size / / scate) , method=tf. image . ResizeMethod . BICUBIC) lr_image = tf. image . resize_images( lr_image, (input—size, input_size) , method=tf. image . ResizeMeth0d . BICUBIC) offset_teft (input_size ー output_size) / / 2 offset_top (input_size output—size) / / 2 ground—truth tf. image. crop—to—bounding—box( cropped—image, offset—top, offset—left, output—size, output_size) lr—images ・ append(lr—image) ground—truths . append(ground—truth) tf. stack(lr_images, axis=@) images tf. stack(ground—truths, axis=@) ground_truths return tr_images / 255 , ground_truths / 255 また、 evat. py でプロックに分割するときのスライド幅 (stride) を、 MODEL. INPUT_SIZE から MODEL. OUTPUT_SIZE に変更します ( リスト 3.6 ) 。 50 第 3 章超解像奮闘記
tr_images ground_truths for i in range(batch—size) : tf. random—crop( cropped—image image, (input—size, input—size, channels)) tf. image. resize_images( ground—truth cropped_image, (output—size' output—size)) tf. image. resize_images( resized image cropped—image' (input—size / / scale' input—size / / scate) , method=tf. image. ResizeMeth0d. BICUBIC) tf. image. resize_images( tr—image resized image, (input—size, input—size) , method=tf. image. ResizeMethod . BICUBIC) ground—truths. append(ground—truth) tr—images ・ append(lr—image) tf. stack(lr—images, axis=@) lr_images tf. stack(ground—truths, axis=@) ground_truths return tr_images / 255 , ground—truths / 255 ランダムで切り出した 33px の画像 (cropped-image) を縮小して高解像度の画像 「 ground-truth 」とします。また、 cropped-image を縮小してから、もう一度拡大する ことで、画像を低解像度の画像「 tr-image 」を作成しています。 48 第 3 章超解像奮闘記
1 . 学習データの読み込み 2 . 誤差関数の定義 3 . 最適化アルゴリズムの設定 4 . 学習の実行 # coding: UTF-8 リスト 2.3 : srcnn/image 」 oader_svs. py 像度の画像 (ground-truths) の組み合わせです。 戻り値は、 batch ー size で指定した数の学習に使う低解像度の画像 (tr-images) と、高解 ズ (input-size) 、モデルが出力する画像サイズ (output-size) を指定します。 引数に読み込む画像ファイルのリスト (jpeg-file-list) と、モデルへ入力する画像サイ リスト 2.3 の関数 load ー image は、画像の読み込み処理です。 学習データの読み込み from from from future future_ future_ import absolute_import import division import print—function import tensorflow as tf def load_image(file—list, input—size' output—size' channets=l' scaIe=2, batch—size=l) : with tf. name_scope( 'image—loader—svs' ) : 26 tf. train. string—input—producer(fite—list, filename_queue shuffte=True) reader = tf. Wh01eFiteReader() reader. read(filename—queue) value tf. image ・ decode—jpeg(value, channets=channets) nmage lr_images ground_truths for i in range(batch—size) : cropped—image = tf. random—crop( image, (input—size, input—size' channets)) tf. image . resize_images( ground—truth 第 2 章 CNN で超解像
def __train(file_list, patches_count, train_dir) : checkpoint—path = OS . path . join(train—dir, 'model . ckpt') math . ceil(patches—count / FLAGS . batch_size) step—of—epoch toad—image ( images, ground—truths fite list, MODEL . INPUT_SIZE, MODEL . OUTPUT_SIZE, channeIs=MODEL . CHANNELS, scaIe=FLAGS. scale, batch_size=FLAGS . batch—size / / 4 ) FLAGS. min_after_dequeue + 4 ☆ FLAGS. batch_size capacity lr—image—batch, ground—truth-batch = tf. train . shuffte—batch( [images' ground—truths] , batch size=FLAGS . batch_size, capacity=capacity, enqueue_many=True, min_after_dequeue=FLAGS. min_after_dequeue, num_threads=FLAGS . num_threads) MODEL . inference(tr_image—batch) loss(sr—images, ground—truth-batch) sr_nmages IOSS tf.VariabIe(), trainable=FaIse) gtobal—step init_optimizer(FLAGS. learning—rate) opt opt. minimize(toss, global—step=global—step) train_op config = tf. ConfigProto( at10w_soft_pIacement=True, Iog—device—ptacement=FLAGS. tog—device—ptacement) saver = tf. train . Saver(tf. global—variables()) tf. train . Coordinator() coord with tf. Session(config=config) as sess: tf. train . get_checkpoint—state(train_dir) checkpoint if not (checkpoint and checkpoint. model—checkpoint—path) : sess . run(tf. global—variables—initializer()) else: 30 第 2 章 CNN で超解像
cropped—image, (output_size, output_size) ) resized_image = tf. image. resize_nmages( cropped—image, (input—size / / scale, input_size / / scate) , method=tf. image . ResizeMethod . BICUBIC) lr—image tf. image. resize_images( resized_image, (input—size, input_size) , method=tf. image. ResizeMethod . BICUBIC) ground—truths. append(ground_truth) lr—images ・ append(lr—image) lr_images tf. stack(lr_images, axis=@) ground_truths tf. stack(ground—truths, axis=@) return lr_images / 255 , ground_truths / 255 ます、読み込んだファイルを tf. image. decode ー jpeg でデコードします 次に、指定したサイズでランダムで切り抜き (tf. random_crop) 、画像 cropped_image を指定のサイズ (output_size) に縮小します。これが ground_truth 、モデルの出力とし て期待する高解像度の画像となります。 また、 cropped-image を元に、解像度の低い画像 (tr_image) を作ります。一度 scale に指定された大きさに縮小 (resize) したあと、再び拡大することで擬似的に解像度を下げ ☆☆☆ 1 1 x 1 1 33X33 cropped—image 図関数 load_image の動作 ます。 CNN 33X33 lr—image ground—truth 21X21 第 2 章 CNN で超解像 27
MODEL . INPUT_SIZE, MODEL . OUTPUT_SIZE, channets=MODEL. CHANNELS, scaIe=FLAGS. scate, batch—size=FLAGS . batch—size / / 4 ) # 省略 図 4.6 は、表 4.3 の条件で学習した場合の誤差の変化です。カラー画像なので、学習に時間が かかると予想して 60 万ステップに設定しましたが、実際には、 10 万ステップ以降は大きな変化 がありませんでした。 表 4.3 学習条件 条件 画像の倍率 (scale) 学習率 (learning-rate) ニバッチサイズ (batch_size) ステップ数 (max_step) チャンネル数 (CHANNELS) 値 2 3 (RGB カラ 図 4.6 誤差の変イヒ (smoothing=O. 9) 7 ℃び」 e ・ 3 6 き 3 5 ℃ CCe -3 3 [ 0 」 e ・ 3 2.0C0 き 3 0 CC 6 : . Ck 5C0. 20C ℃ k 100. 400. 00k 0. 縦横 510PX のカラー画像を用意しました ( 図 4.7 ) 。 一平価用の画像として、 66 第 4 章 さまざまなモデル
リスト 3.3 は、第 1 層と第 3 層の畳み込み層にパディング (padding リスト 3.3 : /srcnn/model/mode1915_svs. py # coding: UTF—8 ' m0de1915 svs' NAME INPUT SIZE 33 OUTPUT SIZE INPUT_SIZE CHANNELS 1 def inference(lr—images) : を設定しています。 convl convl conv2 conv2 conv3 conv3 conv2d( 'convl' input—layer=lr—images, weights—shape=[9, 9 , CHANNELS, 64 ] , biases—shape=C32] , biases_vatue=@.@, weight—stddev=le-l, weights—shape=[l, 1 , 64 , 32 ] , input—tayer=convl, conv2d( 'conv2' tf. nn. retu(convl) padding='SAME' ) st ri des= [ 1 , 1 , 1 , 1 ] , biases—shape=[64] , biases_value=@ . @, weight—stddev=le—l, st ri des= [ 1 , 1 , 1 , 1 ] , padding='VAL 工 D') tf. nn . relu(conv2) conv2d( 'conv3' input—Iayer=conv2, weights—shape=[5, 5 , 32 , weight—stddev=1e-3, biases—shape=[CHANNELS] , st ri des= [ 1 , 1 , 1 , 1 ] , padding='SAME') tf. nn . retu(conv3) CHANNELS] , biases_value=@ . 0, return conv3 この場合、モデルの入力サイズ (INPUT_SIZE) と出力サイズ (OUTPUT_SIZE) は同じに なります。 第 3 章超解像奮闘記 45
tf. app. flags . FLAGS F LAGS tf. app . flags . DEFINE—string('hr_image—path' " 高解像度 ( 元画像 ) のパス " ) tf. app. flags . DEF 工 NE—string('Ir_image_path' tf. app. flags . DEFINE—string('sr_image_path' None, None, None, / 0 (psnr—bicubic, psnr—sr)) % .2fdb' print('PSNR(BICUBIC, (R): %. 2fdb hr—image, sr—image, dynamic—range=255) psnr_sr compare_psnr( hr image, lr—image, dynamic—range=255) psnr bicubic compare_psnr( ' サイズが異なる画像は評価できません ' sr_image. size) and lr_image. Size lr_image. size assert (hr_image. size sr_nmage imread(sr_file) lr image imread(lr_file) hr—image imread(hr_file) def compare(hr—file, tr—file, sr—file) : " 低解像度画像のパス " ) " 超解像画像のパス " ) 64 compare_ssim(hr_image, lr_image, win_size=3) ssim bicubic compare_ssim(hr_image, sr_image, wnn_size=3) S S 1 m S r def main(argv=None) : % (SSim bicubic, ssim_sr)) print('SSIM(BICUBIC, (R): % 4f % 4f assert OS . path. exists(FLAGS . sr—image—path) 96S IS not exist . , % FLAGS. tr_image_path assert OS . path . exists(FLAGS . tr—image—path) 96s is not exist. ' % FLAGS. hr image—path assert OS . path . exists(FLAGS. hr—image—path) FLAGS. sr_image—path 第 4 章さまざまなモデル FLAGS . sr_image_path) FLAGS . tr_image_path, compare(FLAGS . hr—image_path, 0 ′ 96S 1 S not exi St .
図 3.9 image 」 oader_svs. py の動作 33X33 cropped—image lr—image 33X33 1 1 x 1 1 0 → 0 CNN ground—truth 21X21 これまで見てきたように、超解像を実現するには周辺部の特徴を取り込む必要があります。 しかし、畳み込み層でパディングを設定してもプロックの境目にノイズが発生します。 そこで、学習時にパディングの領域を含めて読み込むように変更します。 conv2d で 0 パディングを追加するのでなく、失われるピクセル分、大きめに画像を読み込み ます ( 図 3.10 ) 。 図 3.10 変更後の動作 cropped—image 33X33 lr—image 33X33 ☆☆☆ CNN ground—truth 21X21 リスト 3.5 は、変更後のプログラムです。 リスト 3.5 : srcnn/image 」 oader_vvv. py def toad_image(file_list, input_size, output_size, scaIe=2, batch_size=l) : with tf. name_scope( 'image_loader vvv' ) : channels=l, 第 3 章超解像奮闘記 49
図 3.8 超解像処理をした画像 第第 1 かなり良くなりましたが、一定間隔で黒い点が表示されているのが気になります。 どうやら 処理するプロックの境界にノイズが出ているようです。 tf. nn . conv2d が追加するパディングは「 0 パディング ( = 黒のピクセル ) 」です 周辺領域 の特徴を多く取り込むために追加したパディングが、学習結果に影響を与えたと考えられます。 画像の読み込み処理 こでもう一度、第 2 章で作成した学習時の画像の読み込み処理 ( リスト 3.4 ) を見てみま しよう。 リスト 3.4 : s ℃ nn/image 」 oader_svs. py def load_image(file_list, input_size, output_size, channels=l, scaIe=2, batch_size=l) : with tf. name_scope( 'image_loader—svs' ) : filename—queue = tf. train. string_input_producer(fite_list, shuffte=True) tf. WholeFiIeReader() reader. read(filename_queue) reader value tf. image. decode—jpeg(vatue, channels=channels) 1 mage 第 3 章超解像奮闘記 47