[英]How to change bitmap from VCL.Graphics.TBitmap to FMX.Graphics.TBitmap
I'm trying to show an image taken with a camera on a multi-device form with paintbox after processing it with opencv. However, cvImage2Bitmap returns VCL.Graphics.TBitmap.在使用 opencv 处理后,我试图在带有 paintbox 的多设备形式上显示用相机拍摄的图像。但是,cvImage2Bitmap 返回 VCL.Graphics.TBitmap。 So I need to convert this to FMX.Graphics.TBitmap.
所以我需要将其转换为 FMX.Graphics.TBitmap。
unit xml_cam2;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, VCL.Graphics, FMX.Dialogs, FMX.ScrollBox,
FMX.Memo, FMX.Objects, FMX.Controls.Presentation, FMX.StdCtrls,
ocv.highgui_c,
ocv.core_c,
ocv.core.types_c,
ocv.imgproc_c,
ocv.imgproc.types_c,
ocv.utils;
type
TForm1 = class(TForm)
Button1: TButton;
OpenDialog1: TOpenDialog;
PaintBox1: TPaintBox;
Memo1: TMemo;
procedure PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
procedure FormCreate(Sender: TObject);
private
capture: pCvCapture;
frame: pIplImage;
procedure OnIdle(Sender: TObject; var Done: Boolean);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
var
Bitmap, PaintBoxBitmap: FMX.Graphics.TBitmap;
{$R *.fmx}
procedure TForm1.FormCreate(Sender: TObject);
begin
capture := cvCreateCameraCapture(CV_CAP_ANY);
if Assigned(capture) then
Application.OnIdle := OnIdle;
end;
procedure TForm1.OnIdle(Sender: TObject; var Done: Boolean);
begin
if Assigned(capture) then
begin
frame := cvQueryFrame(capture);
if Assigned(frame) then
begin
Bitmap := cvImage2Bitmap(frame);
//cvImage2Bitmap returns VCL.Graphics.TBitmap
end;
end;
Memo1.Lines.Add(IntToStr(Bitmap.Width));
Memo1.Lines.Add(IntToStr(Bitmap.Height));
if (PaintBoxBitmap = nil) then
PaintBoxBitmap := FMX.Graphics.TBitmap.Create;
PaintBoxBitmap.Assign(Bitmap);
Invalidate;
Bitmap.Free;
end;
procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
begin
if Assigned(PaintBoxBitmap) then
PaintBox1.Canvas.DrawBitmap(PaintBoxBitmap, PaintBox1.ClipRect, PaintBox1.ClipRect, 1);
Memo1.Lines.Add('b');
end;
end.
If you know any other efficient way to show iplimage to paintbox, please let us know.如果您知道向 paintbox 显示 iplimage 的任何其他有效方法,请告诉我们。
There are a couple of ways to do this.有几种方法可以做到这一点。 One way is to write the VCL bitmap to a TStream and then read it into the FMX bitmap. However, you can't convert the other way like that and it may be quite slow.
一种方法是将 VCL bitmap 写入 TStream,然后将其读入 FMX bitmap。但是,您不能像那样转换另一种方式,它可能会很慢。 I prefer to use Scanlines to convert between one and the other.
我更喜欢使用扫描线在一个和另一个之间进行转换。 In my code below I'm using 24 bit VCL bitmaps because I've found that the Windows API prefers these (AVIFile32 for example).
在我下面的代码中,我使用 24 位 VCL 位图,因为我发现 Windows API 更喜欢这些(例如 AVIFile32)。 Both bitmaps need to be created before calling the procedures.
两个位图都需要在调用过程之前创建。 Of course you need to be creating an FMX application for Windows and include VCL.Graphics in your uses.
当然,您需要为 Windows 创建一个 FMX 应用程序,并在您的使用中包含 VCL.Graphics。 Any transparency in the FMX bitmap will be lost when converting to a 24 bit VCL bitmap.
转换为 24 位 VCL bitmap 时,FMX bitmap 中的任何透明度都将丢失。
Convert 24 bit VCL bitmap to FMX bitmap将 24 位 VCL bitmap 转换为 FMX bitmap
procedure VCLtoFMX_Bitmap(const VCLBmp : VCL.Graphics.TBitmap ; out FMXBmp : FMX.Graphics.TBitmap);
var
bData : TBitmapData;
x, y : Integer;
pfmxbyte, pvclbyte : PByte;
begin
VCLBmp.PixelFormat := pf24bit;
FMXBmp.SetSize(VCLBmp.Width, VCLBmp.Height);
FMXBmp.Map(TMapAccess.ReadWrite, bdata);
try
for y := 0 to FMXBmp.Height - 1 do begin
pfmxbyte := bdata.GetScanline(y);
pvclbyte := VCLBmp.Scanline[y];
for x := 0 to FMXBmp.Width - 1 do begin
pfmxbyte^ := pvclbyte^; Inc(pvclbyte); Inc(pfmxbyte);
pfmxbyte^ := pvclbyte^; Inc(pvclbyte); Inc(pfmxbyte);
pfmxbyte^ := pvclbyte^; Inc(pvclbyte); Inc(pfmxbyte);
pfmxbyte^ := $FF; Inc(pfmxbyte); // Full opacity
end;
end;
finally
FMXBmp.Unmap(bdata);
end;
end;
Convert FMX bitmap to 24 bit VCL bitmap将 FMX bitmap 转换为 24 位 VCL bitmap
procedure FMXtoVCL_Bitmap(const FMXBmp : FMX.Graphics.TBitmap ; out VCLBmp : VCL.Graphics.TBitmap);
var
bData : TBitmapData;
x, y : Integer;
pfmxbyte, pvclbyte : PByte;
begin
VCLBmp.PixelFormat := pf24bit;
VCLBmp.SetSize(FMXBmp.Width, FMXBmp.Height);
FMXBmp.Map(TMapAccess.Read, bdata);
try
for y := 0 to FMXBmp.Height - 1 do begin
pfmxbyte := bdata.GetScanline(y);
pvclbyte := VCLBmp.Scanline[y];
for x := 0 to FMXBmp.Width - 1 do begin
pvclbyte^ := pfmxbyte^; Inc(pvclbyte); Inc(pfmxbyte);
pvclbyte^ := pfmxbyte^; Inc(pvclbyte); Inc(pfmxbyte);
pvclbyte^ := pfmxbyte^; Inc(pvclbyte); Inc(pfmxbyte, 2);
end;
end;
finally
FMXBmp.Unmap(bdata);
end;
end;
It would be nice to move image data directly from one bitmap to the other, but doesn't seems to be an easy task.将图像数据直接从一个 bitmap 移动到另一个会很好,但似乎不是一件容易的事。
Instead, the simplest way ( as it was answered here VCL.Bitmap To FMX.Bitmap ) seems to be to save image to a memory stream and load again in FMX bitmap object.相反,最简单的方法(正如此处VCL.Bitmap To FMX.Bitmap所回答的那样)似乎是将图像保存到 memory stream 并在 FMX bitmap object 中再次加载。
This simple code just works.这个简单的代码就可以工作。 You pay the penalty for moving image data to memory to load again in the new bitmap, but seems fair in exchange for simplicity.
您支付移动图像数据到 memory 的罚款以在新的 bitmap 中再次加载,但为了简单起见似乎是公平的。
procedure TForm1.FormCreate(Sender: TObject);
var
ms : TMemoryStream;
begin
ms := TMemoryStream.Create;
try
VCL_bmp := VCL.Graphics.TBitmap.Create;
try
VCL_bmp.LoadFromFile('file.bmp');
VCL_bmp.SaveToStream(ms);
ms.Seek(0, soFromBeginning);
finally
FreeAndNil(VCL_bmp);
end;
FMX_bmp := FMX.Graphics.TBitmap.Create();
try
FMX_bmp.LoadFromStream(ms);
... do something with the image ...
finally
FreeAndNil(FMX_bmp);
end;
finally
FreeAndNil(ms);
end;
end;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.