この投稿は以下の投稿の再現テストです。とりあえずプログラムで動かせたら自分的に敷居が下がるので…
完全に理解するアフィン変換
Python, OpenCVで画像ファイルの読み込み、保存(imread, imwrite)
Pythonのif name == "main" とは何ですか?への回答
matplotlib逆引きメモ(フレーム編)
元画像はこちら。
数学者のイラスト(女性)
pip で OpenCV のインストール
matplotlibのanimation.FuncAnimationを用いて柔軟にアニメーション作成
#Image表示
まずはこんな感じです。
Python, OpenCVで画像ファイルの読み込み、保存
import cv2
image = cv2.imread("desktop/python/suugakusya.jpg")[:,:,::-1]
plt.imshow(image)
plt.show()
#恒等変換
import cv2
import matplotlib.pyplot as plt
import numpy as np
def identity(image):
h, w = image.shape[:2]
src = np.array([[0.0, 0.0],[0.0, 1.0],[1.0, 0.0]], np.float32)
affine = cv2.getAffineTransform(src, src)
return cv2.warpAffine(image, affine, (w, h))
if __name__ == "__main__":
image = cv2.imread("desktop/python/suugakusya.jpg")[:,:,::-1]
converted = identity(image)
plt.imshow(converted)
plt.title("Identity")
plt.show()
#水平移動
添字操作。
【RからPythonへ】ガウス平面上の単位円(Unit Circle)/単位円筒(Unit Cylinder)表示からの再出発
NumPy配列ndarrayの末尾に要素・行・列を追加するappend
[python] スライスでリバース!!(スライスの解説もあるよ!)
import numpy as np
c0=np.arange(0,180,2)
c1=np.append(c0,[180])
c2=c0[::-1]
TimeCode=np.append(c1,c2)
print(TimeCode)
#出力結果
[ 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34
36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70
72 74 76 78 80 82 84 86 88 90 92 94 96 98 100 102 104 106
108 110 112 114 116 118 120 122 124 126 128 130 132 134 136 138 140 142
144 146 148 150 152 154 156 158 160 162 164 166 168 170 172 174 176 178
180 178 176 174 172 170 168 166 164 162 160 158 156 154 152 150 148 146
144 142 140 138 136 134 132 130 128 126 124 122 120 118 116 114 112 110
108 106 104 102 100 98 96 94 92 90 88 86 84 82 80 78 76 74
72 70 68 66 64 62 60 58 56 54 52 50 48 46 44 42 40 38
36 34 32 30 28 26 24 22 20 18 16 14 12 10 8 6 4 2
0]
print(len(TimeCode))
#出力結果
181
import cv2
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
#画面をデフォルトの640*480と設定した場合。
#fig = plt.figure(figsize = (6.4, 4.8))
fig = plt.figure()
#Indx数列を作成。
c0=np.arange(0,180,2)
c1=np.append(c0,[180])
c2=c0[::-1]
indx=np.append(c1,c2)
#元画像読み込み。
image = cv2.imread("desktop/python/suugakusya.jpg")[:,:,::-1]
def shift_x(image,shift):
h, w = image.shape[:2]
src = np.array([[0.0, 0.0],[0.0, 1.0],[1.0, 0.0]], np.float32)
dest = src.copy()
dest[:,0] += shift # シフトするピクセル値
affine = cv2.getAffineTransform(src, dest)
return cv2.warpAffine(image, affine, (w, h));
def update(i):
plt.cla()
converted = shift_x(image,indx[i])
plt.imshow(converted)
plt.title("Sift "+'x='+str(indx[i]));
ani = animation.FuncAnimation(fig, update, interval=50,frames=len(indx))
ani.save("desktop/python/affine19.gif", writer="pillow")
#垂直移動
import cv2
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
#画面をデフォルトの640*480と設定した場合。
#fig = plt.figure(figsize = (6.4, 4.8))
fig = plt.figure()
#Indx数列を作成。
c0=np.arange(0,180,2)
c1=np.append(c0,[180])
c2=c0[::-1]
indx=np.append(c1,c2)
#元画像読み込み。
image = cv2.imread("desktop/python/suugakusya.jpg")[:,:,::-1]
def shift_y(image, shift):
h, w = image.shape[:2]
src = np.array([[0.0, 0.0],[0.0, 1.0],[1.0, 0.0]], np.float32)
dest = src.copy()
dest[:,1] += shift # シフトするピクセル値
affine = cv2.getAffineTransform(src, dest)
return cv2.warpAffine(image, affine, (w, h))
def update(i):
plt.cla()
converted = shift_y(image,indx[i])
plt.imshow(converted)
plt.title("Shift "+'y='+str(indx[i]))
ani = animation.FuncAnimation(fig, update, interval=50,frames=len(indx))
ani.save("desktop/python/affine20.gif", writer="pillow")
#ランダムシフト
水平と垂直移動を組み合わせて任意の座標移動ができます。ディープラーニングのData Augmentationで使う「ランダムクロップ」もアフィン変換で再現できます。移動元、移動先の座標はnp.float32にしないと怒られるので注意しましょう。
添字操作
NumPy, randomで様々な種類の乱数の配列を生成
ind = []
for i in range(10):
rand = (np.random.randint(-40,40),np.random.randint(-40,40))
ind.append(rand)
indx=np.array(ind)
print(indx)
出力結果
[[ 7 -12]
[-13 20]
[ 29 9]
[ 18 -24]
[ 11 32]
[ 7 -30]
[ -6 24]
[-28 -34]
[-29 22]
[ -8 33]]
import cv2
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
#画面をデフォルトの640*480と設定した場合。
#fig = plt.figure(figsize = (6.4, 4.8))
fig = plt.figure()
#Indx数列を作成。
ind = []
for i in range(10):
rand = (np.random.randint(-40,40),np.random.randint(-40,40))
ind.append(rand)
indx=np.array(ind)
#元画像読み込み。
image = cv2.imread("desktop/python/suugakusya.jpg")[:,:,::-1]
def random_shift(image, shifts):
h, w = image.shape[:2]
src = np.array([[0.0, 0.0],[0.0, 1.0],[1.0, 0.0]], np.float32)
dest = src + shifts.reshape(1,-1).astype(np.float32)
affine = cv2.getAffineTransform(src, dest)
return cv2.warpAffine(image, affine, (w, h))
def update(i):
plt.cla()
converted = random_shift(image,indx[i])
plt.imshow(converted)
plt.title("Shift "+'='+str(indx[i]))
ani = animation.FuncAnimation(fig, update, interval=50,frames=len(indx))
ani.save("desktop/python/affine21.gif", writer="pillow")
#拡大・縮小(Scale)
移動系では移動先の座標を足し算で計算していましたが、これを掛け算にかえると拡大・縮小になります。拡大縮小に限った話ではありませんが、補間法も通常のresizeと同様に指定できます。今回は最高品質のLANCZOS法を使ってみました。デフォルトだと線形補間(INTER_LINEAR)になります。
添字操作
import numpy as np
c0=np.arange(0,2,0.1)
c1=np.append(c0,[2.0])
c2=c0[::-1]
indx=np.append(c1,c2)
print(indx)
#出力結果
[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. 1.1 1.2 1.3 1.4 1.5 1.6 1.7
1.8 1.9 2. 1.9 1.8 1.7 1.6 1.5 1.4 1.3 1.2 1.1 1. 0.9 0.8 0.7 0.6 0.5
0.4 0.3 0.2 0.1 0. ]
import cv2
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
#画面をデフォルトの640*480と設定した場合。
#fig = plt.figure(figsize = (6.4, 4.8))
fig = plt.figure()
#Indx数列を作成。
c0=np.arange(0,2,0.1)
c1=np.append(c0,[2.0])
c2=c0[::-1]
indx=np.append(c1,c2)
#元画像読み込み。
image = cv2.imread("desktop/python/suugakusya.jpg")[:,:,::-1]
def expand(image, ratio):
h, w = image.shape[:2]
src = np.array([[0.0, 0.0],[0.0, 1.0],[1.0, 0.0]], np.float32)
dest = src * ratio
affine = cv2.getAffineTransform(src, dest)
return cv2.warpAffine(image, affine, (2*w, 2*h), cv2.INTER_LANCZOS4) # 補間法も指定できる
def update(i):
plt.cla()
converted = expand(image,indx[i])
plt.imshow(converted)
plt.title("Expand "+'='+str(indx[i]))
ani = animation.FuncAnimation(fig, update, interval=50,frames=len(indx))
ani.save("desktop/python/affine22.gif", writer="pillow")
#剪断(Shear)
平行四辺形型の変換ををせん断(Shear)といいます。まずは横方向の歪みを作ります。考え方は水平移動と同じでxx座標を足していくのですが、yy座標ごとに足していくxxを少しずつ変えていくと「水平方向のせん断」の変形になります。
添字操作
Pythonでスライスを使ってリストの要素を抽出する方法
Pythonのリストからの要素の取り出し(抽出)方法のまとめ
リストのインデックスを複数指定する(Python)
[python] リストから複数のインデックスを指定して値を取得・削除するまとめ
import numpy as np
c0=np.arange(0,180,3)
c1=np.append(c0,[180])
c2=c0[::-1]
c3=np.append(c1,c2)
c4=np.arange(-180,0,3)
c5=c4[::-1]
c6=np.append([0],c5)
c7=np.append(c6,c4[1:len(c4)])
indx=np.append(c7,c3)
print(indx)
#変換結果
[ 0 -3 -6 -9 -12 -15 -18 -21 -24 -27 -30 -33 -36 -39
-42 -45 -48 -51 -54 -57 -60 -63 -66 -69 -72 -75 -78 -81
-84 -87 -90 -93 -96 -99 -102 -105 -108 -111 -114 -117 -120 -123
-126 -129 -132 -135 -138 -141 -144 -147 -150 -153 -156 -159 -162 -165
-168 -171 -174 -177 -180 -177 -174 -171 -168 -165 -162 -159 -156 -153
-150 -147 -144 -141 -138 -135 -132 -129 -126 -123 -120 -117 -114 -111
-108 -105 -102 -99 -96 -93 -90 -87 -84 -81 -78 -75 -72 -69
-66 -63 -60 -57 -54 -51 -48 -45 -42 -39 -36 -33 -30 -27
-24 -21 -18 -15 -12 -9 -6 -3 0 3 6 9 12 15
18 21 24 27 30 33 36 39 42 45 48 51 54 57
60 63 66 69 72 75 78 81 84 87 90 93 96 99
102 105 108 111 114 117 120 123 126 129 132 135 138 141
144 147 150 153 156 159 162 165 168 171 174 177 180 177
174 171 168 165 162 159 156 153 150 147 144 141 138 135
132 129 126 123 120 117 114 111 108 105 102 99 96 93
90 87 84 81 78 75 72 69 66 63 60 57 54 51
48 45 42 39 36 33 30 27 24 21 18 15 12 9
6 3 0]
import cv2
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
#画面をデフォルトの640*480と設定した場合。
#fig = plt.figure(figsize = (6.4, 4.8))
fig = plt.figure()
#Indx数列を作成。
c0=np.arange(0,180,3)
c1=np.append(c0,[180])
c2=c0[::-1]
c3=np.append(c1,c2)
c4=np.arange(-180,0,3)
c5=c4[::-1]
c6=np.append([0],c5)
c7=np.append(c6,c4[1:len(c4)])
indx=np.append(c7,c3)
#元画像読み込み。
image = cv2.imread("desktop/python/suugakusya.jpg")[:,:,::-1]
def shear_X(image, shear):
h, w = image.shape[:2]
src = np.array([[0.0, 0.0],[0.0, 1.0],[1.0, 0.0]], np.float32)
dest = src.copy()
dest[:,0] += (shear / h * (h - src[:,1])).astype(np.float32)
affine = cv2.getAffineTransform(src, dest)
return cv2.warpAffine(image, affine, (w, h))
def update(i):
plt.cla()
converted = shear_X(image,indx[i])
plt.imshow(converted)
plt.title("Shear "+'X='+str(indx[i]))
ani = animation.FuncAnimation(fig, update, interval=50,frames=len(indx))
ani.save("desktop/python/affine10.gif", writer="pillow")
import cv2
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
#画面をデフォルトの640*480と設定した場合。
#fig = plt.figure(figsize = (6.4, 4.8))
fig = plt.figure()
#Indx数列を作成。
c0=np.arange(0,180,3)
c1=np.append(c0,[180])
c2=c0[::-1]
c3=np.append(c1,c2)
c4=np.arange(-180,0,3)
c5=c4[::-1]
c6=np.append([0],c5)
c7=np.append(c6,c4[1:len(c4)])
indx=np.append(c7,c3)
#元画像読み込み。
image = cv2.imread("desktop/python/suugakusya.jpg")[:,:,::-1]
def shear_Y(image, shear):
h, w = image.shape[:2]
src = np.array([[0.0, 0.0],[0.0, 1.0],[1.0, 0.0]], np.float32)
dest = src.copy()
dest[:,1] += (shear / w * (w - src[:,0])).astype(np.float32)
affine = cv2.getAffineTransform(src, dest)
return cv2.warpAffine(image, affine, (w, h))
def update(i):
plt.cla()
converted = shear_Y(image,indx[i])
plt.imshow(converted)
plt.title("Shear "+'Y='+str(indx[i]))
ani = animation.FuncAnimation(fig, update, interval=50,frames=len(indx))
ani.save("desktop/python/affine11.gif", writer="pillow")
import cv2
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
#画面をデフォルトの640*480と設定した場合。
#fig = plt.figure(figsize = (6.4, 4.8))
fig = plt.figure()
#Indx数列を作成。
c0=np.arange(0,180,3)
c1=np.append(c0,[180])
c2=c0[::-1]
c3=np.append(c1,c2)
c4=np.arange(-180,0,3)
c5=c4[::-1]
c6=np.append([0],c5)
c7=np.append(c6,c4[1:len(c4)])
indx=np.append(c7,c3)
#元画像読み込み。
image = cv2.imread("desktop/python/suugakusya.jpg")[:,:,::-1]
def shear_Y(image, shear):
h, w = image.shape[:2]
src = np.array([[0.0, 0.0],[0.0, 1.0],[1.0, 0.0]], np.float32)
dest = src.copy()
dest[:,1] += (shear / w * src[:,0]).astype(np.float32)
affine = cv2.getAffineTransform(src, dest)
return cv2.warpAffine(image, affine, (w, h))
def update(i):
plt.cla()
converted = shear_Y(image,indx[i])
plt.imshow(converted)
plt.title("Shear "+'Y='+str(indx[i]))
ani = animation.FuncAnimation(fig, update, interval=50,frames=len(indx))
ani.save("desktop/python/affine12.gif", writer="pillow")
#反転(Flip)
import cv2
import matplotlib.pyplot as plt
import numpy as np
def horizontal_flip(image):
h, w = image.shape[:2]
src = np.array([[0.0, 0.0],[0.0, 1.0],[1.0, 0.0]], np.float32)
dest = src.copy()
dest[:,0] = w - src[:,0]
affine = cv2.getAffineTransform(src, dest)
return cv2.warpAffine(image, affine, (w, h))
if __name__ == "__main__":
image = cv2.imread("desktop/python/suugakusya.jpg")[:,:,::-1]
converted = horizontal_flip(image)
plt.imshow(converted)
plt.title("Horizontal flip")
plt.show()
import cv2
import matplotlib.pyplot as plt
import numpy as np
def vertical_flip(image):
h, w = image.shape[:2]
src = np.array([[0.0, 0.0],[0.0, 1.0],[1.0, 0.0]], np.float32)
dest = src.copy()
dest[:,1] = h - src[:,1]
affine = cv2.getAffineTransform(src, dest)
return cv2.warpAffine(image, affine, (w, h))
if __name__ == "__main__":
image = cv2.imread("desktop/python/suugakusya.jpg")[:,:,::-1]
converted = vertical_flip(image)
plt.imshow(converted)
plt.title("Vertical Flip")
plt.show()
#回転(Rotate)
さていよいよ回転です。左上を原点として反時計回りにθだけ回転する操作を考えます。こうしてみると、回転というのはアフィン変換におけるほんの一部でしかないのがわかります。回転は「原点を固定して、三角形の辺の長さが維持されるという縛りをおいたアフィン変換」ということになりますね。
添字操作
import numpy as np
c0=np.arange(0,90,3)
c1=np.append(c0,[90])
c2=c0[::-1]
c3=np.append(c1,c2)
c4=np.arange(-90,0,3)
c5=c4[::-1]
c6=np.append([0],c5)
c7=np.append(c6,c4[1:len(c4)])
indx=np.append(c7,c3)
print(indx)
#出力結果
[ 0 -3 -6 -9 -12 -15 -18 -21 -24 -27 -30 -33 -36 -39 -42 -45 -48 -51
-54 -57 -60 -63 -66 -69 -72 -75 -78 -81 -84 -87 -90 -87 -84 -81 -78 -75
-72 -69 -66 -63 -60 -57 -54 -51 -48 -45 -42 -39 -36 -33 -30 -27 -24 -21
-18 -15 -12 -9 -6 -3 0 3 6 9 12 15 18 21 24 27 30 33
36 39 42 45 48 51 54 57 60 63 66 69 72 75 78 81 84 87
90 87 84 81 78 75 72 69 66 63 60 57 54 51 48 45 42 39
36 33 30 27 24 21 18 15 12 9 6 3 0]
import cv2
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
#画面をデフォルトの640*480と設定した場合。
#fig = plt.figure(figsize = (6.4, 4.8))
fig = plt.figure()
#Indx数列を作成。
c0=np.arange(0,90,3)
c1=np.append(c0,[90])
c2=c0[::-1]
c3=np.append(c1,c2)
c4=np.arange(-90,0,3)
c5=c4[::-1]
c6=np.append([0],c5)
c7=np.append(c6,c4[1:len(c4)])
indx=np.append(c7,c3)
#元画像読み込み。
image = cv2.imread("desktop/python/suugakusya.jpg")[:,:,::-1]
def rotate(image, angle):
h, w = image.shape[:2]
affine = cv2.getRotationMatrix2D((0,0), angle, 1.0)
return cv2.warpAffine(image, affine, (w, h))
def update(i):
plt.cla()
converted = rotate(image,indx[i])
plt.imshow(converted)
plt.title("Rotate ="+str(indx[i]))
ani = animation.FuncAnimation(fig, update, interval=50,frames=len(indx))
ani.save("desktop/python/affine15.gif", writer="pillow")
左上を原点とすると画像が大きく切れてしまうので、中心を原点にして回転してみます。ほとんどコードは一緒です。内部的には回転と平行移動の合成変換をやっています。
import cv2
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
#画面をデフォルトの640*480と設定した場合。
#fig = plt.figure(figsize = (6.4, 4.8))
fig = plt.figure()
#Indx数列を作成。
c0=np.arange(0,90,3)
c1=np.append(c0,[90])
c2=c0[::-1]
c3=np.append(c1,c2)
c4=np.arange(-90,0,3)
c5=c4[::-1]
c6=np.append([0],c5)
c7=np.append(c6,c4[1:len(c4)])
indx=np.append(c7,c3)
#元画像読み込み。
image = cv2.imread("desktop/python/suugakusya.jpg")[:,:,::-1]
def rotate_center(image, angle):
h, w = image.shape[:2]
affine = cv2.getRotationMatrix2D((w/2.0, h/2.0), angle, 1.0)
return cv2.warpAffine(image, affine, (w, h))
def update(i):
plt.cla()
converted = rotate_center(image,indx[i])
plt.imshow(converted)
plt.title("Rotate ="+str(indx[i]))
ani = animation.FuncAnimation(fig, update, interval=50,frames=len(indx))
ani.save("desktop/python/affine16.gif", writer="pillow")
中心を原点にして回転しても微妙にはみ出してしまいます。完全にはみ出さない方法は、アフィン変換の合成を意識して使います。
添字操作
import numpy as np
indx=np.arange(0,360,3)
print(indx)
#出力結果
[ 0 3 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48 51
54 57 60 63 66 69 72 75 78 81 84 87 90 93 96 99 102 105
108 111 114 117 120 123 126 129 132 135 138 141 144 147 150 153 156 159
162 165 168 171 174 177 180 183 186 189 192 195 198 201 204 207 210 213
216 219 222 225 228 231 234 237 240 243 246 249 252 255 258 261 264 267
270 273 276 279 282 285 288 291 294 297 300 303 306 309 312 315 318 321
324 327 330 333 336 339 342 345 348 351 354 357]
import cv2
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
#画面をデフォルトの640*480と設定した場合。
#fig = plt.figure(figsize = (6.4, 4.8))
fig = plt.figure()
#Indx数列を作成。
indx=np.arange(0,360,3)
#元画像読み込み。
image = cv2.imread("desktop/python/suugakusya.jpg")[:,:,::-1]
def rotate_fit(image, angle):
h, w = image.shape[:2]
# 回転後のサイズ
radian = np.radians(angle)
sine = np.abs(np.sin(radian))
cosine = np.abs(np.cos(radian))
tri_mat = np.array([[cosine, sine],[sine, cosine]], np.float32)
old_size = np.array([w,h], np.float32)
new_size = np.ravel(np.dot(tri_mat, old_size.reshape(-1,1)))
# 回転アフィン
affine = cv2.getRotationMatrix2D((w/2.0, h/2.0), angle, 1.0)
# 平行移動
affine[:2,2] += (new_size-old_size)/2.0
# リサイズ
affine[:2,:] *= (old_size / new_size).reshape(-1,1)
return cv2.warpAffine(image, affine, (w, h))
def update(i):
plt.cla()
converted = rotate_fit(image,indx[i])
plt.imshow(converted)
plt.title("Rotate ="+str(indx[i]))
ani = animation.FuncAnimation(fig, update, interval=50,frames=len(indx))
ani.save("desktop/python/affine18.gif", writer="pillow")
おや? この操作には見覚えが…
数理考古学】ピタゴラスの定理あるいは三平方の定理からの出発
現段階の私に食い切れるのはここまでの様です。そして、こうした処理をマスク処理と組み合わせるとこんな感じに…
【Python画像処理】スプライト・アニメーションを試す。
そんな感じで、以下続報…