2018/02/07(水) [n年前の日記]
#2 [gimp][python] GIMPのPython-fu + pycairoで描いた画像をGIMPのレイヤー用に変換する処理について
GIMPのPython-fuスクリプトの中では、cairo (pycairo) を使った描画もできるっぽいのだけど。cairo が利用する ImageSurface は、1ドットが ARGB で並んでいるけど、GIMPのレイヤー側は、1ドットが RGBA で並んでいるので、ARGB から RGBA に変換する処理が必要で。ソレをしないと、cairo の ImageSurface を GIMPレイヤーに正常な色で転送できない。
一応、以前そういう処理を書いたのだけど。
_mieki256's diary - Ubuntu Linux 16.04LTS上でGIMPのPython-fuに関して試行錯誤中
どうも処理が遅いと言うか…。64x64ドットぐらいの処理なら気にならないけど、1024x1024 = 1048576回ループさせると数秒待たされる感じで。
もうちょっと高速化できないかと色々試して、結局、以下のようになったけれど…。
これでもまだ遅い。どうにかならんかな…。コレ以上は無理かな…。
ちなみに、cairo の ImageSurface の1ドットがバイト単位で ARGB の順に必ず並んでるかどうかは分からない。おそらく、CPUがビッグエンディアンかリトルエンディアンで並びが違ってくる可能性がある。GIMPレイヤー側はビッグエンディアンのRGBAで並んでるっぽいけど…。
一応、以前そういう処理を書いたのだけど。
_mieki256's diary - Ubuntu Linux 16.04LTS上でGIMPのPython-fuに関して試行錯誤中
どうも処理が遅いと言うか…。64x64ドットぐらいの処理なら気にならないけど、1024x1024 = 1048576回ループさせると数秒待たされる感じで。
もうちょっと高速化できないかと色々試して、結局、以下のようになったけれど…。
import struct def get_rgba_str(src): """Convert cairo surface data to RGBA.""" unpack = struct.Struct('=L').unpack_from pack = struct.Struct('>L').pack lmax = len(src) / 4 rgba_buf = [None] * lmax for i in xrange(lmax): argb = unpack(src, i * 4)[0] rgba_buf[i] = pack(((argb & 0x00ffffff) << 8) | ((argb >> 24) & 0x0ff)) if i & 0x3fff == 0: gimp.progress_update(0.5 + 0.5 * float(i + 1) / lmax) return ''.join(rgba_buf)
これでもまだ遅い。どうにかならんかな…。コレ以上は無理かな…。
ちなみに、cairo の ImageSurface の1ドットがバイト単位で ARGB の順に必ず並んでるかどうかは分からない。おそらく、CPUがビッグエンディアンかリトルエンディアンで並びが違ってくる可能性がある。GIMPレイヤー側はビッグエンディアンのRGBAで並んでるっぽいけど…。
◎ 関連する話をメモ。 :
Python の structモジュールを使うと、Cの構造体っぽいソレでまとめられたバイナリデータを扱うことができる。バイナリを読むときは struct.unpack() を使って、バイナリにする時は struct.pack() を使う。
で、ループ処理内で、以下のように書けなくもないのだけど。
Python は文字列結合が遅いので、リスト(配列)に追加していくほうが速い、という話も見かけた。ただ、Python 2.4以降は a += b のような文字列結合は最適化されてるので、現状ではそれほど効果は無いらしい…。
_Python文字列バッファいろいろ - 一番速い奴連れてこいシリーズ - HDEラボ
_Pythonの処理速度を上げる方法 その2 - とある誰かの覚え書き
リストの長さを事前に確保しておく際は、[ None ] * n が速い、という話があるらしいのでコレも試した。
_あなたのPythonを爆速にする7つの方法
以前は、一旦、a, r, g, b に分けてから、struct.pack('4B', r, g, b, a) でバイナリにしていたけど。考えてみたら、ARGB を RGBA にするのだから、A だけをずらせば済むのだなと。4行あった処理を1行で済ませることができた。
これは Python-fu特有の話だけど、どうも体感では、プログレスバーの表示を更新する gimp.progress_update( 0.0 〜 1.0 の値 ) を頻繁に呼ぶと、それだけで遅くなるようで。考えてみれば、更新されるたびに画面内の該当部分を描画し直すのだから、そこで処理がかかるのも当たり前か…。更新回数を減らしてみたら、それだけでも効果があった…ような気がする。
で、ループ処理内で、以下のように書けなくもないのだけど。
argb = struct.unpack_from('=L', src, i * 4)[0]何度も同じフォーマット文字列を使う場合は、struct.Struct(フォーマット文字列) を使って、ループ外でフォーマット文字列を与えたソレを事前に用意して使ったほうが速くなる、という話を見かけたので一応試してみたり。
Python は文字列結合が遅いので、リスト(配列)に追加していくほうが速い、という話も見かけた。ただ、Python 2.4以降は a += b のような文字列結合は最適化されてるので、現状ではそれほど効果は無いらしい…。
_Python文字列バッファいろいろ - 一番速い奴連れてこいシリーズ - HDEラボ
_Pythonの処理速度を上げる方法 その2 - とある誰かの覚え書き
リストの長さを事前に確保しておく際は、[ None ] * n が速い、という話があるらしいのでコレも試した。
_あなたのPythonを爆速にする7つの方法
以前は、一旦、a, r, g, b に分けてから、struct.pack('4B', r, g, b, a) でバイナリにしていたけど。考えてみたら、ARGB を RGBA にするのだから、A だけをずらせば済むのだなと。4行あった処理を1行で済ませることができた。
これは Python-fu特有の話だけど、どうも体感では、プログレスバーの表示を更新する gimp.progress_update( 0.0 〜 1.0 の値 ) を頻繁に呼ぶと、それだけで遅くなるようで。考えてみれば、更新されるたびに画面内の該当部分を描画し直すのだから、そこで処理がかかるのも当たり前か…。更新回数を減らしてみたら、それだけでも効果があった…ような気がする。
[ ツッコむ ]
以上です。