LoginSignup
2
1

More than 1 year has passed since last update.

GDI+(windowsのAPI)の行列演算を使って容易に画像の変身(簡易モーフィング)を実現する

Last updated at Posted at 2023-01-28

実行結果

Animation3.gif

解説

画像は点(ドット)の集まりで構成されていて、その各ドットの色要素が(RGBα)の場合、α(透過度)を徐々に変化させれば徐々に透けていく絵が表示できます。
2つの画像を用意して、一方はαの値を255→0(無透過→透明)、もう一方を0→255(透明→無透過)に徐々に変化させ、この2つの画像を重ね合わせれば、画像の変身(簡易モーフィング)が実現します。

GDI+を使えば、画像を構成するすべてのドットのα値の変更を簡単に実現させることができます。
その方法は以下の行列式の演算で行えます。

\begin{pmatrix}
R,& G,& B,& α,& 1
\end{pmatrix}
\begin{pmatrix}
1,&0,&0,&0,&0 \\
0,&1,&0,&0,&0 \\
0,&0,&1,&0,&0 \\
0,&0,&0,&透過率,&0 \\
0,&0,&0,&0,&1 \\
\end{pmatrix}
=>
\begin{pmatrix}
R,& G,& B,& α \times 透過率,& 1
\end{pmatrix}

詳しくは以下の記事が参考になります。

プログラム

Delphiでコーディングしたら以下のようになります
参考元

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, GDIPOBJ, GDIPAPI;

type
  TForm1 = class(TForm)
    Image1: TImage;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Image1Click(Sender: TObject);
  private
    FGPBitmap: array[1..2] of TGPBitmap;
    procedure Image1Paint(Count: Integer);
  public
    { Public 宣言 }
  end;

const
  FILE1 = 'z:\file1.jpg';
  FILE2 = 'z:\file2.jpg';
  WAITTIME = 10; //1回ごとのウェイトタイム(ミリ秒)
  TIMES = 100;   //変身までに何回描画するか

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FGPBitmap[1] := TGPBitmap.Create(FILE1);
  FGPBitmap[2] := TGPBitmap.Create(FILE2);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(FGPBitmap[1]);
  FreeAndNil(FGPBitmap[2]);
end;

procedure TForm1.Image1Click(Sender: TObject);
begin
  for var i := 0 to TIMES do begin
    Image1Paint(i);
    Image1.Repaint;
    Sleep(WAITTIME);
  end;
  var swap := FGPBitmap[1];
  FGPBitmap[1] := FGPBitmap[2];
  FGPBitmap[2] := swap;
end;

procedure TForm1.Image1Paint(Count: Integer);
const
  CM_DEFAULT : TColorMatrix = (
      (1, 0, 0, 0, 0),
      (0, 1, 0, 0, 0),
      (0, 0, 1, 0, 0),
      (0, 0, 0, 1, 0),
      (0, 0, 0, 0, 1));
var
  gcanvas: TGPGraphics;
  cm: TColorMatrix;
  ia: TGPImageAttributes;
begin
  var w := FGPBitmap[1].GetWidth;
  var h := FGPBitmap[1].GetHeight;

  ia := TGPImageAttributes.Create;
  gcanvas := TGPGraphics.Create(Image1.Canvas.Handle);

  cm := CM_DEFAULT;

  cm[3,3] := Count / TIMES;
  ia.SetColorMatrix(cm);
  gcanvas.DrawImage(FGPBitmap[1],
                    MakeRect(0, 0, w, h),
                    0, 0,
                    w, h,
                    UnitPixel,
                    ia);

  cm[3,3] := 1 - cm[3,3];
  ia.SetColorMatrix(cm);
  gcanvas.DrawImage(FGPBitmap[2],
                    MakeRect(0, 0, w, h),
                    0, 0,
                    w, h,
                    UnitPixel,
                    ia);

  FreeAndNil(gcanvas);
  FreeAndNil(ia);
end;
end.

Githubにソースがあります。

あとがき

GDI+を使えば、複雑な画像の回転・拡大縮小などを行うアフィン変換も容易に実現できるようです。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1