広告
広告

Blender で numpy を使う

カテゴリ:blender

基本的な使い方はnumpyの使い方Blenderではじめる画像処理を参照。

高速化に関してはNumPyによる数値計算の高速化 : 基礎NumPyから最高のパフォーマンスを得る方法が参考になる。

データの型を指定する

データの型を指定せずに numpy.array で配列に変換すると、型は float64 になる。

import bpy
import numpy as np

img = bpy.data.images['image']
arr = np.array(img.pixels[:])
print(arr.dtype)
#arr は dtype('float64')

float64 は計算が遅い。たいていの場合 float32 で十分精度は足りる。テクスチャなら float16 でも十分だ。その方がメモリ使用量が少ないうえ計算も速い。型の指定は dtype でできる。

arr = np.array(img.pixels[:], dtype=np.float16)

ベンチマーク

以下のコードは画像の R 成分を 0 にする処理のベンチマークだ。

import bpy
import numpy as np
import time

def timer_alloc(img, dtype):
    oldtime = time.time()
    arr = np.array(img.pixels[:], dtype=dtype)
    if dtype==np.float16:
        str_type='float16'
    elif dtype==np.float32:
        str_type='float32'
    else:
        str_type='float64'
    print("alloc time %s  : %08f sec" % (str_type, time.time()-oldtime))
    return arr
    
def timer_process(arr, text):
    oldtime = time.time()
    arr[0::4] = 0.0 # R成分に一括で0を代入
    print("process time %s: %08f sec\n" % (text, time.time()-oldtime))

img = bpy.data.images['big_texture']

#float 16
arr = timer_alloc(img, np.float16)
timer_process(arr, 'float16')

# float 32
arr = timer_alloc(img, np.float32)
timer_process(arr, 'float32')

# float 64
arr = timer_alloc(img, np.float64)
timer_process(arr, 'float64')

結果は以下のようになった。

bench result
ベンチ結果

このベンチではメモリの型に関わらず、メモリコピーにかかる時間は同じだった。float16 と float32 とを比べると約4倍ほど float16 の方が速い。

numpy.array が遅いのはメモリコピーの部分だ。メモリ確保の速度は numpy.empty() を使えば高速だ。しかし numpy でない配列アクセスが遅いので、メモリコピーの部分は高速化できない。

整数を使う

整数で十分な精度なら int8 を使うことでさらに高速になる。float から int への変換コストがかかるが、処理が複雑ならやる価値はある。

# 上のベンチの最後に追加する
# float16 to int8
oldtime = time.time()
arr = arr * 255
arr = arr.astype(np.int8)
print("alloc time %s  : %08f sec" % ('int8', time.time()-oldtime))

timer_process(arr, 'int8')
imt8 result
ベンチ結果

int8 の処理時間は float16 の処理時間の半分になっている。float64 と比べると8倍速い。

関連記事

Blender 記事の目次


広告
広告