TBitmap画像を任意の角度に回転したTBitmap画像を生成 ~Delphiソースコード集
(参考)バイキュービック法(bicubic)で拡大縮小する(VCL)
(参考)バイキュービック法(bicubic)で拡大縮小する(FMX)
(参考)バイラテラルフィルタ(bilateral filter)で美肌に加工(VCL)
(参考)バイラテラルフィルタ(bilateral filter)で美肌に加工(FMX)
TBitmap画像の回転を使用する為のファイルの準備
本ページの下部のソースコード(URotateImageユニット)をコピーして「URotateImage.pas」ファイルを作成し、 プロジェクトフォルダ内に入れる必要があります。
プロジェクトの作成と画面設計
プロジェクトを新規作成(VCLアプリケーション)し、フォーム(Form1)にTButtonを1個、TEditを1個、TImageを2個を配置します。
Image1のPictureプロパティに、回転させたい画像を設定します。
Image1のPropotionalプロパティとStretchプロパティをTrueに設定します。
Image2のPropotionalプロパティとStretchプロパティをTrueに設定します。
すべて保存ボタンを押して、プロジェクトフォルダを作成し、ユニットとプロジェクトに名前を付けて保存します。
「URotateImage.pas」ファイルをプロジェクトフォルダに保存してください。
ソースコードの記述
TButton1をダブルクリックして、以下ソースコードを記述します。
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Imaging.jpeg, System.Math,URotateImage; type TForm1 = class(TForm) Image1: TImage; Image2: TImage; Button1: TButton; Edit1: TEdit; procedure Button1Click(Sender: TObject); private { Private 宣言 } public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var bmp1,bmp2:TBitmap; begin bmp1:=TBitmap.Create; bmp2:=TBitmap.Create; try bmp1.Assign(Image1.Picture.Graphic); //バイキュービックを使用する場合は第3引数をTrueに設定します RotateBitmap(bmp1, bmp2, StrToFloatDef(Edit1.Text,0), False); Image2.Picture.Assign(bmp2); finally bmp1.Free; bmp2.Free; end; end; end.
実行する
実行ボタンを押して実行します。(デバッグ実行でもOK)
Edit1に「15」を入力します。
Button1をクリックするとImage1画像を時計回りに15度回転した画像がImage2に表示されます。
「URotateImage.pas」ファイルのソースコード
unit URotateImage; interface uses System.Types,System.Classes,Vcl.Graphics; type TRRGB=record B,G,R:Extended;end; TRGB =record B,G,R:byte;end; TRGBArr=array[0..32767] of TRGB; PRGBArr=^TRGBArr; TRGBArrArr=array[0..32767] of PRGBArr; TMamRotateBitmapUseBicubic=(NotUseBicubic,UseBicubic); {Src:回転元 Dest:回転後 Angle:0-360度 IsUseBicubic:バイキュービックの使用} procedure RotateBitmap(Src,Dest:TBitmap;Angle:Single;IsUseBicubic:Boolean=False); implementation //バイキュービック4x4の距離による重み定数 Function MamBicubicWeight4x4(d:Single):Single; const a:Single=-0.5; //シャープの強さ -0.5(弱) ~ -1.0(強)の値を与える begin if d<0 then begin result:=0; end else if d<=1 then result:= (a+2)*d*d*d - (a+3)*d*d + 1 else if d<=2 then result:= a*d*d*d - 5*a*d*d + 8*a*d - 4*a else result:=0; end; Function MamBicubic4x4(srgb:TRGBArrArr;sbmp:TBitmap;sx,sy:Single):TRGB; var x,y,tsx,tsy:Integer; w:Single; m:array[0..3]of array[0..3]of Single; sum:Single; rrgb:TRRGB; begin //カーネルの作成 sum:=0; for y := 0 to 3 do begin for x := 0 to 3 do begin tsx:=trunc(sx)+x-1; tsy:=trunc(sy)+y-1; if (tsx>=0) and (tsx<=(sbmp.Width-1)) and (tsy>=0) and (tsy<=(sbmp.Height-1)) then begin w:=MamBicubicWeight4x4( sqrt((tsx-sx)*(tsx-sx)+(tsy-sy)*(tsy-sy)) ); m[x,y]:=w; sum:=sum+w; end else begin m[x,y]:=-256;//計算対象外 end; end; end; rrgb.R:=0; rrgb.G:=0; rrgb.B:=0; for y := 0 to 3 do begin for x := 0 to 3 do begin tsx:=trunc(sx)+x-1; tsy:=trunc(sy)+y-1; //if (tsx>=0) and (tsx<=(sbmp.Width-1)) and // (tsy>=0) and (tsy<=(sbmp.Height-1)) then if m[x,y]<>-256 then begin m[x,y]:=m[x,y]/sum; rrgb.R:=rrgb.R+srgb[tsy,tsx].R*m[x,y]; rrgb.G:=rrgb.G+srgb[tsy,tsx].G*m[x,y]; rrgb.B:=rrgb.B+srgb[tsy,tsx].B*m[x,y]; end; end; end; if rrgb.R<0 then rrgb.R:=0; if rrgb.R>255 then rrgb.R:=255; if rrgb.G<0 then rrgb.G:=0; if rrgb.G>255 then rrgb.G:=255; if rrgb.B<0 then rrgb.B:=0; if rrgb.B>255 then rrgb.B:=255; result.R:=trunc(rrgb.R); result.G:=trunc(rrgb.G); result.B:=trunc(rrgb.B); end; {Src:回転元 Dest:右回りの回転角(度) Angle:0-360度} procedure RotateBitmap(Src,Dest:TBitmap;Angle:Single;IsUseBicubic:Boolean=False); var bmp:TBitmap; //Srcをコピーしたビットマップ CosV,SinV:Single; scX,scY:Single;//元画像中心 dcX,dcY:Single;//変換後画像中心 exX,exY:Single;//変換後画像のxy座標を回転して元画像に変換した座標 x,y:Integer; dRGB,sRGB:TRGBArrArr;//スキャンライン begin if not Assigned(Src) then Exit; if not Assigned(Dest) then Dest:=TBitmap.Create; bmp:=TBitmap.Create; bmp.Assign(Src); bmp.PixelFormat:=pf24bit; dest.PixelFormat:=pf24bit; //時計回り方向で予めSin,Cosを計算しておく CosV:=Cos(-Angle*Pi/180); SinV:=Sin(-Angle*Pi/180); //変換後画像のサイズ計算 //x'=x*cos(a)-y*sin(a) y'=x*sin(a)+y*cos(a) Dest.Width :=Round(Abs(bmp.Width*CosV)+Abs(-bmp.Height*Sinv)); Dest.Height:=Round(Abs(bmp.Width*SinV)+Abs(bmp.Height*CosV)); //元画像の中心 scX:=bmp.Width/2; scY:=bmp.Height/2; //変換後画像の中心 dcX:=Dest.Width/2; dcY:=Dest.Height/2; //予め元画像のスキャンラインをすべて取得 for y := 0 to bmp.Height-1 do sRGB[y]:=bmp.ScanLine[y]; //予め変換後画像のスキャンラインをすべて取得 for y := 0 to dest.Height-1 do dRGB[y]:=Dest.ScanLine[y]; for y := 0 to Dest.Height-1 do begin for x := 0 to Dest.Width-1 do begin //変換後画像の座標(dx,dy)から元画像の座標(exX,exY)を計算 //x'=x*cos(a)-y*sin(a) y'=x*sin(a)+y*cos(a) exX:=((x-dcX)*CosV-(y-dcY)*SinV)+scX; exY:=((x-dcX)*SinV+(y-dcY)*CosV)+scY; //元画像座標が有効範囲である場合 if(0<=exX)and(exX<bmp.Width)and(0<=exY)and(exY<bmp.Height)then begin if IsUseBicubic then dRGB[y,x]:=MamBicubic4x4(sRGB,bmp,exX,exY) else dRGB[y][x]:=sRGB[Trunc(exY)][Trunc(exX)]; end else begin //元画像にない箇所については黒色とする dRGB[y][x].R := 0; dRGB[y][x].G := 0; dRGB[y][x].B := 0; end; end; end; end; end.