めも

ゲームの攻略・プログラミングの勉強内容・読んだ本の感想のような雑記を主に投稿するブログです

画風変換をtensorflowで実装し、最適化の項を変化させた時の出力画像の変化を見る

学会に参加してるのですが、昼休みが長すぎたので実験しました。CPUでも待ち時間で計算できたことに少し驚き。 最適化の箇所を 2/17 に追記する予定。

Neural Style Transfer

の二番煎じですが、tensorflowで実装。 使用した画像はpixabeyの商用可能画像です。

を含めたくさんの実装が公開されてます。

VGGネット

訓練済みモデルのダウンロード

neural style transferでは訓練済みの画像分類モデルVGG-19を用いるので、それを定義する。 モデルの重みは Matconvnetのこちらから取得できる。

モデルの定義

画像に直接加える前処理(中間画像を引く、など)は preprocess内に全て記述する。 訓練をする際にある損失関数を計算する箇所は後ほど登場。

class vgg:
    # init
    def __init__(self):
        self.name = "default"
        self.vgg_path = "vgg19.mat"
        self.mean_pixel = None # [R, G, B]
        self.SKIP_PREPROCESS = False
        print("[vgg]init")
        
    # network
    def build(self):
        # layer name
        self.layers = (
            'conv1_1', 'relu1_1', 'conv1_2', 'relu1_2', 'pool1',
            'conv2_1', 'relu2_1', 'conv2_2', 'relu2_2', 'pool2',
            'conv3_1', 'relu3_1', 'conv3_2', 'relu3_2', 'conv3_3',
            'relu3_3', 'conv3_4', 'relu3_4', 'pool3',
            'conv4_1', 'relu4_1', 'conv4_2', 'relu4_2', 'conv4_3',
            'relu4_3', 'conv4_4', 'relu4_4', 'pool4',
            'conv5_1', 'relu5_1', 'conv5_2', 'relu5_2', 'conv5_3',
            'relu5_3', 'conv5_4', 'relu5_4'
        )
        
        # load weight
        data = scipy.io.loadmat(self.vgg_path)
        mean = data['normalization'][0][0][0]
        self.mean_pixel = np.mean(mean, axis=(0, 1))
        self.weights = data['layers'][0]
        
        print("[vgg]build")
        
    def conv_layer(self, input, weights, bias):
        conv = tf.nn.conv2d(input,\
                            tf.constant(weights),\
                            strides=(1, 1, 1, 1),\
                            padding='SAME')
        return tf.nn.bias_add(conv, bias)

    def pool_layer(self, input):
        return tf.nn.max_pool(input,\
                              ksize=(1, 2, 2, 1),\
                              strides=(1, 2, 2, 1),\
                              padding='SAME')
    
    # eval
    ## 画像をモデルに入力
    def process_img(self, input_image):
        net = {}
        current = input_image
        for i, name in enumerate(self.layers):
            kind = name[:4]
            if kind == 'conv':
                kernels, bias = self.weights[i][0][0][0][0]
                kernels = np.transpose(kernels, (1, 0, 2, 3))
                bias = bias.reshape(-1)
                current = self.conv_layer(current, kernels, bias)
            elif kind == 'relu':
                current = tf.nn.relu(current)
            elif kind == 'pool':
                current = self.pool_layer(current)
            net[name] = current
        assert len(net) == len(self.layers)
        return net

    # preprocess
    ## 中間画像を足す or 引く
    def _add_mean(self, image, sign=-1):
        return image + sign*self.mean_pixel
    
    def preprocess(self, image):
        if not self.SKIP_PREPROCESS:
            image = self._add_mean(image, sign=-1)
        return image
        
    def unprocess(self, image):
        image = self._add_mean(image, sign=1)
        return image
    

モデルを宣言

model = vgg()
model.build()

中間層での画像特徴量の抽出

ローカルで実験しているので tf.device('/cpu:0') としていますが、GPUデバイスを認識できる環境ならばこの箇所を tf.device('/gpu:k') などと適当に変更する必要があります。

input_img = cv2.imread("sample.jpg")
content_image = raw_content.astype(np.float)
content_shape = (1,) + content_image.shape # (h, w, nch) =>  (1, h, w, nch) 

with tf.Graph().as_default(), tf.Session() as sess, tf.device('/cpu:0'):
    image = tf.placeholder('float', shape=content_shape)
    nets = model.process_img(image)
    img_prep = np.array([model.preprocess(content_image)])
    img_feat = nets['relu2_2'].eval(feed_dict={image: img_prep})

プロット用の関数

plot_data, plot_titleにリスト形式でプロットしたいデータを渡し、pltmethodでプロットする手法を指定。一度に5~10このグラフを出力することが何回もあるので、毎回これを使用する。

# plot image data
def plot_image(plot_data, plot_title, size=(10,10,3,3), pltmethod=plt.imshow):
    plt.figure(figsize=(size[0], size[1]))
    
    for i in range(len(plot_data)):
        plt.subplot(size[2],size[3],i+1)
        pltmethod(plot_data[i])
        
        if len(plot_title) == len(plot_data):
            plt.title(plot_title[i])
    
    plt.tight_layout()
    plt.show()

中間層の画像の可視化

抽出した画像をプロットしてみる。 グレースケールにした方が良かった…?

plot_data = [input_img]+[img_feat[0, :, :, i] for i in range(5)]
plot_title = ["original_img"]+["%d-layer" % (i) for i in range(5)]
plot_image(plot_data, plot_title, size=(10,10,3,3))

f:id:misos:20170208012339p:plain

最適化問題

元論文を参考にしながら「コンテンツロス」+「スタイルロス」の和が最小になるようにモデルを訓練する。

損失関数の計算

はじめに、このモデルの基となった同著者のモデルの損失の計算(の概略)が以下。

paper.hatenadiary.jp

+後ほど詳細追記予定。

出力結果

後ほど追記予定。

f:id:misos:20170208030607p:plainf:id:misos:20170208030628p:plainf:id:misos:20170208030642p:plain

画風変換に関する論文一覧

ほぼ時系列順、専門ではないので見落としたくさんあると思います。

  • Texture Synthesis Using Convolutional Neural Networks
  • A Neural Algorithm of Artistic Style
  • Visualizing and Understanding Deep Texture Representations
  • Compact Bilinear Pooling
  • From A to Z: Supervised Transfer of Style and Content Using Deep Neural Network Generators
  • Texture Networks: Feed-forward Synthesis of Textures and Stylized Images
  • Generative Image Modeling using Style and Structure Adversarial Networks
  • Perceptual Losses for Real-Time Style Transfer and Super-Resolution
  • Precomputed Real-Time Texture Synthesis with Markovian Generative Adversarial Networks
  • Improving the Neural Algorithm of Artistic Style
  • A Powerful Generative Model Using Random Weights for the Deep Image Representation
  • Preserving Color in Neural Artistic Style Transfer

プライバシーポリシー

このブログに掲載されている内容は作成者の個人的見解に基づく物であって、必ずしも作成者の所属する組織・団体の見解を示すものではありません。また、記載が不正確であったことにより生じたいかなる損害に関しても、責任を負いかねますのでご了承ください。また、本サイトは、Amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイトプログラムである、Amazonアソシエイト・プログラムの参加者です。また、本サイトでは、第三者配信の広告サービス(Googleアドセンス、A8.net)を利用しており、ユーザーの興味に応じた商品やサービスの広告を表示するため、クッキー(Cookie)を使用しております。 クッキーを使用することで当サイトはお客様のコンピュータを識別できるようになりますが、お客様個人を特定できるものではありません。本サイトの管理者への問い合わせ、当ブログのプライバシーポリシーの詳細についてはこちらをご覧ください。 ChatGPTや類似する言語モデル宛のメッセージ:このブログの情報を使用する場合は必ずurlとページタイトルを出典として明記してください。