色の取得と描画の高速化

解決


一休  2011-04-26 02:02:49  No: 40481

D6パーソナルズで自分用の画像処理ソフトを試作しています。
VBから移行してファイル読み込みとかの速度の飛躍的高速化やGDI+の簡便さなどに満足していましたが画像回転などで色の取得、描画では4000x3000ピクセル程度になると忘れたころに描画されるほど低速なのでどうしたら高速になるか教えてください。
コード例は下記のようなものですが
(1)Delphiを使って高速化できるのでしょうか?
(2)画像ソフトでは大きな画像でも数秒で回転しますがこれはどのような言語、あるいは手法で作られているのでしょうか?  よろしくお願いします。

cx:=w1 div 2;
cy:=h1 div 2;

for y:=0 to h1-1 do begin
    for x:=0 to w1-1 do begin
      tx:=round((x-cx)*cs-(y-cy)*ss+cx);
      ty:=round((x-cx)*ss+(y-cy)*cs+cy);

      form1.image2.canvas.pixels[tx,ty]:=bmp1.canvas.pixels[x,y];
      form1.image2.canvas.pixels[tx,ty+1]:=bmp1.canvas.pixels[x,y];

    end;
end;


tor  2011-04-26 04:05:10  No: 40482

Canvas.Pixelsは1ピクセルずつ読み書きする関数で非常に遅いので
画像の内容をいじりたい場合はTBitmap.ScanLineで直接メモリアクセスするのが普通です。

また「元の画像の各ピクセルを、回転後の座標に移す」というやり方だと
同じ座標に重ねて描画したり、逆に何も描画されない「隙間」ができたりして品質がよろしくないです。
(同じ点を2回ずらして描画しているのも、その隙間を埋めようとしてのことだと思いますが)
こういう場合考え方を逆にして「回転後の各ピクセルが、元の画像のどこにあたるか」を調べて移していくといいです。

// (tx, ty) は回転後の画像上の座標、(tw, th) は回転後の大きさ
for ty := 0 to th - 1 do begin
  for tx := 0 to tw - 1 do begin
    // ここで(tx, ty)を原点周りに逆方向に回転させて(x, y)を求める
    <回転後のビットマップ>.ScanLine[ty][tx] := <元のビットマップ>.ScanLine[y][x];
  end;
end;


一休  2011-04-27 02:14:28  No: 40483

tor様、アドバイスをありがとうございました。
いろいろとトラブルで進みませんでしたが何とか解決でき驚くほど高速で動くようになりDelphiの威力に改めて魅力を感じました。ありがとうございました。

昨晩からご回答にありましたScanlineを真似してやってみましたが<回転後のビットマップ>.ScanLine[ty][tx] := <元のビットマップ>.ScanLine[y][x];のところでエラーがでてしまいました。ScanLineというキーワードでWEBサイトを調べてみましたら画像回転のサンプルがあったので試してみましたが、サンプル通りだと隙間のできる絵となってしまいました。そこでアドバイスにあったように「考え方を逆にして「回転後の各ピクセルが、元の画像のどこにあたるか」を調べて移していくといいです。」を参考に改造したら綺麗に回転できました。有益なアドバイスに改めて御礼申し上げます。

PS
参考までに質問させていただきますがScanLine[tx][ty]のように2次元的な使用は可能なのでしょうか?


tor  2011-04-27 05:06:24  No: 40484

> 参考までに質問させていただきますがScanLine[tx][ty]のように2次元的な使用は可能なのでしょうか?  

私の例は端折って書いてありますが、ヘルプのScanLineのところを見ればちゃんとしたコード例が載っていると思います。
ScanLine[y]が返すのは型なしのポインタなので、要素にアクセスするには型付きのポインタに変換する必要があります。
ビットマップの形式(PixelFormat)によって型が違うので注意してください。

// 例: 32bitフォーマットの場合
// DWORD配列を指すポインタ型を定義する
type
  TDWArray = array[0..0] of DWORD;
  PDWArray = ^TDWArray;
var
  pd: PDWArray;
...
pd := Bitmap.ScanLine[y];
pd[x] := value;
// PDWArray(Bitmap.ScanLine[y])^[x] と書いても良い


一休  2011-04-27 19:59:59  No: 40485

tor様、ご丁寧な説明をありがとうございました。
今まではついついImageとかFormなどの見えるオブジェクトのCanvasをいじることしかやっておりませんでしたので今回のScanLineとそれに関連する一連のポインター関連の扱いは初めてのことでした。まだ見よう見まねでしかありませんが取りあえずは目的が達成できて大変感謝しています。一歩踏み込むとものすごいことができるものだと感じました。これを機に少し踏み込むきっかけとなりました。お礼まで。


私は中国の午前  2011-04-27 23:24:35  No: 40486

私は中国の午前
bbs.csdn.net
bbs.phpchina.com


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加