画像のミラー処理
画像のミラー処理は、簡単なのですが、一応サンプルプログラムを作成してみました。
左図は、水平方向のミラー処理です。
垂直方向のミラー処理も可能です。
処理は単純にピクセル単位で、左右、又は、上下を入れ替えるだけです。
ファイルをTBitmapに読み込んで、ミラー用TBitmapに左右、又は、上下を入れ替えるて書き込みます。
ピクセル単位で処理をすると、処理速度が遅いので、ScanLineを使用します。
更に、TBitmapを24ビットフォーマットし、TRGBtriple レコードのポインターを使用して、3バイト単位で処理ができるようにします。
マウスで範囲を指定すれば、指定した範囲だけの、左右、又は、上下のミラー処理が可能です。
実際には、まず全体を、そのままコピーして、その後、指定された範囲のミラー処理をします。
サンプルプログラムには組み込んでありませんが、縦と横のミラーを同時に行えば、180度回転した画像となります。
サンプルプログラム
本サンプルプログラムには、画像の保存はありません、保存処理のある画像処理プログラムもあるので、必要であれば、それを参照して追加してください。
保存処理は、アンシャープマスキング、画像の回転のプログラムに組み込んであります。
unit MirroringMain; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls, Vcl.ExtDlgs, system.Types; type TForm1 = class(TForm) FileOpen: TButton; Image1: TImage; Image2: TImage; OpenPictureDialog1: TOpenPictureDialog; HMirroring: TButton; VMirroring: TButton; RadioGroup1: TRadioGroup; procedure FormCreate(Sender: TObject); procedure FileOpenClick(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure HMirroringClick(Sender: TObject); procedure VMirroringClick(Sender: TObject); procedure Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); procedure Image1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); private { Private 宣言 } procedure Allimagecopy; procedure HImgeCopy; procedure VImgeCopy; public { Public 宣言 } end; var Form1: TForm1; type TPrgbarry = array[0..0] of Trgbtriple; // 24ビットカラーレコード 32ビット用はTRGBQuadArray Prgbarry = ^TPrgbarry; // ポインターの宣言 // 配列のポインターであればよいので、配列の大きさは1 [0..0]にします。 implementation {$R *.dfm} const OpenFileFilter = '画像ファイル|*.png;*.jpg;*.gif;*.bmp;*.tif;*.ico;*.wdp'+ '|*.png|*.png' + '|*.jpg|*.jpg' + '|*.gif|*.gif' + '|*.bmp|*.bmp' + '|*.tif|*.tif' + '|*.ico|*.ico' + '|*.wdp|*.wdp'; SaveFileFilter = '画像ファイル|*.png;*.jpg;*.gif;*.bmp;*.tif;*.wdp' + '|*.png|*.png' + '|*.jpg|*.jpg' + '|*.gif|*.gif' + '|*.bmp|*.bmp' + '|*.tif|*.tif' + '|*.wdp|*.wdp'; ImageHW = 384; // 表示枠サイズ var GHeight, GWidth : integer; Vrect : Trect; InBitmap : TBitmap; OutBitmap : TBitmap; Origin : TPoint; EndPoint : TPoint; Drawing : Boolean; NewMove : Boolean; OX, EX, OY, EY : Integer; IHeight : Integer; IWidth : Integer; SWidth : integer; // サンプル画像幅 SHeight : integer; // サンプル画像高さ procedure TForm1.FileOpenClick(Sender: TObject); var WIC : TWICImage; InFilename : String; begin HMirroring.Enabled := False; VMirroring.Enabled := False; RadioGroup1.Enabled := False; VRect := Rect(0, 0, Image1.Width, Image1.Height); Image1.Canvas.Brush.Style := bsSolid; Image1.Canvas.Brush.Color := clBtnface; Image1.Canvas.FillRect(VRect); // Canvas 画像消去 Image2.Canvas.Brush.Style := bsSolid; Image2.Canvas.Brush.Color := clBtnface; Image2.Canvas.FillRect(VRect); // Canvas 画像消去 OpenPictureDialog1.Filter := OpenFileFilter; // ファイルオープンフィルターの設定 if OpenPictureDialog1.Execute then // ファイルが指定されたら begin WIC := TWICImage.Create; // TWICImageの生成 try InFilename := OpenPictureDialog1.FileName; // ファイル名の取得 WIC.LoadFromFile(InFilename); // 画像の読み込み GHeight := WIC.Height; // 画像高さ取得 GWidth := WIC.Width; // 画像幅 IWidth := ImageHW; // 出力先イメージ1の幅 IHeight := ImageHW; // 出力先イメージ1の高さ if GHeight <= GWidth then // 縦横比により出力サイズ設定 IHeight := Round(IWidth * GHeight / GWidth) else IWidth := Round(IHeight * GWidth / GHeight); Image1.Width := IWidth; Image1.Height:= IHeight; Image1.Picture.Bitmap.SetSize(IWidth, IHeight); Image2.Width := IWidth; Image2.Height:= IHeight; Image2.Picture.Bitmap.SetSize(IWidth, IHeight); VRect := Rect(0, 0, IWidth, IHeight); // 出力枠設定 Image1.Canvas.StretchDraw(VRect, WIC); // 出力枠に変倍出力 InBitmap.Width := GWidth; InBitmap.Height := GHeight; InBitmap.Canvas.Draw(0, 0, WIC); // DrawでInBitmapに入力画像設定フォーマット24ビットに変換 OutBitmap.Width := GWidth; OutBitmap.Height := GHeight; finally WIC.Free; // TWICImage 解放 end; end else exit; HMirroring.Enabled := True; VMirroring.Enabled := True; RadioGroup1.Enabled := True; end; procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin Drawing := True; with Image1.Canvas do begin // マウスダウンフラグセット Brush.Color := clWhite; Brush.Style := bsClear; Pen.Color := ClBlack; Pen.Width := 2; end; Origin := Point(X, Y); // 座標の保存 NewMove := True; end; procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin if Drawing and (Shift = [ssLeft]) then begin With Image1.Canvas do begin Pen.Mode := pmNotXor; // ペンモード Not Xor if not NewMove then begin // 最初の移動には消去がありません MoveTo(Origin.X, Origin.Y); // 前の線消去 LineTo(EndPoint.X, Origin.Y); LineTo(EndPoint.X, EndPoint.Y); LineTo(Origin.X, EndPoint.Y); LineTo(Origin.X, Origin.Y); // 前の線消去 end; EndPoint := point(X, Y); // 新しい座標を保存 MoveTo(Origin.X, Origin.Y); // 新しい線の描画 LineTo(EndPoint.X, Origin.Y); LineTo(EndPoint.X, EndPoint.Y); LineTo(Origin.X, EndPoint.Y); LineTo(Origin.X, Origin.Y); // 新しい線の描画 NewMove := False; Pen.Mode := pmCopy; end; // ペンモード通常に end; end; procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var TMPI : integer; Scal : Double; begin if not Drawing or NewMove then exit; Image1.Canvas.Pen.Mode := pmNotXor; // ペンモード Not Xor Image1.Canvas.MoveTo(Origin.X, Origin.Y); // 前の線消去 Image1.Canvas.LineTo(EndPoint.X, Origin.Y); Image1.Canvas.LineTo(EndPoint.X, EndPoint.Y); Image1.Canvas.LineTo(Origin.X, EndPoint.Y); Image1.Canvas.LineTo(Origin.X, Origin.Y); // 前の線消去 Image1.Canvas.Pen.Mode := pmCopy; // ペンモード通常に NewMove := False; Drawing := False; // 範囲指定正規方向へ変換 if Origin.X > EndPoint.X then begin TMPI := Origin.X; Origin.X := EndPoint.X; EndPoint.X := TMPI; end; if Origin.Y > EndPoint.Y then begin TMPI := Origin.Y; Origin.Y := EndPoint.Y; EndPoint.Y := TMPI; end; IWidth := Image1.Width; IHeight := Image1.Height; // マウスポイントを元画像座標に変換 Scal := GWidth / IWidth; // 元画像と表示画像の倍率 OX := Round(Origin.X * Scal); EX := Round(EndPoint.X * Scal); OY := Round(Origin.Y * Scal); EY := Round(EndPoint.Y * Scal); if OX < 0 then OX := 0; if EX > GWidth - 1 Then EX := GWidth - 1; if OY < 0 then OY := 0; if EY > GHeight - 1 then EY := GHeight - 1; // 範囲サイズ設定 SWidth := EX - OX; SHeight := EY - OY; Allimagecopy; // 全Copy case RadioGroup1.ItemIndex of 0: HImgeCopy; 1: VImgeCopy; end; end; procedure TForm1.Allimagecopy; // 全Copy var I, J : integer; OutPrgbarray : Prgbarry; InpPrgbarray : Prgbarry; begin for I := 0 to GHeight - 1 do begin InpPrgbarray := InBitmap.ScanLine[I]; OutPrgbarray := OutBitmap.ScanLine[I]; for J := 0 to GWidth - 1 do begin OutPrgbarray[J] := InpPrgbarray[J]; // 単純コピー end; end; Image2.Canvas.StretchDraw(VRect, OutBitmap); // 出力枠に変倍出力 end; procedure TForm1.HImgeCopy; // 指定範囲横方向ミラーコピー var I, J : integer; OutPrgbarray : Prgbarry; InpPrgbarray : Prgbarry; begin for I := OY to EY do begin InpPrgbarray := InBitmap.ScanLine[I]; OutPrgbarray := OutBitmap.ScanLine[I]; for J := OX to EX do begin OutPrgbarray[OX + EX - J] := InpPrgbarray[J]; // 横方向の配列を逆にしてコピー end; end; Image2.Canvas.StretchDraw(VRect, OutBitmap); // 出力枠に変倍出力 end; procedure TForm1.VImgeCopy; // 指定範囲縦方向ミラーコピー var I, J : integer; OutPrgbarray : Prgbarry; InpPrgbarray : Prgbarry; begin for I := OY to EY do begin InpPrgbarray := InBitmap.ScanLine[I]; OutPrgbarray := OutBitmap.ScanLine[OY + EY - I]; // 縦方向の配列を逆にしてコピー for J := OX to EX do begin OutPrgbarray[J] := InpPrgbarray[J]; end; end; Image2.Canvas.StretchDraw(VRect, OutBitmap); // 出力枠に変倍出力 end; procedure TForm1.HMirroringClick(Sender: TObject); // 横方向ミラー var I, J : integer; M1Width : integer; OutPrgbarray : Prgbarry; InpPrgbarray : Prgbarry; begin M1Width := GWidth - 1; for I := 0 to GHeight - 1 do begin InpPrgbarray := InBitmap.ScanLine[I]; OutPrgbarray := OutBitmap.ScanLine[I]; for J := 0 to M1Width - 1 do begin OutPrgbarray[J] := InpPrgbarray[M1Width - J]; // 横方向の配列を逆にしてコピー end; end; Image2.Canvas.StretchDraw(VRect, OutBitmap); // 出力枠に変倍出力 end; procedure TForm1.VMirroringClick(Sender: TObject); // 縦方向ミラー var I, J : integer; M1Height : integer; OutPrgbarray : Prgbarry; InpPrgbarray : Prgbarry; begin M1Height := GHeight - 1; for I := 0 to M1Height do begin InpPrgbarray := InBitmap.ScanLine[M1Height - I]; // 縦方向の配列を逆にしてコピー OutPrgbarray := OutBitmap.ScanLine[I]; for J := 0 to GWidth - 1 do begin OutPrgbarray[J] := InpPrgbarray[J]; end; end; Image2.Canvas.StretchDraw(VRect, OutBitmap); // 出力枠に変倍出力 end; procedure TForm1.FormCreate(Sender: TObject); begin Image1.Width := ImageHW; Image1.Height := ImageHW; Image2.Width := ImageHW; Image2.Height := ImageHW; InBitmap := TBitmap.Create; OutBitmap := TBitmap.Create; InBitmap.PixelFormat := pf24bit; // 24ビットカラーに設定 OutBitmap.PixelFormat := pf24bit; // 24ビットカラーに設定 RadioGroup1.Caption := '範囲選択'; end; procedure TForm1.FormDestroy(Sender: TObject); begin InBitmap.Free; OutBitmap.Free; end; end.