Autor Beitrag
pmw
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 65



BeitragVerfasst: So 29.12.02 12:36 
Hallo!

Ich möchte gerne alle Farbwerte aller Pixel eines Bildes in einem String speichern. Mein kleines Programm funkoniert auch, braucht aber leider für ein normales Bitmap (512x384 Pixel) mehrere Minuten! Kann ich die Farbwerte irgendwie schneller auslesen?

ausblenden Quelltext
1:
2:
3:
4:
for x := 0 to Image.Width-1 do begin
   for y := 0 to Image.Height-1 do
      sFileString := sFileString+Copy(ColorToString(Image.Canvas.Pixels[x,y]),4,6);
end;


Viele Grüße
Martin Winandy
Popov
Gast
Erhaltene Danke: 1



BeitragVerfasst: So 29.12.02 13:55 
Mit ScanLine
pmw Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 65



BeitragVerfasst: So 29.12.02 14:11 
Zitat:
Mit ScanLine

Wie funktoniert dies?
Popov
Gast
Erhaltene Danke: 1



BeitragVerfasst: So 29.12.02 15:31 
Guck dir am besten dieses Tutorial an:

www.tutorials.delphi-source.de/bitmap
pmw Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 65



BeitragVerfasst: So 29.12.02 18:59 
Hallo!

Danke! Mein neues Programm ist jetzt deutlich schneller. Aber 5 Sekunden empfinde ich immer noch als zu langsam. Gibt es eine Möglichkeit dern Algorithmus noch ein wenig zu beschleunigen. Ich bräuchte eine Geschwindigkeit < 1 Sekunde.

Viele Grüße
Martin Winandy

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
procedure TForm1.EncodeClick(Sender: TObject);
type
PixArray = Array [1..3] of Byte;
var
p: ^PixArray;
x,y: integer;
begin

   for y := 0 to Image.Height-1 do begin
      p:= Image.Picture.Bitmap.ScanLine[y];
      for x := 0 to Image.Width-1 do begin
         sFileString := sFileString+Char(p^[1])+Char(p^[2])+Char(p^[3]);
         Inc(p);
      end;
   end;

end;
AXMD
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 4006
Erhaltene Danke: 7

Windows 10 64 bit
C# (Visual Studio 2019 Express)
BeitragVerfasst: Mo 30.12.02 14:19 
Hi,

Soweit ich weiß, gibts nichts schnelleres als ScanLine

AXMD
Popov
Gast
Erhaltene Danke: 1



BeitragVerfasst: Mo 30.12.02 14:47 
Ich kann mir nicht vorstellen, daß es 5 Sekunden dauert. Wenn ich ohne ScanLine arbeite, dann dauert es nicht mal 5 Sekunden.
Udontknow
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2596

Win7
D2006 WIN32, .NET (C#)
BeitragVerfasst: Mo 30.12.02 14:53 
Dann setze dich doch mal an eine Maschine, die weniger als 3 GHz hat... :wink:

Ne im Ernst: Was habt ihr denn für Rechner?

Cu,
Udontknow
pmw Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 65



BeitragVerfasst: Mo 30.12.02 15:03 
Hallo!

Ich besitze einen Pentium III mit 1GHz und 128MB SD-Ram (PC 133).

Viele Grüße
Martin Winandy
Popov
Gast
Erhaltene Danke: 1



BeitragVerfasst: Mo 30.12.02 15:05 
Das Problem durfte nicht ScanLine sein, sondern etwas anderes. Im Grunde genommen sind das nur noch zwei for-Schleifen. Die kosten doch keine Zeit.

Sowas kostet aber Zeit:

ausblenden Quelltext
1:
String := String + Char;					


Hier muß die Größe des Strings einige Tausend mal neu bestimmt werden. Das könnte die 5 Sekunden dauern.

Am besten ein String von der größe (Height-1 + Width-1) * 3 erstellen und dann nur noch mit String[x] arbeiten.
pmw Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 65



BeitragVerfasst: Mo 30.12.02 15:12 
Hallo!

Zitat:
Am besten ein String von der größe (Height-1 + Width-1) * 3 erstellen und dann nur noch mit String[x] arbeiten.

Wie mache ich das ohne Schleifen?

Ich habe noch ein Problem: Ich kann die Procedure "EncodeClick" immer nur ein Mal durchlaufen lassen. Beim 2. AUfruf der Procedure reagiert das Programm nicht mehr. Es kommt aber keine Fehlermeldung.

ausblenden volle Höhe Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
var
  Form1: TForm1;
  sFileString: AnsiString;

implementation

{$R *.dfm}

function SaveStringToFile(sOutFile: String; sFileString: AnsiString): Boolean;
var
  FileStream: TFileStream;
begin
  Result := False;
  FileStream := TFileStream.Create(sOutFile, fmCreate);
  try
    if Length(sFileString) <> 0 then begin
      FileStream.Write(sFileString[1], Length(sFileString));
      Result := True;
    end;
  finally FileStream.Free end;
end;

procedure TForm1.EncodeClick(Sender: TObject);
type
PixArray = Array [1..3] of Byte;
var
p: ^PixArray;
x,y: integer;
pic: TBitmap;
begin

   pic := TBitmap.Create;
   pic.LoadFromFile(bmp.Text);
   pic.PixelFormat:= pf24Bit;

   for y := 0 to pic.Height-1 do begin
      p := pic.ScanLine[y];
      for x := 0 to pic.Width-1 do begin
         sFileString := sFileString+Char(p^[1])+Char(p^[2])+Char(p^[3]);
         Inc(p);
      end;
   end;

   try DeleteFile(spf.Text); except end;

   SaveStringToFile(spf.Text,sFileString);

   pic.Free;

end;
Popov
Gast
Erhaltene Danke: 1



BeitragVerfasst: Mo 30.12.02 17:06 
Um die Länge des Strings festzulegen nimmst du:

SetLength(var S: string; NewLength: Integer);

Dannach ist der S String so lang wie in NewLength festgelegt. Du hast dann eine Art Stream.
pmw Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 65



BeitragVerfasst: Mo 30.12.02 18:18 
Hallo!

Leider bringt dies auch keinen Geschwindikkeitsvorteil.Bis zur Fertig-Meldung sind es immer noch 5 Sekunden.


ausblenden volle Höhe Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
procedure TForm1.EncodeClick(Sender: TObject);
type
PixArray = Array [1..3] of Byte;
var
p: ^PixArray;
x,y: integer;
pic: TBitmap;
begin

   pic := TBitmap.Create;
   pic.LoadFromFile(bmp.Text);
   pic.PixelFormat:= pf24Bit;
   SetLength(sFileString,(pic.Height+pic.Width)*3);
 
   ShowMessage('START!');
  
   for y := 0 to pic.Height-1 do begin
      p := pic.ScanLine[y];
      for x := 0 to pic.Width-1 do begin
         sFileString := sFileString+Char(p^[1])+Char(p^[2])+Char(p^[3]);
         Inc(p);
      end;
   end;

   ShowMessage('FERTIG!');

   try DeleteFile(spf.Text); except end;

   SaveStringToFile(spf.Text,sFileString);

   pic.Free;

end;
AndyB
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1173
Erhaltene Danke: 14


RAD Studio XE2
BeitragVerfasst: Mo 30.12.02 23:10 
Du hast ja nicht genau das gemacht, was Popov gemeint hat. Du baust den String immer noch mit + zusammen.

ausblenden volle Höhe Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
procedure TForm1.EncodeClick(Sender: TObject);
var
  p: PByte;
  x, y, i: Integer;
  Pic: TBitmap;
  Stream: TFileStream;
  sFileString: string;
  Width, Height: Integer;
  ByteTable: array[0..255] of string[2];
  F: PChar;
begin
 // "Nachschau" Tabelle anlegen
  for i := 0 to 255 do ByteTable[i] := IntToHex(i, 2);

  Pic := TBitmap.Create;
  try
    Pic.LoadFromFile('c:\Test.bmp');
    Pic.PixelFormat := pf24Bit;
    Width := Pic.Width;
    Height := Pic.Height;

   { genug Speicher reservieren: 3 Farbwerte mit je 2 Zeichen }
    SetLength(sFileString, Height * Width * 3 * 2);

    ShowMessage('START!');
    F := PChar(sFileString);
    for y := 0 to Height - 1 do
    begin
      p := Pic.ScanLine[y];
      for x := 0 to (Width * 3) - 1 do
      begin
        PWord(F)^ := PWord(@ByteTable[p^][1])^;
{    ein wenig schneller als:
        F[0] := ByteTable[p^][1];
        F[1] := ByteTable[p^][2]; }
        Inc(F, 2);

        Inc(p);
      end;
    end;
  finally
    Pic.Free;
  end;

  ShowMessage('FERTIG!');

  Stream := TFileStream.Create('C:\Test.txt', fmCreate);
  try
    if Length(sFileString) > 0 then
      Stream.Write(sFileString[1], Length(sFileString));
  finally
    Stream.Free;
  end;
end;


Der Code brauch bei mir (650 MHz) 79 Millisekunden für ein 1024x768x24 Bild.

_________________
Ist Zeit wirklich Geld?
pmw Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 65



BeitragVerfasst: Di 31.12.02 12:01 
Hallo!

Danke für den Quellcode. Der Ist wirklich schnell! Leider verstehe ich den nicht ganz richtig und es kommt auch nicht ganz das gewünschte Ergebnis raus.
Der Code braucht für jeden Farbwert 2 Bytes (30 45 30 41 30 35). Ich wollte aber jeden Farbwert in einen Byte (0E 0A 05) speichern.

Viele Grüße
Martin Winandy
pmw Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 65



BeitragVerfasst: Di 31.12.02 12:10 
Hallo!

Ich habe jetzt die Lösung gefunden!

ausblenden volle Höhe Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
procedure TForm1.EncodeClick(Sender: TObject); 
var 
p: PByte;
x, y: Integer;
Pic: TBitmap;
sFileString: string;
F: PChar;
begin 

   Pic := TBitmap.Create;
   try
      Pic.LoadFromFile(bmp.Text);
      Pic.PixelFormat := pf24Bit;
      SetLength(sFileString, Pic.Height * Pic.Width * 3);

      F := PChar(sFileString);
      for y := 0 to Pic.Height - 1 do begin
          p := Pic.ScanLine[y];
          for x := 0 to (Width * 3) - 1 do begin
             F^ := Char(p^);
             Inc(F);
             Inc(p);
          end;
      end;
   finally
      Pic.Free;
   end;

   try DeleteFile(spf.Text); except end;

   SaveStringToFile(spf.Text,sFileString);

end;


Vielen Dank an alle, die mir geholfen haben und ich wünsche eine guten Rutsch ins Jahr 2003!
Martin Winandy
pmw Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 65



BeitragVerfasst: Di 31.12.02 12:17 
Hallo!

Ich sehe gerade, dass mein Quellcode doch nicht stimmt. Ab dem 576.000 Byte werde nur noch Nullen ausgegeben. Woran kann das liegen?
AndyB
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1173
Erhaltene Danke: 14


RAD Studio XE2
BeitragVerfasst: Di 31.12.02 12:28 
pmw hat folgendes geschrieben:
Der Code braucht für jeden Farbwert 2 Bytes (30 45 30 41 30 35). Ich wollte aber jeden Farbwert in einen Byte (0E 0A 05) speichern.

Also wenn du pro Pixel 3 Bytes (RGB = 3 * 1 Byte) verwenden willst, dann kann ich dir einen um einiges schnelleren Code geben:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
SetLength(sFileString, Pic.Height * Pic.Width * 3);
for y := 0 to Height - 1 do
begin
  p := Pic.ScanLine[y];
  Move(p^, sFileString[y * (Width * 3) + 1], Width * 3);
end;


Mein Code (2 Bytes pro Farbwert) wandelt die Bytes in Hexadezimal-Strings um und schreibt diese dann in die Datei. Das was du da willst (1 Byte pro Farbwert) ist nichts anderes, als die Daten 1:1 kopieren.

_________________
Ist Zeit wirklich Geld?
pmw Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 65



BeitragVerfasst: Di 31.12.02 13:53 
Hallo!

Danke! Jetzt funktioniert alles wies es soll! Dafür arbeitet aber meine DLL nicht mehr richtig mit dem String zusammen. Ich bekomme immer eine Fehlermeldung "External Error 80003".

Programm - Form1:
ausblenden volle Höhe Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, Spin;

type
  TForm1 = class(TForm)
    bmp: TEdit;
    Label1: TLabel;
    spf: TEdit;
    Label2: TLabel;
    Encode: TButton;
    Label3: TLabel;
    concolor: TComboBox;
    Label4: TLabel;
    filesize: TSpinEdit;
    Button1: TButton;
    procedure EncodeClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function convert(const filesize, color, pixelx,pixely: integer; picture: AnsiString): AnsiString; stdcall; external 'spf_en.dll';

function SaveStringToFile(sOutFile: String; sFileString: AnsiString): Boolean;
var
  FileStream: TFileStream;
begin
  Result := False;
  FileStream := TFileStream.Create(sOutFile, fmCreate);
  try
    if Length(sFileString) <> 0 then begin
      FileStream.Write(sFileString[1], Length(sFileString));
      Result := True;
    end;
  finally FileStream.Free end;
end;

procedure TForm1.EncodeClick(Sender: TObject); 
var 
p: PByte;
y: Integer;
pic: TBitmap;
input, output: AnsiString;
begin 

   pic := TBitmap.Create;
   try
   
      Pic.LoadFromFile(bmp.Text);
      Pic.PixelFormat := pf24Bit;
      SetLength(input,pic.Height*pic.Width*3);

      for y := 0 to pic.Height-1 do begin
         p := pic.ScanLine[y];
         Move(p^,input[y*(pic.Width*3)+1],pic.Width*3);
      end;

   finally pic.Free; end;

   output := input;
   output := convert(filesize.Value,concolor.ItemIndex,pic.Width,pic.Height,output); // Fehlermeldung! 

   try DeleteFile(spf.Text); except end;

   SaveStringToFile(spf.Text,output);

end;

procedure TForm1.Button1Click(Sender: TObject);
begin

   ShowMessage(convert(0,0,30,55,'Hallo!')); // Funktioniert einwandfrei! 

end;

end.


DLL:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
library spf_en;

uses
  ShareMem, Dialogs, SysUtils, Classes;

{$R *.res}

function convert(const filesize, color, pixelx,pixely: integer; picture: AnsiString): AnsiString; stdcall;
var
header: String;
begin

   result := picture;

end;

exports
  convert;
  
begin
end.


Viele Grüße
Martin Winandy
AndyB
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1173
Erhaltene Danke: 14


RAD Studio XE2
BeitragVerfasst: Di 31.12.02 17:43 
Hast du ShareMem auch in Hauptprogramm (.dpr-Datei) als erste Unit eingebunden?

_________________
Ist Zeit wirklich Geld?