【问题标题】:writing/reading bitmaps to a tfilestream将位图写入/读取到 tfilestream
【发布时间】:2012-12-16 02:50:09
【问题描述】:

我已经搜索和搜索,似乎无法找到任何描述我希望在 delphi 代码中执行的操作。溶液有时很接近,但不够接近,我无法弄清楚。所以我在这里问..

我有许多位图,我正在从屏幕截图中提取。我一直在做的是保存到bitmaps_001.bmp,但是它需要很多存储空间,所以我升级了例程以保存为bitmaps_001.png,这样可以节省更多的空间,但是现在我想保存到一个文件中,一个 tfilestream,并使用 tprogressbar 从中读取,我可以在屏幕上显示图像时向左/向右拖动。

基本上,我正在尝试完成以下操作:

procedure SaveBMPtoStream(st: tfilestream; bmp: tbitmap);
procedure ReadBMPfrStream(st: tfilestream; bmp: tbitmap; bnum: integer);

到目前为止,代码(如下)按原样工作,(它在按下 t 按钮时写入和读取一个位图图像)但我只能写入一个位图图像。我需要将每个会话所需的尽可能多的图像实时写入 tfilestream,可能使用 ttimer 控件并让它写入尽可能多的图像,直到我按下停止按钮。我该怎么做才能修改下面的代码来解决这个问题?谢谢。

我正在运行 windows xp,连接到带有 NTFS 文件系统的外部 usb3.0 1tb 驱动器。

type
  TMS = TFileStream; 
var
  MS:  TMS; 
  pos: int64;   // bnum for 0-99,999 images.
  sz:  integer; // size of the image/stream ?

//write bitmaps to stream
procedure SaveBMPtoStream(ms: TMS; Bmp: TBitmap; bnum: integer);
begin
  // create (or append to) stream
  if fileexists('d:\streams\s.stm') then MS := TFileStream.Create('d:\streams\s.stm', fmOpenReadWrite)
    else MS := TFileStream.Create('d:\streams\s.stm', fmCreate);
  //sz:=MS.Size; pos:=ms.Position;
  bmp.SaveToStream(MS); 
  // free stream
  ms.free;
end;

//read bitmaps from stream
procedure ReadBMPfrStream(ms: TMS; Bmp: TBitmap; bnum: integer);
begin
  // open stream.
  MS := TFileStream.Create ('d:\streams\s.stm', fmOpenReadWrite); 
  // read in bitmap from stream
  //sz:=MS.Size; pos:=ms.Position;
  bmp.LoadFromStream(MS);
  // free stream
  ms.free;
end;

【问题讨论】:

  • 您遇到了一些复制/粘贴错误 (?),like type TMS: TFileStream;。 ;) 如果信息不必在会话中保留,您可以保留一个单独的位图编号索引 - 流位置/大小并将该部分复制到临时流中以供读取。否则,您可以从流中读取位图信息标头以将流推进到请求的索引,然后再次复制,或者在应用程序开始时以相同的方式构建索引。但后者的工作量更大。
  • 哦,那是一个错字。现在更正了。谢谢。
  • 您在哪里看到 (//sz:=MS.Size; pos:=ms.Position;) 我在尝试时正在研究各个地方的部分代码 sn-ps。这一切都让我感到困惑,我似乎无法弄清楚。它在我头上,但我需要弄清楚这一切,所以几个月后我仍然在做。这就是为什么我来这里,以获得答案。但仍然没有。所以,我会继续寻找。感谢您的尝试。
  • 我刚刚意识到我遗漏了一些东西(关于图像),也许这可能会帮助我得到一些答案。图像尺寸相同,300x300 像素。但尺寸可能会在运行时发生变化。前几天晚上,我用超过 6 万张 png 图像填充了一个文件夹。它导致我的硬盘锁定。我可以减少到大约 4 万张图像,但文件夹需要很长时间才能让我看到文件。这就是为什么我认为走 tfilestream 路线是最好的,因为我将写入一个文件。
  • 听起来你真正想做的是record the screen。如果这就是你的程序所能做的,那么不要费心编写你自己的程序。只需使用一个off the shelf

标签: delphi bitmap tfilestream


【解决方案1】:
Function LoadBMPFromStream(const fn: String; Bmp: TBitmap; Nr: Integer):Boolean;
var // Nr is 0 based first Bitmap=0
  fs: TFileStream;
  ms: TMemoryStream;
  intNr: Integer;
  pos: Cardinal;
  size: DWord;
begin
  intNr := 0;
  if fileexists(fn) then
  begin
    Result := true;
    fs := TFileStream.Create(fn, fmOpenRead or fmShareDenyNone);
    try
      fs.Read(size, SizeOf(DWord)); // Read Size of first Bitmap
      while (Nr > intNr) and (fs.Position < fs.size) do
      begin
        fs.Seek(size, soFromCurrent);
        fs.Read(size, SizeOf(DWord)); // Read Size of  Bitmap with intNr
        inc(intNr);
      end;
      if fs.Position < fs.size then
      begin
        ms := TMemoryStream.Create;
        try
          ms.CopyFrom(fs, size);
          ms.Position := 0;
          Bmp.LoadFromStream(ms);
        finally
          ms.Free;
        end;
      end
      else Result := false;
    finally
      fs.Free;
    end;

  end;
end;


procedure SaveBMPtoStream(const fn: String; Bmp: TBitmap);
var
  fs: TFileStream;
  ms: TMemoryStream;
  pos: Cardinal;
  size: DWord;
begin
  if fileexists(fn) then
  begin
    fs := TFileStream.Create(fn, fmOpenReadWrite or fmShareDenyNone);
    fs.Seek(0, soEnd);
  end
  else
  begin
    fs := TFileStream.Create(fn, fmCreate or fmShareDenyNone);
  end;
  try
    ms := TMemoryStream.Create;
    try
      Bmp.SaveToStream(ms);
      size := ms.size;
      ms.Position := 0;
      fs.Write(size, SizeOf(DWord)); // Write Size of next Bitmap first
      fs.CopyFrom(ms, size);
    finally
      ms.Free;
    end;
  finally
    fs.Free;
  end;

end;

procedure TForm6.Button2Click(Sender: TObject);
begin
  // load first Picture
  LoadBMPFromStream('C:\temp\test.str', Image3.picture.bitmap, 0);
  // load third picture
  // LoadBMPFromStream('C:\temp\test.str', Image3.picture.bitmap, 2);
end;

procedure TForm6.Button1Click(Sender: TObject);
begin
  SaveBMPtoStream('C:\temp\test.str', Image1.picture.bitmap);
  SaveBMPtoStream('C:\temp\test.str', Image2.picture.bitmap);
  SaveBMPtoStream('C:\temp\test.str', Image1.picture.bitmap);
end;

【讨论】:

  • @bummi,谢谢,您的读/写程序按原样工作。我添加了进度条,可以向左/向右扫描。然而,一个小问题,因为我不知道最后一个图像编号,当我超过某个数字时,例程会崩溃。有没有办法存储最后一个图像编号?我使用 gl_cntr 作为记录的图像数量。我可以添加另一个 tbutton 来关闭带有最终编号的文件。谢谢。
  • 我做了修改,应该不会再崩溃了。将附加信息存储为总数有不同的可能性。你可以有一个固定的标题,例如。必须调整用于计数、读取和写入的 4Bytes Dword。您还可以在流的末尾附加计数作为 DWord,这意味着写入将从 fs.Seek(4, soEnd); 开始。另一种方法可能是在开始时使用一种“FAT”,例如。有 100 个图像的空间和一个指向下一个“FAT”的指针,如果需要,像结构一样,依此类推。目前我不知道你喜欢什么,我没有进一步修改。
  • 嗨,笨蛋。我发现了一个小问题。你的日常是正确的。但是,当屏幕捕获开始增长时,捕获开始每隔一定秒数暂停或犹豫。例如,每隔 5 秒,它会暂停 2 秒然后继续,一直如此。因此,考虑到它正在丢帧。我相信这是由于打开/关闭/寻找捕获的每一帧。我认为这一次,它需要在应用程序启动时(给定文件名)和停止时打开一次,以刷新/关闭等。这听起来对你来说是正确的吗?
  • 是的,当然,你是对的。它不是优化的喷气机,只是为了演示而保持简单。你可以打开一次,完成后关闭。
  • 我想尝试将其更改为连续的开放流。我会告诉你进展如何。
【解决方案2】:

您想将所有图像保存到同一个文件吗?然后,您最简单的选择是使用 0 压缩(仅存储)的 7zip 文件,这将非常轻松地为您管理存档中文件的索引/保存/检索。

这是一个很好的免费组件,可以做到这一点:http://www.rg-software.de/rg/index.php?option=com_content&view=article&id=29&Itemid=51

您现在也可以再次尝试仅使用位图,同时在 7zip 中使用快速压缩设置,这可能会获得比 PNG 更好的速度。

【讨论】:

  • 这对我不起作用,因为我正在尽可能快地实时读取/写入图像。
  • 您需要永久存储图像,还是仅在程序运行时临时存储?
  • 我不确定。但到目前为止,我一直在做的是将 png 图像保存到磁盘,在它运行时一张一张地保存。有一个计数器让我知道谁创建了许多 png 图像,加上该数字是填充到文件名的索引,即 bmp001.png bmp002.png,... bmp998.png 等,然后我在另一个应用程序中打开以查看。但我想从我自己的应用程序中完成所有这些(以及更多)。写入一个文件(流)将为我节省空间并帮助提高流程效率。
  • 我什么都不懂。这条线的目的是什么 (s.Position := 0;) where s=tmemorystream,假设 's.position' 是改变图像索引,为什么它总是等于零?设置它有什么意义?好吧,让我继续弄清楚这一切。
  • 如果我知道您的应用需要做什么,那将会有所帮助。如果您写入文件流,您必须先搜索到它的末尾,然后以不同的方式从那里写入 bmp,但是您还需要为每个图像保留文件中的位置。如果您只需要在应用程序运行时保存图像,则应使用 TMemoryStream 数组,如果需要能够动态删除图像,则应使用 ObjectList。首先创建一个 TMemoryStream 数组,然后为每个图片创建一个新流并根据需要从那里加载,这将比磁盘快得多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-20
  • 1970-01-01
  • 2014-12-29
  • 2016-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多