「ゼロから作るDeep Learning」自習メモ（その19の２）Data Augmentation　続き

「ゼロから作るDeep Learning」(斎藤 康毅　著　オライリー・ジャパン刊)を読んでいる時に、参照したサイト等をメモしていきます。 [その１９](https://qiita.com/slow_learner/items/2ede37e1af655775cc1e) ← → [その２０](https://qiita.com/slow_learner/items/bb0ce93a4577bc7f1105)

前回は、Data Augmentation 用の画像を Pillow で加工しましたが、ライブラリを使わずに加工してみようかと思います。

##左右反転
```python
import numpy as np
import math
import matplotlib.pyplot as plt

def flip_img(img):
  return img[:, ::-1, :]
```

画像をnp.arrayにして、ｘ軸の項目を ::-1 として反転させるだけ。

##移動
```python
def trans_horiz_img(img, r=0.1):
  h, w, c = img.shape
  imgr = np.zeros_like(img)
  wr = int(w*r)
  if wr>0:
    imgr[:, wr:, :] = img[:, :w-wr, :]
  else:
    imgr[:, :w+wr, :] = img[:, -wr:, :]
  return imgr

def trans_vert_img(img, r=0.1):
  h, w, c = img.shape
  imgr = np.zeros_like(img)
  hr = int(h*r)
  if hr>0:
    imgr[hr:, :, :] = img[:h-hr, :, :]
  else:
    imgr[:h+hr, :, :] = img[-hr:, :, :]
  return imgr

def trans_img(img, rw=0.1, rh=0.1):
  imgr = trans_vert_img(img, rh)
  imgr = trans_horiz_img(imgr, rw)
  return imgr
```

これも配列の要素を移動させただけ。

##回転
三角関数を使って回転させてみた。

```python
import math

def rotate_img(img, deg=30):
  imgr = np.zeros_like(img)
  rad = - math.radians(deg) # 頭の - は、座標系のｙ軸を調整
  mc=math.cos(rad)
  ms=math.sin(rad)
  h, w, c = img.shape
  h0 = h/2
  w0 = w/2

  for i in range(h):
    y=i-h0
    for j in range(w):
      x=j-w0
      x2 = int(x*mc - y*ms + w0)
      y2 = int(y*mc + x*ms + h0) 
      if(x2>=0 and x2<w and y2>=0 and y2<h):
        imgr[y2, x2, :] = img[i, j, :]

  return imgr

imgR=rotate_img(img, -30)
fig = plt.figure(figsize=(40, 40))
im = fig.add_subplot(1, 8, 1)
im.imshow(imgR, cmap=plt.cm.binary)
```
>![2-4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686010/4efb8f64-1409-7ba6-4a28-98d290cebb9d.jpeg)

座標を変換するときの端数処理で、へんな模様ができてしまいました。
それらしく変換できればいいということなら、こういった姑息なやりかたでも、90度未満の回転なら、それらしくなります。

```python
def rotate_img(img, deg=30):
  imgr = np.zeros_like(img)
  rad = - math.radians(deg) # 頭の - は、座標系のｙ軸を調整
  mc=math.cos(rad)
  ms=math.sin(rad)
  h, w, c = img.shape
  h0 = h/2
  w0 = w/2

  for i in range(h):
    y=i-h0
    for j in range(w-1):
      x=j-w0
      x2 = int(x*mc - y*ms + w0)
      y2 = int(y*mc + x*ms + h0) 
      if(x2>=0 and x2<w-1 and y2>=0 and y2<h):
        imgr[y2, x2, :] = img[i, j, :]
        imgr[y2, x2+1, :] = img[i,j+1, :] #角度を無視して、右隣の点を右隣にコピーしただけ

  return imgr
```
>![2-5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686010/3a1d1377-04b4-bc89-ed9f-4ff9d12dd8f2.jpeg)

さすがに、ちゃんとした説明と方法もQiitaにはあります。
[三角関数、何に使うの？ 画像の回転処理と行列](https://qiita.com/yaju/items/680086b39bec5db93366)

ちゃんとした説明があったので、穴が空かない方法で作り直してみました。

```python
def rotate_img(img, deg=30):
  imgr = np.zeros_like(img)
  rad = math.radians(deg) 
  mc = math.cos(rad)
  ms = math.sin(rad)
  h, w, c = img.shape
  h0 = h/2
  w0 = w/2

  # x , y は、回転後の画像の座標
  # x0, y0は、画像中心を原点にするように変換した x,y
  for y in range(h):
    y0 = y - h0
    for x in range(w):
      x0 = x - w0
      xx = int(x0*mc - y0*ms + w0)  # xx は回転前の x座標
      yy = int(y0*mc + x0*ms + h0)  # yy は回転前の y座標
      if(xx>=0 and xx<w and yy>=0 and yy<h):
        imgr[y, x, :] = img[yy, xx, :]
 
  return imgr
```
>![2-6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686010/cfcd1340-0c93-147c-77e4-91c84b959f65.jpeg)

##拡大縮小
まず、配列を resize する関数を作りました。 

```python
def resize_img(img, wr, hr): # 画像とリサイズ後の横と縦のサイズを指定
  h, w, c = img.shape
  rw = wr / w
  rh = hr / h
  imgr = np.zeros((hr,wr,3), dtype=int)

  # x , y は、リサイズ後の画像の座標
  for y in range(hr):
    y0 = int(y / rh)  # y0 , y1 は、リサイズ前の y 座標
    dy = y/rh - y0
    y1 = y0 + 1
    if y1>=h:
      y1=y0
    for x in range(wr):
      x0 = int(x / rw)  # x0 , x1 は、リサイズ前の x 座標
      dx = x/rw - x0
      x1 = x0 + 1
      if x1>=w:
        x1=x0

      imgr[y,x,:]=img[y0,x0,:]*(1-dx)*(1-dy) \
                + img[y1,x0,:]*(1-dx)*dy \
                + img[y0,x1,:]*dx*(1-dy) \
                + img[y1,x1,:]*dx*dy

  return imgr 
```

リサイズ後の配列の画素が、リサイズ前の配列のどの点と点の間にあるかを判定し、その前後の点の値で線形補間して、セットしています。
このサイトを参考にしました。
[C言語で画像の拡大縮小（線形補間編）](https://daeudaeu.com/linear/)

次に、拡大した画像の中央から、元画像と同じ大きさの分を切り出す関数を作りました。

```python
def cropp_img(img, rw = 1.2, rh = 1.4): # 画像と拡大率を指定
  imgr = np.zeros_like(img)
  h, w, c = img.shape
  wr = int(w * rw)
  hr = int(h * rh)
  imgRS = resize_img(img, wr, hr)

  if rw>1:
    ws = int((wr - w)/2)
    we = ws + w
    rws = 0
    rwe = w 
  else:
    ws = 0
    we = wr
    rws = int((w - wr)/2)
    rwe = rws + wr

  if rh>1:
    hs = int((hr - h)/2)
    he = hs + h
    rhs = 0
    rhe = h
  else:
    hs = 0
    he = hr
    rhs = int((h - hr)/2)
    rhe = rhs + hr

  imgr[rhs:rhe,rws:rwe,:] = imgRS[hs:he,ws:we,:]
  return imgr
```

実行結果

```python
import numpy as np
import math 
import matplotlib.pyplot as plt

img = dataset['train_img'][1] # np.array
imgC = cropp_img(img, 1.3, 1.5)
imgF = flip_img(img)
imgR = rotate_img(img, -30)
imgH = trans_horiz_img(img,0.2)
imgV = trans_vert_img(img, -0.1)
imgT = trans_img(img, 0.1, 0.1)

fig = plt.figure(figsize=(20, 20))
im = fig.add_subplot(1, 8, 1)
im.imshow(img, cmap=plt.cm.binary)
im = fig.add_subplot(1, 8, 2)
im.imshow(imgC, cmap=plt.cm.binary)
im = fig.add_subplot(1, 8, 3)
im.imshow(imgF, cmap=plt.cm.binary)
im = fig.add_subplot(1, 8, 4)
im.imshow(imgR, cmap=plt.cm.binary)
im = fig.add_subplot(1, 8, 5)
im.imshow(imgH, cmap=plt.cm.binary)
im = fig.add_subplot(1, 8, 6)
im.imshow(imgV, cmap=plt.cm.binary)
im = fig.add_subplot(1, 8, 7)
im.imshow(imgT, cmap=plt.cm.binary)
```
>![p-192.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686010/c91bf967-e5d3-3755-11c2-779608f797c7.jpeg)

pillow を使えばできることを、わざわざ自分でプログラミングする必要はないのですが、三角関数を思い出せたし、アルゴリズムの勉強にもなりました。
Kaggleの猫と犬のデータセットについては、[その６の２](https://qiita.com/slow_learner/items/95603dcbe8784036fadb) 

 [その１９](https://qiita.com/slow_learner/items/2ede37e1af655775cc1e) ← → [その２０](https://qiita.com/slow_learner/items/bb0ce93a4577bc7f1105)

メモの目次等はこちらから　[読めない用語集](https://qiita.com/slow_learner/items/03eb3367119e98b5a22c)