トップ «前の日記(2006-03-25) 最新 次の日記(2006-03-28)» 編集

AOISAKURA 日記

2003|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|04|05|06|07|08|09|12|
2013|02|03|05|06|08|09|10|11|
2015|11|
2016|12|
2017|03|08|
2018|01|03|06|
2019|05|07|09|10|
2020|01|02|03|
2021|03|07|
2022|06|
2023|11|

pixiv / twitter

bluesky / misskey / pawoo

※発信する情報は、個人の見解・意見であり、所属する会社の見解・意見ではありません。


2006-03-27 やっとゆっくり [長年日記]

_ [gimp][translation] how to write a GIMP plug-in 勝手和訳 3P目

前回の2P目の和訳からえらい間が空きましたが、How to write a GIMP plug-in 3Pの和訳です。訳出の意図、注意は1P目同様です。

コードについてはプロトタイプ宣言だけは載せていますので、コード全体は原文を適宜参照してください。

この和訳文書はcreative commons : 帰属 - 非営利 - 同一条件許諾 2.5でライセンスされてます。

_ GIMPプラグインの書き方 How to write a GIMP plug-in 3P目

原著者: Dave Neary(bolsh@NOSPAM.gimp.org)

第二章で、私はピクセル単位か行単位での画像データ(image data)の操作を説明した。今回は、さらに進めて、今までに作ったプラグインの性能を改善させられるタイル単位でのデータ処理を行う。また、より大きな半径を配慮したアルゴリズムへの改良を行い、パラメータを変更させることができるようなグラフィカルインターフェイスの構築も行う。

_ はじめに

前に作った単純なアルゴリズムを確認してみよう。このアルゴリズムは、ピクセル単位で、各レイヤーに(2r+1)x(2r+1) - x,yの形式、rは半径 - の近傍を設定し、その近傍の平均でレイヤーの各ピクセルを置き換えるモノであった。

例の画像で画像の境界で注意を払わなければいけないのはいささか複雑ではある。しかしこのアルゴリズムによるブラー処理は一般的にそれほど悪くはない。

とはいえ、今まで書いてきたアルゴリズムは3x3近傍ピクセルを対象とした処理であった。この章では、対象とする範囲を一般化し、パラメータとして近傍の範囲を取得するようにする。

まずはタイルについて。

_ タイルの管理

タイルとはサイズ64x64ピクセルの画像データブロックです。通常タイルはプラグインに対して一つ一つ要求に応じて、共有メモリから送られます。もちろん、この処理は大量の資源を必要とし避けるべきである。

普通、特定のキャッシュを必要とすることはない、どのタイルも必要とされたとき送られ、他のタイルが呼ばれれば、開放される。それでもなお、以下の関数を使うことによって、一定間隔で回ってくるround tripを避けてプラグインにタイルキャッシュを保持させることができる。

gimp_tile_cache_ntiles (gulong ntiles);

part2の例では、gimp_pixel_rgn_get_row()とgimp_pixel_rgn_set_row()を呼び出したが、キャッシュを使うことはしなかった。

タイル列一列のタイルの数は、タイルの幅でレイヤーの幅を割った数に1足した数となるだろう。そのため、レイヤーの幅が65の場合、我々は二つのタイルをキャッシュすることになる。通常シャドウタイルも処理するので、プラグインの扱うキャッシュのサイズは倍にしている。

gimp_tile_cache_ntiles (2 * (drawable->width / 
                        gimp_tile_width () + 1));

キャッシュによって遅かったプラグインは早くなった。300x300の画像上では、最新のブラー処理は3秒かかった。しかし、2000x1500の画像上では遅くなり、142秒かかった。

上記のコードを追加することで、処理速度は向上し、11秒で終了した。我々はまだタイルの変わり目での変化に要する時間を失っている。2行分のタイルキャッシュを取得する変わりに4行分取得するようにしたら、10秒まで減らすことができた。ある点を堺に処理時間の増加を減らすことができたが、より多くのキャッシュを必要とするようになり、またさらなるハードディスクへのアクセスを発生させることになった。

_ アルゴリズムの一般化

半径パラメータを考慮するために、アルゴリズムを修正しよう。半径が3の場合、ピクセル近傍は7x7とし、半径1の場合は3x3とする。これを実現するために、以前のアルゴリズムを次のように修正する。

  • 2r+1(rは半径)個のタイルの列に必要な領域を割り当てる
  • 境界に注意しながら、タイルの配列を初期化する。
  • 一つ一つの行にたいして以下の処理を行う
    • 行の各ピクセルにたいして以下の処理を行う
      • 境界に注意しながら、近傍の平均を計算する。
    • 新しい行を取得し、行の配列を回す。

このアルゴリズムは一つ前のよりも複雑である、なぜなら平均の計算時間がO(r^2)のアルゴリズムだからである。

このように動作するよう修正されたソースコードを以下に示す。ほとんどの処理は、process_row()関数内で行われる。init_mem()関数とshuffle()関数はブラー処理のソースコードをきれいで小さく保つ。

static void blur        (GimpDrawable *drawable);
...
static void blur (GimpDrawable *drawable) { ... }
static void init_mem (guchar ***row, guchar  **outrow, gint num_bytes) { ... }
static void process_row (guchar **row, guchar  *outrow, gint x1, gint y1, 
                         gint width, gint height, gint channels, gint i) { ... }
static void shuffle (GimpPixelRgn *rgn_in, guchar **row, gint x1, gint y1,
                     gint width, gint height, gint ypos) { ... }

_ グラフィカルなインターフェイスの追加とパラメータの保存

ユーザが半径を修正しようとするために、もしくは対話的で無いスクリプトにパラメータとして値を渡す場合、run()関数へ戻る必要があり、それは単純なスクリプトでできる。

まず我々は、オプションを保存し返すことができる構造体を作成する。たいてい、これは一つだけパラメータをもつプラグインの為に使われる。

typedef struct {
        gint radius;
      } MyBlurVals;
//
/* Set up default values for options */
static MyBlurVals bvals = {
        3  /* radius */
      };

次に、execution modes(実行モード)を考慮するためにrun()関数を修正する。interactive mode(対話モード)や、一つ前のフィルタを繰り返すモードにおいて、われわれはgimp_get_data()関数で利用された値を取得しようとした。この関数は一番最初に入力パラメータとして一意なデータ識別子を提供してくれる。たいてい、プロシージャ名を使う。

最終的には、対話モードで、オプションを修正するためのグラフィカルインターフェイスを構築するための数行を追加した。

static void run (const gchar *name, gint nparams, const GimpParam  *param,
                      gint *nreturn_vals, GimpParam **return_vals) { ... }

_ グラフィカルなインターフェイス

私は、GTK+プログラミングの詳細は詳しくない、GTK+プログラミングについては他の素晴らしいサイトを参照してくれ。まず最初に試したのは非常に単純なものだ。GIMPのウィジェットユーティリティを使い、ヘッダーを持つウィンドウをGimpDialogで作成し、数値を制御する(GtkAdjustmentと関連付けられた)GtkSpinButtonも作り、そしてGtkFrameでラベルを作成した。

以下のパートでは、このような作業がどれだけ簡単にできるかを示すために、パラメターの効果をリアルタイムに示すプレビューダイアログを追加する。

最終的に、Gladeのツリー表示パネルで以下のように表示される程度になった。

Glade tree

GIMP2.2で、GNOMEヒューマンインターフェイスガイドラインに沿って、首尾一貫した振る舞いを行うように考慮された、パラメータを伴うウィジェットが存在する。GimpPreviewは2.2から現れた、とりあえずそれを使わない版を作ってみる。

Blur Dialog
static gboolean blur_dialog (GimpDrawable *drawable) { ... }

_ GimpPreviewの追加

GimpPreviewを追加するのは本当に簡単だ。最初に、gimp_drawable_preview_new()関数を使ったGtkWidgetを作成する。その時、無効化シグナル(訳者注)をこの関数に設定し、プレビューが更新される度にblur()関数を呼ぶようにする。また、MyBlurValsにプレビューが有効かどうかを覚えておくための第二引数を追加する。

簡易的なプレビューの更新方法とは、プレビューパラメータをblur()関数内に追加することです。そして、プレビュー引数がNULLでないなら、限定的にGimpPreviewを行う。そして、run()関数からblur()関数を呼ぶ場合、プレビューパラメータはNULLに設定する。

限定的なGimpPreviewを行う為に、gimp_preview_get_position()とgimp_preview_get_size()を利用する。これで表示されている箇所だけにぼかし処理をかけることができる。

最適な方法に到達するには、コードのいくらかを調整しないといけない。例えば、プレビュー生成時はプログレスバーの更新は必要ないし、GimpPixelRgn初期化時、タイルキャッシュがコアに送り返さないようにさせる。

最終的に、gimp_drawable_preview_draw_region()によって更新されたプレビューを表示させる。プラグインの効果をリアルタイムに表示するダイアログボックスを得られる。さらには、GIMPコアのおかげで既にこのプラグインは選択領域も考慮できているからだ。

改良されたダイアログ 選択領域のぼかし
static void blur (GimpDrawable *drawable, GimpPreview  *preview) { ... }
static gboolean blur_dialog (GimpDrawable *drawable) { ... }

タイルキャッシュ処理UIプレビュー処理についてはそれぞれのコードを見てくれ。

_ まとめ

この記事では、我々はいくつかのGIMPプラグインの側面を基本的な概念を見てきた。単純なアルゴリズムによる画像データの処理を行ってきた、そしてパフォーマンス問題をどのようにして避けるべきなのかという道を示した。最終的に我々はアルゴリズムを一般化し、パラメータを追加し、GIMPウィジェットを良いユーザインターフェイスにするために使った。

_ 謝辞

この記事を書いている間中、私を助けてくれた妻AnneとDavid Odinへ感謝する。

_ [gimp] ぐっちゃぐちゃ

体裁がぐっちゃぐちゃなんで、どうにかしたい。っていうかやっぱり、翻訳支援ツールとか使ってみるべきだった。後、語尾もですます調にするべきだったなぁ。

さて、次はどうするべか。ってせっかく訳したんやし何かPlug-inを作ってみんとなぁ、調べたことが無駄になる...。

_ [gimp] 「invalidated signal」について

gimp_drawable_preview_new()では、invalidated signalというシグナルが定義されていて、最初何の意味かさっぱりわからなかった。

「無効化されたシグナル」とか直訳して、”シグナルを無効化する関数があるんかなぁ”とか思ったけども、どうやら違うらしい。っていうかそれじゃあ意味が通らないし。

invalidateはGUIプログラミングでupdateやrepaintと同じ再描画に関する信号としてよく使われる名前のようで、「描画した効果がなくなった」ことを示すシグナルだということでした。windowsでのゲームプログラミングの記事でその名前が確認できました。

というわけで、上記の文章でinvalidated signalが表れた箇所は、再描画の必要がある際、invalidated signalが発行されるとblur()関数を呼ぶようにしている。

_ [life] アームレストの高さ調節は必要だったかどうか

アーロンチェアBタイプスタンダード

土曜日に机、棚よりも一足早くやってきました、アーロンチェア。一番安い、スタンダードですが、大方問題無し。アームレストの高さ調節がスタンダードではできないので、腕の行き場所に悩みました。しかし、思いっきりリクライニングさせてキーボードに手を伸ばすとそもそもアームレスとが机の下に隠れる上、腕が机に完全に乗っかってしまったので問題解決。

前傾姿勢ではないので座っている間、足に全然力がかかりません。おかげで、立ち上がるとき体を重く感じます(何

_ [opensource][translation] OSC2006の翻訳BOFより OSSにおける翻訳

現状まぁそんなもんなんだろうなぁって感じはするけど、「一般的にOSS翻訳は軽視されており」ってのはどうなんだろ。debianやFedoraでは翻訳に関するMLがあったりするし、GNOME、KDE等にしても翻訳が軽く見られているなんて思えないんだけどなぁ。まぁ思えないってだけで中がほんとはどうなのか見たことないからなんともいえんが。

この発表をされてる樋口さんってSunで翻訳スタイルガイドを策定された人かな。ここんとこオープンソースな翻訳支援ツールもいくつかあるんで、これから試してみたい。

いつまでも質の低い和訳を垂れ流す訳にもいかんしのぅ。