読者です 読者をやめる 読者になる 読者になる

アラカン"BOKU"のITな日常

人事評価と人工知能について考えたことがメインテーマです。

MNIST(手書き数字)データを変形させて、バリエーションを作ってみた

プログラミング わき道(ひまつぶし)

MNISTの手書き数字画像データを、ずらしたり回転させて、バリエーション画像を作ってみました。

 

ディープラーニング実装の正解率をあげるアプローチで必要になって作ったのですが、pythonで数字を入れ替えるだけで、画像が加工できるというのは、ちょっとおもしろかったです。

 

まず、処理結果のイメージです。

f:id:arakan_no_boku:20161203164822j:plain

左上がオリジナルで、あと上、下、右(上段)、段左から 左、90度回転、180度回転、270度回転した画像です。

 

上下左右には、それぞれ4バイトずつずらした結果になってます。数字の形を維持しつつ、微妙にずれているのがわかると思います。

 

扱うMNISTデータの特徴

 MNISTは上記のように手書き数字画像ですが、実は、pythonではNumpy配列に変換して使います。

 

28×28なので、784バイトの数字の配列になります。

 

[ 0 0 0 0 255 255 0 0 128 0 .........]みたいな感じですね。

 

なので、上下左右にずらしたり、回転させると言っても、実態としては配列データの入れ替えを行うことになります。

 

言葉ではイメージしずらいので、図で説明してみます。見やすくするために、4×4の小さなデータにしました。

f:id:arakan_no_boku:20161203162856j:plain

 

2つの図が1セットになっていて、一番左上が元画像データ、その下の右左下上の文字をはさんだ4セット分がずらすパターン、右側の90度、180度、270度の3セットが回転パターンです。

 

1セットのうちの左側の0~15の数字は、配列の添字です。

 

右側の白抜きの0の部分が画像イメージで黒背景の部分、色をつけて10(この数字に意味はありません)となっているのが、数字部分だと思ってください。

 

Pとあるのはパディングです。上下左右にずらした分穴があいた部分にはとりあえず0を詰める必要があるので、そのことをPとしてます。

 

加工の方法

上の図を見てもらえば、すべて、元の添字をもとにして移動先の添字を求めて、そこに値をセットする処理を行えば良いだけだということがわかると思います。

 

例えば、右にNバイトずらす場合だと、自分の添字にNを足した添字の場所に値をセットすれば良いわけです。

 

ただ、注意すべきは論理上の横幅がありますので、Nを足した結果を、横幅で割った余りをとって、横幅-N より小さければはみ出しているのでセットしない、でなければセットするという考慮が必要です。

 

左や上・下もほぼ同じように実装できます。

 

回転は、やや複雑ですが、それでも規則性はあります。

 

2つの変数 i と j を用意してやって、以下の図のように変化させれば、「横幅 * i + j」という同じ式でセットすべき添字を求めることができます。

f:id:arakan_no_boku:20161203170110j:plain

あとは、この横幅を28に拡張してやればよいわけですね。

 

最後にソースの抜粋です

ブログの引用に貼り付けたときにおそらくインデントが壊れているので、そのままコピペしても、pythonでインデントエラーとかになる可能性は高いと思います。

 

なので、試される場合は、そのあたりの調整はご自分でお願いします。

 

なお、メソッド名を見ればわかると思い、引用部にコメントはしてません。

 

また、以下のソースは配列データの変換部分だけです。(変換後のNumpy配列を画像データ(pngファイル)に変換する部分は今回のテーマではないのではぶいてます。ご注意ください)

import numpy as np

def rotate90(x,w=28):

    size = len(x)
    outwk = [0]*size
    b = 0
    for j in range(0,w):
        for i in reversed(range(0,w)):
            outb = w * i + j
            outwk[outb] = x[b]
            b = b + 1
    out = np.array(outwk)
    return out


def rotate180(x,w=28):
    size = len(x)
    outwk = [0]*size
    b = 0
    for i in reversed(range(0,w)):
         for j in reversed(range(0,w)):
              outb = w * i + j
              outwk[outb] = x[b]
              b = b + 1
    out = np.array(outwk)
    return out

 

def rotate270(x,w=28):
     size = len(x)
     outwk = [0]*size
     b = 0
     for j in reversed(range(0,w)):
          for i in range(0,w):
               outb = w * i + j
               outwk[outb] = x[b]
               b = b + 1
     out = np.array(outwk)
     return out


def right(x,n,w=28):
     size = len(x)
     outwk = [0]*size
     b = 0
     for b in range(0,size):
          a = b % w
          if( a < (w - n)):
               outb = b + n
               outwk[outb] = x[b]
     out = np.array(outwk)
     return out


def left(x,n,w=28):
     size = len(x)
     outwk = [0]*size
     b = 0
     for b in range(0,size):
          a = b % w
          if( a >= n):
               outb = b - n
               outwk[outb] = x[b]
     out = np.array(outwk)
     return out


def upto(x,n,w=28):
     size = len(x)
     outwk = [0]*size
     b = 0
     for b in range(0,size):
          if( b >= (w * n)):
               outb = b - (w * n)
               outwk[outb] = x[b]
     out = np.array(outwk)
     return out


def downto(x,n,w=28):
     size = len(x)
     outwk = [0]*size
     b = 0
     for b in range(0,size):
           if( b <= (size - 1 - (w * n))):
                outb = b + (w * n)
                outwk[outb] = x[b]
     out = np.array(outwk)
     return out