#はじめに
NumPy配列ndarrayをシフト(スクロール)させるnp.rollを用いて画像をスクロールさせて遊んでみます。スクロールした画像をGIFに保存していきます。
せっかくなので子供が喜びそうなネタを扱っていきます。遊び心満載でお届けしますので、最後まで気楽にご覧ください。
#NumPy配列ndarrayをシフトするnp.rollとは
まずは以下のように一次元配列を考えます。
import numpy as np
a = np.arange(10)
print(a)
# [0 1 2 3 4 5 6 7 8 9]
これにnp.roll()の処理をしてシフトさせます。
第一引数aに元のndarray、第二引数shiftにシフトさせる要素数を指定します。
a_roll = np.roll(a, 3)
print(a_roll)
# [7 8 9 0 1 2 3 4 5 6]
print(a)
# [0 1 2 3 4 5 6 7 8 9]
第二引数shiftには負の値や全体の要素数を超える値も指定可能です。
print(np.roll(a, -3))
# [3 4 5 6 7 8 9 0 1 2]
print(np.roll(a, 12))
# [8 9 0 1 2 3 4 5 6 7]
# print(np.roll(a, 0.5))
二次元配列の場合は以下の通りです。
a_2d = np.arange(12).reshape(3, 4)
print(a_2d)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
print(np.roll(a_2d, 2))
# [[10 11 0 1]
# [ 2 3 4 5]
# [ 6 7 8 9]]
print(np.roll(a_2d, 5))
# [[ 7 8 9 10]
# [11 0 1 2]
# [ 3 4 5 6]]
np.roll()の第三引数axisに軸を指定すると、軸に沿ってシフトされます。
print(np.roll(a_2d, 1, axis=0))
# [[ 8 9 10 11]
# [ 0 1 2 3]
# [ 4 5 6 7]]
print(np.roll(a_2d, 2, axis=1))
# [[ 2 3 0 1]
# [ 6 7 4 5]
# [10 11 8 9]]
第二引数shiftと第三引数axisにタプルで複数の値を指定すると、複数の軸に沿ってシフトさせることも可能です。
print(np.roll(a_2d, (1, 2), axis=(0, 1)))
# [[10 11 8 9]
# [ 2 3 0 1]
# [ 6 7 4 5]]
以上、基本的な扱いとなります。
#画像処理への応用(画像をスクロール)
次にnp.roll()を画像にあてはめてみます。
np.roll()を利用するとNumPy配列ndarrayとして読み込んだ画像をスクロールさせることができるようになります。
import cv2
import numpy as np
from PIL import Image
input_file = "1.png"
output_file = "1.gif"
speed = -2
def cv2pil(imgCV):
imgCV_RGB = imgCV[:, :, ::-1]
imgPIL = Image.fromarray(imgCV_RGB)
return imgPIL
img = cv2.imread(input_file)
imgs = []
for t in range(19):
img = np.roll(img, speed*t , axis=1)
imgPIL = cv2pil(img)
imgs.append(imgPIL)
imgs[0].save(output_file,save_all=True, append_images=imgs[1:],optimize=False, duration=400, loop=0)
speed
でスクロールする速度を調整します。
またaxis=1
とすることで横方法にスクロールするように指定しています。
最後にスクロールしたフレームをつなぎ合わせてGIFファイルを出力します。
#画像の一部をスクロール
次に画像の一部分のみのをスクロールしてみます。
import cv2
import numpy as np
from PIL import Image
input_file = "2.jpg"
output_file = "2-2.gif"
speed = -50
img = cv2.imread(input_file)
height, width, channels = img.shape
#分割する座標を指定する
img_scroll = img[0:117, 0:width]
img_main = img[117:height, 0:width]
imgs = []
def cv2pil(imgCV):
imgCV_RGB = imgCV[:, :, ::-1]
imgPIL = Image.fromarray(imgCV_RGB)
return imgPIL
for t in range(8):
img_scroll = np.roll(img_scroll, speed*t, axis=1)
imgPIL = cv2pil(cv2.vconcat([img_scroll, img_main])) #分割した画像を結合する
imgs.append(imgPIL)
imgs[0].save(output_file, save_all=True, append_images=imgs[1:],optimize=False, duration=400, loop=0)
img_scroll
でスクロールする部分の座標を指定しています。
cv2.vconcat([img_scroll, img_main])
により画像を結合します。
画像の一部分だけスクロールするGIFが出力できました。
#行ごとにシフト量を調整して揺らぎの画像を作る
上の例のように行ごとにシフト量を調整して揺らぎの画像を作ってみます。
import cv2
import math
import numpy as np
from PIL import Image
input_file = "3.png"
output_file = "3.gif"
img = cv2.imread(input_file)
imgH, imgW = img.shape[:2]
A = imgW/32 # 振幅
f = 2*math.pi/imgH # 周波数
θ = 0 # 位相のずれ
step = 1 # 波の粗さ
T = 16 # 周期
def cv2pil(imgCV):
imgCV_RGB = imgCV[:, :, ::-1]
imgPIL = Image.fromarray(imgCV_RGB)
return imgPIL
imgs = []
for t in range(16):
for y in range(0, imgH, step):
x = int(A*math.sin(2*math.pi*(t-θ)/tt+f*y))
img[y:y+step] = np.roll(img[y:y+step], x, axis=1)
imgPIL = cv2pil(img)
imgs.append(imgPIL)
imgs[0].save(output_file, save_all=True, append_images=imgs[1:],optimize=False, duration=20, loop=0)
振幅と周波数を変えることで動きを変えることができます。
#一行ずつシフトする方向を変える
上の例のように行ごとにシフトして画像を作ってみます。
import cv2
import math
import numpy as np
from PIL import Image
input_file = "4.png"
output_file = "4.gif"
img = cv2.imread(input_file)
imgH, imgW = img.shape[:2]
A = imgW/16 # 振幅
θ = 0 # 位相のずれ
step = 1 # 計算のステップ 波の粗さ・計算量に関係
T = 16 # 周期
def cv2pil(imgCV):
imgCV_RGB = imgCV[:, :, ::-1]
imgPIL = Image.fromarray(imgCV_RGB)
return imgPIL
imgs = []
for t in range(16):
for y in range(0, imgH, step):
sign = 1 if y%2==0 else -1
x = int(sign*A*math.sin(2*math.pi*(t-θ)/T))
img[y:y+step] = np.roll(img[y:y+step], x, axis=1)
imgPIL = cv2pil(img)
imgs.append(imgPIL)
imgs[0].save(output_file, save_all=True, append_images=imgs[1:],optimize=False, duration=100, loop=0)
同様に振幅と周波数を変えることで動きを変えることができます。
#おわりに
最後までご覧いただきありがとうございました。