第3回 スキュー

本題に入る前に、必ず 連絡 の動画を見てください。
Google Colabへのリンク

1. 画像をx方向に30°スキューさせる

動画の解説を参照

x方向のスキューとは、例えばこのような画像を

このように歪ませる変形のことです。

画像上のそれぞれの点に注目すれば、「y座標に応じてx方向の座標を増やす(右にずらす)」という変換です。
上の画像の例では、左の黒い直角三角形の上の角が30°なので、「x方向への30°スキュー」になります。
スキュー処理は、前回の「回転」で使った「warpAffine」関数で実行できます。
warpAffine関数を呼ぶときの第2引数に使う変換行列 \(M\) は、スキュー角度を \(\theta\) とすると
\(M= \begin{bmatrix} 1 & \tan\theta & 0 \\ 0 & 1 & 0 \end{bmatrix} \)
のようになります。
ただし、元画像と同じサイズのままだと右下の部分が画像外にはみ出してしまうので、横幅を広げる必要があります。
広げる幅は、図の黒い直角三角形の横幅、つまり
\(h\tan\theta\)
です。結局、出力画像の横幅は
\(w + h\tan\theta\)
にすればいいことになります。
import cv2
import numpy as np
from google.colab import files

file = files.upload()
filename = next(iter(file))
img = cv2.imread(filename)
h, w = img.shape[:2]

# x方向に30°スキュー
t = np.tan(np.deg2rad(30))
M = np.array([[1, t, 0], [0, 1, 0]], dtype=np.float32)
img1 = cv2.warpAffine(img, M, (int(w + h * t), h))
cv2.imwrite('ex1.jpg', img1)

課題1

適当な画像を読み込んで、概要のコードでx方向に30°スキューさせた画像「ex1.jpg」を出力してください。

2. 画像をx方向に-30°スキューさせる

動画の解説を参照

スキューの角度 \(\theta\) が負の場合も基本的な考え方は同じです。
ただし、\(\tan\theta\) も負になるので、出力画像の横幅は
\(w + h|\tan\theta|\)
です。また、画像の下側が左方向にずれるので、前項と同じ \(M\) で変換するとこのように左側が画像外にはみ出してしまいます。

全体が収まるようにするには、\(h|\tan\theta|\) だけ右にずらす必要があります。
つまり、変換行列を
\(M= \begin{bmatrix} 1 & \tan\theta & h|\tan\theta| \\ 0 & 1 & 0 \end{bmatrix} \)
とすれば、このような結果になります。
# x方向に-30°スキュー
t = np.tan(np.deg2rad(-30))
M = np.array([[1, t, h * np.abs(t)], [0, 1, 0]], dtype=np.float32)
img2 = cv2.warpAffine(img, M, (int(w + h * np.abs(t)), h))
cv2.imwrite('ex2.jpg', img2)

課題2

課題1と同じ画像を概要のコードでx方向に-30°スキューさせた画像「ex2.jpg」を出力してください。

3. 画像をy方向に30°スキューさせる

動画の解説を参照

y方向のスキューとは、これまでの説明の元画像をこのように歪ませる変形のことです。

画像上のそれぞれの点に注目すれば、「x座標に応じてy方向の座標を増やす(下にずらす)」という変換です。
上の画像の例では、上の黒い直角三角形の左の角が30°なので、「y方向への30°スキュー」になります。
スキュー角を \(\theta\) とすると、変換行列は
\(M= \begin{bmatrix} 1 & 0 & 0 \\ \tan\theta & 1 & 0 \end{bmatrix} \)
のようになります。
ただし、元画像と同じサイズのままだと右下の部分が画像外にはみ出してしまうので、縦幅を広げる必要があります。
広げる幅は、図の黒い直角三角形の縦幅、つまり
\(w\tan\theta\)
です。結局、出力画像の縦幅は
\(h + w\tan\theta\)
にすればいいことになります。
# y方向に30°スキュー
t = np.tan(np.deg2rad(30))
M = np.array([[1, 0, 0], [t, 1, 0]], dtype=np.float32)
img3 = cv2.warpAffine(img, M, (w, int(h + w * t)))
cv2.imwrite('ex3.jpg', img3)

課題3

課題1と同じ画像を概要のコードでy方向に30°スキューさせた画像「ex3.jpg」を出力してください。

4. 画像をy方向に-30°スキューさせる

動画の解説を参照

「x方向の30°スキュー」から「x方向の-30°スキュー」への変更を行ったときと同様に考えれば、「y方向の30°スキュー」をベースにして「y方向の-30°スキュー」ができます。
出力画像の縦幅を
\(h + w|\tan\theta|\)
変換行列を
\(M= \begin{bmatrix} 1 & 0 & 0 \\ \tan\theta & 1 & w|\tan\theta| \end{bmatrix} \)
とすれば、このような出力結果ができます。
# y方向に-30°スキュー
t = np.tan(np.deg2rad(-30))
M = np.array([[1, 0, 0], [t, 1, w * np.abs(t)]], dtype=np.float32)
img4 = cv2.warpAffine(img, M, (w, int(h + w * np.abs(t))))
cv2.imwrite('ex4.jpg', img4)

課題4

課題1と同じ画像を概要のコードでy方向に-30°スキューさせた画像「ex4.jpg」を出力してください。

提出

動画の解説を参照

今回作成したノートブックを「imgprc2024@gmail.com」と共有してください。
※ 前回共有した、画像共有用の「画像処理」フォルダの中に「第3回」フォルダを作り、その中に今回の元画像と出力画像を入れてください。
※「画像処理」フォルダは前回の時点で共有済なので、再度共有の処理を行う必要はありません (というか、しないでください)。
※ 課題の再提出の際は、ノートブックの再度の共有はせずにチャットで連絡してください。