ライン全体を取得できる場合は単一ピクセルを取得しない

MAXScript に関する質問と回答 > 処理速度を上げる方法 > ライン全体を取得できる場合は単一ピクセルを取得しない

ビットマップへのピクセルの読み込みおよび書き込みを行う getPixels と setPixels 関数は、処理に時間がかかります。

ビットマップのピクセルを大量にまたはすべて変更する場合は、 getPixels() setPixels() を水平方向ラインごとに 1 回だけ実行し、結果として生成された配列要素を使用して残りの作業を行うことをお勧めします。

次の例では、1 つ 1 つのピクセルを変更することによって 1000 x 1000 ピクセルのビットマップが変更されます。つまり、100 万ピクセルを読み込んだ後、書き戻すことを意味します。

最初の例では、1 つ 1 つのピクセルが別々に読み込まれ、数で乗算されて戻されます。最適化された例と比較するため、時間を停止します。

例 1 - 最適化されていないスクリプト:

b = bitmap 1000 1000 color:red--create new bitmap with red background
st = timestamp()--get start time in milliseconds
for y = 1 to 1000 do--go through all lines in the bitmap
(
  for x = 1 to 1000 do--go through all pixels in a line
  (
    pixels = getPixels b [x-1,y-1] 1--read one pixel from X,Y
    pixels[1] *= (x+y)/2000.0--alter the pixel
    setPixels b [x-1,y-1] pixels--write modified pixel back
  )--end x loop
)--end y loop
et = timestamp()--get end time in milliseconds
print (et-st)--print time to finish
display b--show a nice black-red gradient

このスクリプトを 2.1GHz のシステムで実行した結果、タイム スタンプは 16094 ミリ秒でした。

2 番目の例では、1 つ 1 つのピクセルを読み込む代わりに、すべてのラインを読み込んで、 getPixels() で返された配列内部のすべてのピクセルを処理してからライン全体をビットマップに書き込みます。つまり、呼び出しは 100 万回の読み込みと 100 万回の書き込みではなく、1000 回の読み込みと 1000 回の書き込みだけでよいことになります。

例 2 - 最適化されているスクリプト:

b = bitmap 1000 1000 color:red--create new bitmap with red background
st = timestamp()--get start time in milliseconds
for y = 1 to 1000 do--go through all lines in the bitmap
(
  pixels = getPixels b [0,y-1] 1000--read all 1000 pixels of a single line
  for x = 1 to 1000 do--go through all pixels in the line
  (
    pixels[x] *= (x+y)/2000.0--alter the pixel
  ) 
  setPixels b [0,y-1] pixels--write back the complete modified line
)
et = timestamp()--get end time in milliseconds
print (et-st)--print time to finish
display b--show the same nice black-red gradient

最適化されたスクリプトを同じ 2.1GHz のシステムで実行した結果、タイム スタンプは 4937 ミリ秒となり、処理速度は 3 倍以上向上しました。

3ds Max 2010 以降では、カスタム function: モードで pasteBitmap() 関数を使用して、MAXScript 関数を使用してピクセルごとの操作を実行できるようになりました。このメソッドには getPixels() 呼び出しおよび setPixels() 呼び出しは関係ありませんが、同じ処理が行われるほか、2 つのビットマップを合成する場合や、テスト サンプルと同じように、単一のビットマップ内でピクセルを操作する場合にも適しています。この例では同じビットマップを単純に 2 回渡しています。結果は通常は 2 つ目のビットマップ内に保存され、この場合は1 つ目のビットマップとまったく同じであるため、1 つのビットマップ値を使用して効率的に読み出しおよび書き込みを行うことができます。

例 3 - 代替スクリプト:

fn compfn c1 p1 c2 p2 =
(
  c1 * ((p1.x+p1.y)/2000.0)
)
b = bitmap 1000 1000 color:red--create new bitmap with red background
st = timestamp()--get start time in milliseconds
pasteBitmap b b [0,0] [0,0] type:#function function:compfn--call the function per pixel
et = timestamp()--get end time in milliseconds
print (et-st)--print time to finish
display b--show the same nice black-red gradient

代替スクリプトを同じ 2.1GHz のシステムで実行した結果、タイム スタンプは 3828 ミリ秒となり、上記の最適化されているスクリプトよりも速くなりました。

次の表は、これらの 3 つの例を、異なる画像解像度で実行した場合の処理時間をミリ秒単位で示したものです。

 

2000x2000

1000x1000

500x500

250x250

125x125

例 1

64657

16094

3578

906

203

例 2

20000

4937

1266

344

62

例 3:

15187

3828

906

172

47

前のヒント

「flagForeground」ノードのビューポート状態のメソッドを使用する

次のヒント

可能であれば計算の実行は 1 回だけにする