画像拡大時のタイルを円滑化

解決


807single  2011-07-14 21:52:49  No: 40786

Delphi6パーソナル+XP
写真画像をStretchBltで拡大して見た時にタイル状のモザイクが目立つのでこれを円滑化したいと試行錯誤しています。SetStretchBltModeを試しましたがタイルは変わらずに出るためGDI+を試してみたら滑らかな画像が表示されました。しかし原画のある部分を切り出して拡大、縮小するStretchBltのような方法がわかりません。どのような方法があるのか教えてください。

//GDI+のテスト例
bmp := TGPBitmap.Create('C:\test.jpg');
  try
    w :=bmp.GetWidth  * 20; 
    h :=bmp.GetHeight * 20; 
    graphics.SetInterpolationMode(InterpolationModeNearestNeighbor);
    graphics.DrawImage(bmp, MakeRect(10, 10, w, h));
  finally
    bmp.Free;
    graphics.Free;
  end;


deldel  2011-07-15 18:10:45  No: 40787

ここの
http://www.asahi-net.or.jp/~ha3t-nkmr/deldown.htm
TNkDIBはどうでしょうか?
あの有名な中村さんのサイトです。


yama  2011-07-16 00:01:15  No: 40788

先に切り抜いて、そのあと拡大すれば良いんじゃないですかね?


807single  2011-07-16 00:28:12  No: 40789

deldel様、早速ありがとうございます。
TNkDIBをDLしてPhotoDemoを試してみましたがNKDIB.dcuが見つかりません、というメッセージが出て起動できませんでした。コード(回転やミラー)を見てみましたがどれも1ピクセルずつ色を拾って描画するように見受けられました。これでStretchBltに匹敵する速度を得ることができるのでしょうか?  

初心者のためこのソフトの持つ力がわかりませんがこれを工夫すればStretchBltみたいに使えるようになるのでしょうか?  よろしくご指導ください。


tor  2011-07-16 02:01:27  No: 40790

> しかし原画のある部分を切り出して拡大、縮小するStretchBltのような方法がわかりません。
DrawImageには何種類かオーバーロードがあって、中には転送元矩形をとるものもあったと思いますが。
http://msdn.microsoft.com/en-us/library/ms535746.aspx

graphics.DrawImage(bmp, 転送先, 転送元, UnitPixel) みたいな感じでいけませんか?


807single  2011-07-16 02:03:20  No: 40791

yamaさん、ありがとうございます。投稿が前後していて失礼しました。
転写する先の位置とサイズは指定できるわけですから確かに事前に切り取っておけばできるかもしれないと次のように試してみました。
TBitMapを切り取って準備できたのですがそれをTGPBitMapに入れる方法がわかりませんので教えてください。  初心者で申し訳ありませんがよろしくお願いします。

procedure TForm1.Button3Click(Sender: TObject);

var
  graphics : TGPGraphics;

  Jpg0: TJpegImage;
  bmp0,bmp1:tbitmap;
  tgpbmp: TGPBitmap;
  w0,h0,w1,h1: double;
  l0,t0,l1,t1: double;

begin

  Jpg0 :=TJpegImage.Create ;
  jpg0.LoadFromFile('C:\test.jpg');

  Bmp0 :=TBitmap.Create;
  Bmp0.Assign(jpg0);
  jpg0.Free;

  //BitBltで切り取ってBmp1に描画
  l0:=10; t0:=10; w0:=10; h0:=10; //原画の抜き取り位置とサイズ

  bmp1:=TBitmap.Create;
  bmp1.width:=w0;  bmp1.Height:=h0;

  BitBlt(bmp1.Canvas.Handle,0,0,w0,h0,bmp0.Canvas.Handle,l0, t0,SRCCOPY);
  bmp0.Free;

  // *********************TGPbmpにbmp1をAssignしたい*****************************

  tgpbmp := TGPBitmap.Create;
  //?????????????????????????
  bmp1.free;

  graphics := TGPGraphics.Create(Canvas.Handle);

  try

    l1:=50; t1:=50; w1:=100; h1:=100;

    graphics.SetInterpolationMode(InterpolationModeNearestNeighbor);
    graphics.DrawImage(tgpbmp, MakeRect(l1, t1, w1, h1));

  finally
    tgpbmp.Free;
    graphics.Free;
  end;

end;


807single  2011-07-16 02:25:48  No: 40792

tor様、ありがとうございます。投稿が前後したようで失礼しました。

確かにご紹介のサイトで見ると「The Graphics::DrawImage method draws a specified portion of an image at a specified location.」というのがありました。コードは(恐らく)CのようですがこれをDelphiで使えれば複雑なプロセスなしで一発で目的が達成できる可能性を感じました。Delphiで転送元のRectを指定する方法などご存知でしたら教えてください。


Mr.XRAY  2011-07-17 07:19:09  No: 40793

こんちには,Mr.XRAYです.

>転送元のRectを指定する方法などご存知でしたら教えてください。

ということですが,転送元は直接,値で設定しています.
転送先のRectは使用しました.
動作確認環境が違うので,うまくいかなかったらゴメンなさい.

動作確認環境
Windows XP(SP3) + Delphi 2010 Pro
Delphi GDI+ Library for use with Delphi 2009 (version 1.2)

//=============================================================================
//  .NET互換のGDI+ライブラリなので,そうでない場合は以下の修正が必要
//  GPGraphics : IGPGraphics; --> TGPGraphics
//  bmp        : IGPBitmap;   --> TGPBitmap
//  これらの解放処理も必要
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  GPGraphics : IGPGraphics;
  bmp        : IGPBitmap;
  DestRect   : TGPRectF;
begin
  GPgraphics := TGPGraphics.Create(Image1.Canvas.Handle);
  bmp        := TGPBitmap.Create('002.jpg');
  DestRect   := TGPRectF.Create(50, 50, 300, 400);

  //bmpのRect(50, 30, 100, 100)の範囲をDestRectに描画
  GPGraphics.DrawImage(bmp, DestRect, 50 ,30, 100, 100, UnitPixel);
end;


807single  2011-07-21 04:20:33  No: 40794

Mr.XRAY様、ありがとうございました。遅くなってしまい失礼いたしました。

ご提示いただいたコードのUsesにgdipapi,gdipobj,jpegを加え、またIGP***をTGP***に変えて何度も試しましたが  DestRect   := TGPRectF.Create(50, 50, 300, 400);  のラインで「オブジェクトまたはクラス型が必要です」とのエラーが出て進みませんでした。これはD6パーソナルが原因でしょうか?  
D6で上手くいくとありがたいのですが。


D6は原因にあらず  2011-07-21 06:48:00  No: 40795

>DestRect   := TGPRectF.Create(50, 50, 300, 400);  のラインで「オブジェクトまたはクラス型が必要です」とのエラーが出て進みませんでした。

TGPRectF はクラスではなく構造体なんだから、Create はないよ。
GDIPAPI.pasの中身を自分で確認すれば直ぐに分かるはず。
また、整数指定でよければ、TGPRectFでなく TGPRectを使えばいい。
DestRect := MakeRect(50, 50, 300, 400);


Mr.XRAY  2011-07-21 07:20:41  No: 40796

こんにちは.

>DestRect   := TGPRectF.Create(50, 50, 300, 400);  のラインで「オブジェクトまた>>はクラス型が必要です」とのエラーが出て

スミマセン.決して故意でも意地悪でもありません.
単に,ここの修正も必要なことを忘れただけです.ゴメンなさい.
やっぱり手抜きレスはいけないという見本です.


Mr.XRAY  2011-07-21 18:32:28  No: 40797

GDI+のライブラリとして  http://www.progdigy.com/?page_id=7  を使用したコードです.
Delphi 6 Pro + Windows XP(SP3) で動作確認しました.

パーソナル版を使用していると,VCL関係のソースコードが付いていませんが,
このGDI+のライブラリにはソースコードが付いています.
ソースコードの,DrawImageのところを見ると,
「DrawImageって関数はいくつもあって,いろいろな引数が使えるんだ」
ということが分かります.引数の名前や型から,おおよその動作を知ることもできます.
ソースコードの存在は強力です.活用するといいと思います.

また,このGDI+ライブラリには,デモプログラムがあります.このようなデモプログラムを
実際にコンパイルして動作させるみると,いろいろなことが見えてきます.
手間はかかりますが,「急がば回れ」ということもあります.
せっかく作者が作成したデモプログラムですので,これも活用するといいと思います.

implementation

uses GDIPAPI, GDIPOBJ;

procedure TForm1.Button3Click(Sender: TObject);
var
  GPGraphics : TGPGraphics;
  bmp        : TGPBitmap;
  DestRect   : TGPRect;
begin
  GPgraphics := TGPGraphics.Create(Image1.Canvas.Handle);
  bmp        := TGPBitmap.Create('002.JPG');
  DestRect   := MakeRect(0, 0, 800, 800);

  try
    //bmpのRect(50, 30, 100, 100)の範囲をDestRectに描画
    GPGraphics.DrawImage(bmp, DestRect, 250 ,230, 400, 400, UnitPixel);
  finally
    GPGraphics.Free;
    bmp.Free;
  end;
end;


Mr.XRAY  2011-07-21 18:37:42  No: 40798

> //bmpのRect(50, 30, 100, 100)の範囲をDestRectに描画

間違いです.コード内の(250, 230, 400, 400)のことです.


807single  2011-07-21 21:09:57  No: 40799

D6は原因にあらず 様  、Mr.XRAY様  そして皆様ありがとうごじました。
HINTを頂き、試してみましたらうまくいきました。朝から解決の結果のソースコードをまとめてここに出させていただこうとしていたら既に完璧なものをお出しいただいておりました。ありがとうございました。

DGIPAPI.PASのソースも見ましたがVB6から移行したばかりなのでかなりしきいが高く感じますが具体的に動かして慣れていこうと思いますのでよろしくお願いします。

これにちょっと書き加えて速度テストをしてみましたところ(Destが)650x650ピクセルの絵1000枚を15秒で描くことができました(PCは1.6GHz)ので速度的にも問題なく実装できるのではないかと思います。  ありがとうございました。

(既に解決のチェックをいれさせていただき、あとは工夫で何とか実装できると思いますが付随して疑問がわきましたので書かせていただきます)

try
  for i:=1 to 1000 do begin    //複数枚
       L1:=  Random(1000);
       T1:=  Random(1000);
       DestRect:=makerect(L1,T1,w1,h1);
       gpgraphics.DrawImage(bmp,destrect, L0, T0, w0-50, h0-50,UnitPixel);

  end;

  finally
    beep;
    bmp.Free;
    gpgraphics.Free;
  end;
としてForm1に描かせるとパラパラと描画するのが見えるのですが1000枚終了すると消えてしまいます。1枚だけだと消えません。  何が違うのでしょうか?


Mr.XRAY  2011-07-21 23:11:43  No: 40800

>1000枚終了すると消えてしまいます

オバケのように消えているわけではありませんよ.きっと.
w0やh0の値を変えてテストしてみてください.

それから,質問の時に提示するコードは,できるだけ,コピペして,他の人がすぐ実行できるものにするとよいのです.
そうすれば,誰かがやってくれるかも知れません.コピペしてすぐできないなら
「や〜めた」
となってしまいます.
回答する人に手間をかけさせるのか,質問する人が手間をかけるのかの違いですけどね.


Mr.XRAY  2011-07-21 23:18:34  No: 40801

>w0やh0の値を変えてテストしてみてください.

おおっと,またまた書き忘れました.他の値もです.


807single  2011-07-22 00:00:13  No: 40802

Mr.XRAY様  ありがとうございました。
W1,H1を毎回変化させてみましたら全て描画され、また1000枚目が終了した時点でも画像が残りました。理由はわかりませんが、これで連続してこのテクニックが使用できる確信が持てました。改めて御礼申し上げます。

>それから,質問の時に提示するコードは,できるだけ,コピペして,他の人がすぐ実行できるものにするとよいのです.

これは特に私のような初心者にとって重要で、Uses節に何を加えたらよいかなどで苦戦することが多いためできるだけコピペで動くものを解決時にはお示しするように心がけます。無駄なラインも含まれておりサーバーの容量を消費してしまい申し訳ありませんが今回成功したものをコピペさせていただきました。

procedure TForm1.Button6Click(Sender: TObject);
//uses
//  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
//  Dialogs, StdCtrls,gdipapi,gdipobj,jpeg, ExtCtrls;
var
  gpgraphics : TGPGraphics;
  bmp: TGPBitmap;
  DestRect:TGPrect;
  w,h,w0,h0,L0,T0,W1,H1,L1,T1,I: integer;
begin

  gpgraphics := TGPGraphics.Create(Canvas.Handle);

  bmp := TGPBitmap.Create('C:\test.jpg');
    w0 :=bmp.GetWidth;   h0 :=bmp.GetHeight;  L0:=50;  T0:=50;
    w1:=w0*5;     h1:=h0*5;    L1:=120;  T1:=100;
    DestRect:=makerect(L1,T1,w1,h1);

  try

    gpgraphics.SetInterpolationMode(InterpolationModeHighQualityBicubic);
    gpgraphics.DrawImage(bmp,destrect, L0, T0, w0-50, h0-50,UnitPixel);  //1枚だけ

    for i:=1 to 1000 do begin                                            //複数枚
       L1:=  Random(image1.Width);   T1:=  Random(image1.Height);   w1:=round(random(w0));  h1:=round(random(h0));
       DestRect:=makerect(L1,T1,w1,h1);
       gpgraphics.DrawImage(bmp,destrect, L0, T0, w0-50, h0-50,UnitPixel);

    end;

  finally
    beep;
    bmp.Free;
    gpgraphics.Free;
  end;

end;


Mr.XRAY  2011-07-22 01:04:22  No: 40803

>理由はわかりませんが、

う〜む.そのうち理解できる時がくるでしょう!
ともかく順調に行っているようですので,いいのではないかと思います.
がんばってくださいませ.


807single  2011-07-22 03:56:35  No: 40804

Mr.XRAY様
>う〜む.そのうち理解できる時がくるでしょう!
早くそうありたいと、とにかく書いて慣れたいと思います。
>ともかく順調に行っているようですので,いいのではないかと思います.
前回のコードをよく見ると型がおかしい箇所があると思いましたが何故か動いていました。正しくは
(誤)L1:=  Random(image1.Width);   T1:=  Random(image1.Height);   
(正)L1:=  Round(Random(image1.Width));   T1:=  Round(Random(image1.Height));  ではないかと思います。

ありがとうございました。


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

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






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