コンピュータビジョンにおけるアフィン変換

## コンピュータビジョンにおけるアフィン変換

### 1. 同次座標の紹介

コンピュータビジョンにおいて、幾何学的変換を効率的に扱うために**同次座標（Homogenous Coordinate）** が用いられます。同次座標は、デカルト座標に1つの次元を追加したもので、2Dの点 **(x, y)** は同次座標系では **(x, y, 1)** として表現されます。これにより、平行移動、回転、拡大縮小などの変換を行列の積として統一的に扱うことができます。

### 2. アフィン変換の紹介

アフィン変換（Affine Transformation）は、幾何学的変換の一種であり、次のような操作を含みます：

#### 平行移動

平行移動は、点 \((x, y)\) を定数ベクトル \((t_x, t_y)\) だけ移動させる変換です。同次座標系では次の行列で表されます：


```math
\begin{pmatrix}
1 & 0 & t_x \\
0 & 1 & t_y \\
0 & 0 & 1
\end{pmatrix}

```

#### 回転

回転は、点 \((x, y)\) を原点を中心に角度 \(\theta\) だけ回転させる変換です。同次座標系では次の行列で表されます：

```math
\begin{pmatrix}
\cos\theta & -\sin\theta & 0 \\
\sin\theta & \cos\theta & 0 \\
0 & 0 & 1
\end{pmatrix}
```
#### 拡大縮小

拡大縮小は、点 **(x, y)** をx方向に **s_x**、y方向に **s_y** で拡大縮小する変換です。同次座標系では次の行列で表されます：

```math
\begin{pmatrix}
s_x & 0 & 0 \\
0 & s_y & 0 \\
0 & 0 & 1
\end{pmatrix}
```

### 3. アフィン変換の特徴

アフィン変換には以下の特徴があります：

1. **線形性**：アフィン変換は線形変換（行列操作）と平行移動を組み合わせたものであり、線形性を持っています。
2. **平行性の保持**：アフィン変換では、元の図形の平行な直線は変換後も平行を保ちます。
3. **形状の保全**：直線や平行四辺形などの基本的な形状が変換後も保持されますが、距離や角度は変わることがあります。
4. **統一的な表現**：同次座標系を用いることで、複数の変換を1つの行列に統一して表現でき、計算が簡単になります。

### 4. OpenCV Pythonでのアフィン変換の使い方

OpenCVは、Pythonでアフィン変換を簡単に実装するための強力なツールを提供しています。以下に、画像に対して平行移動、回転、拡大縮小を適用する例を示します。

#### OpenCVの`cv2.warpAffine`関数の使い方

`cv2.warpAffine`は、画像に対してアフィン変換を適用するための関数です。この関数を使用すると、平行移動、回転、拡大縮小、剪断などのアフィン変換を簡単に行うことができます。この記事では、`cv2.warpAffine`の引数とその準備過程について詳しく説明します。

#### `cv2.warpAffine`の基本的な使い方

`cv2.warpAffine`関数は、以下のように使用します：

```python
dst = cv2.warpAffine(src, M, dsize, flags=interpolation, borderMode=border_mode, borderValue=border_value)
```

#### 引数の説明

1. **src**: 入力画像
   - 変換を適用する元の画像です。これは`cv2.imread`で読み込んだ画像や、他の処理によって生成された画像です。

2. **M**: 2x3の変換行列
   - アフィン変換を定義する2x3の行列です。この行列は、平行移動、回転、拡大縮小などの変換を組み合わせたものです。

3. **dsize**: 出力画像のサイズ
   - 出力画像のサイズを指定します。これはタプル形式で指定し、(width, height) のようになります。

4. **flags**: 補間方法（オプション）
   - 補間方法を指定します。主なオプションには以下があります：
     - `cv2.INTER_LINEAR`：線形補間（デフォルト）
     - `cv2.INTER_NEAREST`：最近傍補間
     - `cv2.INTER_CUBIC`：バイキュービック補間
     - `cv2.INTER_LANCZOS4`：Lanczos補間

5. **borderMode**: 境界モード（オプション）
   - 境界モードを指定します。主なオプションには以下があります：
     - `cv2.BORDER_CONSTANT`：定数境界（デフォルト）
     - `cv2.BORDER_REPLICATE`：最も近い境界ピクセルを複製
     - `cv2.BORDER_REFLECT`：境界を反射
     - `cv2.BORDER_WRAP`：境界をラップ

6. **borderValue**: 境界値（オプション）
   - `borderMode`が`cv2.BORDER_CONSTANT`の場合に使用される境界の値を指定します。デフォルトは0です。

#### 必要なライブラリのインポート

```python
import cv2
import numpy as np
```

#### 画像の読み込み

```python
# 画像を読み込む
image = cv2.imread('example.jpg')
```

![tokyo-sample.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/cbd08b95-f7e1-0814-401f-8d3cbf6cc1e4.jpeg)



#### 平行移動

```python
# 平行移動の行列を定義する
tx, ty = 50, 30  # x方向に50ピクセル、y方向に30ピクセル移動
translation_matrix = np.float32([[1, 0, tx], [0, 1, ty]])

# 平行移動を適用する
translated_image = cv2.warpAffine(image, translation_matrix, (image.shape[1], image.shape[0]))
```

![translated.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/ed32e04e-7be7-a3ff-0af3-bbc6c68fd119.png)

#### 回転

```python
# 回転の行列を定義する
center = (translated_image.shape[1] // 2, translated_image.shape[0] // 2)  # 画像の中心を回転の中心にする
angle = 45  # 回転角度45度
scale = 1.0  # スケールは1（変わらない）
rotation_matrix = cv2.getRotationMatrix2D(center, angle, scale)

# 回転を適用する
rotated_image = cv2.warpAffine(image, rotation_matrix, (translated_image.shape[1], translated_image.shape[0]))
```

![Rotated.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/68e8d5f3-418b-e6ff-c190-041f7f68a17a.png)


#### 拡大縮小

```python
# 拡大縮小の行列を定義する
sx, sy = 1.5, 1.5  # x方向に1.5倍、y方向に1.5倍に拡大
scaling_matrix = np.float32([[sx, 0, 0], [0, sy, 0]])

# 拡大縮小を適用する
scaled_image = cv2.warpAffine(image, scaling_matrix, (int(rotated_image.shape[1] * sx), int(rotated_image.shape[0] * sy)))
```

![Scaled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/1a1e388b-e9ec-f595-dc2a-402830e3746c.png)
**Webページの表示でオリジナル画像と同じく見えるかもしれませんが、拡大されています**



## 全体コード

このコードは、指定された順序で画像に対して平行移動、回転、および拡大縮小を適用し、それぞれの結果を表示します。`cv2.warpAffine`関数を使用することで、簡単に画像に対してさまざまなアフィン変換を適用できます。

```python
import cv2
import numpy as np


image = cv2.imread('./Images/tokyo-sample.jpg')

# Define the Transition Matrix
tx, ty = 50, 30
translation_matrix = np.float32([[1, 0, tx], [0, 1, ty]])

# Apply the Transition
translated_image = cv2.warpAffine(image, translation_matrix, (image.shape[1], image.shape[0]))

# Define the Rotation Matrix
center = (translated_image.shape[1] // 2, translated_image.shape[0] // 2)
angle = 45
scale = 1.0
rotation_matrix = cv2.getRotationMatrix2D(center, angle, scale)

# Apply the Rotation
rotated_image = cv2.warpAffine(image, rotation_matrix, (translated_image.shape[1], translated_image.shape[0]))

# Define the Scale up and down
sx, sy = 1.5, 1.5
scaling_matrix = np.float32([[sx, 0, 0], [0, sy, 0]])

# Apply the Scale up and down
scaled_image = cv2.warpAffine(image, scaling_matrix, (int(rotated_image.shape[1] * sx), int(rotated_image.shape[0] * sy)))

# Showing the result
cv2.imshow('Original Image', image)
cv2.imshow('Translated Image', translated_image)
cv2.imshow('Rotated Image', rotated_image)
cv2.imshow('Scaled Image', scaled_image)

cv2.imwrite("./Images/translated.png", translated_image)
cv2.imwrite("./Images/Rotated.png", rotated_image)
cv2.imwrite("./Images/Scaled.png", scaled_image)

cv2.waitKey(0)
cv2.destroyAllWindows()

```



### まとめ

アフィン変換は、コンピュータビジョンや画像処理において非常に重要なツールです。同次座標系を利用することで、平行移動、回転、拡大縮小などの幾何学的変換を簡単に行うことができます。OpenCVを使用すれば、これらの操作をPythonで効率的に実装することができ、画像処理の幅が広がります。

### 参考記事
https://qiita.com/koshian2/items/c133e2e10c261b8646bf