Autor Beitrag
Takeshi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 64



BeitragVerfasst: Di 22.07.03 00:29 
Hallo!

Um die Größe eines Images zu verändern habe ich tausend Sachen gefunden. Aber geht das auch, dass das Bild dabei nicht pixelig wird ? Mit Anti Alias quasi ?

www.delphipraxis.net/viewtopic.php?t=63

DP hat folgendes geschrieben:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TForm1.ResizeImage(newWidth,newHeight: Integer); 
begin 
  SetStretchBltMode (Image2.Canvas.Handle, Halftone); 
  StretchBlt (
      Image2.Canvas.Handle, 
      00
      newwidth, newheight, 
      Image1.Canvas.Handle, 
      00
      Image1.Width, Image1.Height,
      SRCCOPY
    ); 
end

procedure TForm1.Button1Click(Sender: TObject); 
begin 
  ResizeImage(100,50); 
end;

In Image1 wird das Bild geladen und in Image2 wird es vergrößert oder verkleinert dargestellt.


funzt leider nur bei 2k/xp

viele Grüße, Takeshi

Moderiert von user profile iconTino: Sourcecode hinzugefügt.
Logiqu
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 43

Win XP
Delphi 2006 Pro
BeitragVerfasst: Di 22.07.03 08:56 
hi! wenn du nicht weiter kommst, versuchs mal mit der gaußschen unschärfe. ist ein bisschen mathematik, kommst aber zum ziel...
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8549
Erhaltene Danke: 478

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Di 22.07.03 11:53 
möchtest du ein kleines Bild groß darstellen oder ein großes Bild verkleinern?

Wenn du ein kleines Bild groß darstellen möchtest, würde ich es über Interpolation machen. Das geht pinzipiell so (bei einer Verdoppelung der Bildgröße):
Zuerst machts du das Bild doppelt so breit. Die neu hinzukommenden Pixel berechnest du als Mittelwerte der umliegenden Pixel. Also, wenn eine Zeile z.b. die Grauwerte 0, 100, 200 hat, dann hat die verdoppelte Zeile die Werte 0, 50, 100, 150, 200. Mit den RGB-Werten geht das entsprechend. (ok..das ist nicht GENAU doppelt so groß, da fehlt ein Pixel...)
Dann machst du das neue, breitere Bild mit demselben Verfahren doppelt so hoch.
Das sind die Grundlagen der Interpolation (so machen das prinzipiell auch Scanner um die Auflösung des Bildes zu erhöhen). Wie man das jetzt genau in Delphi realisiert, ist da noch ne andre Frage. Müßte man sich wohl selbst zusammenbasteln.

Wenn das Bild nicht um Faktor 2 größer werden soll, musst du dir überlegen, welche Pixel des Ursprungsbildes bei dem vergößerten eine Rolle spielen und dementsprechend die einzelnen Pixelfarben berechnen.
(Ist auch ein bissel Mathematik dabei, aber ohne die wirst du bei deinem Problem kaum weit kommen ;-) )

_________________
We are, we were and will not be.
Takeshi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 64



BeitragVerfasst: Di 22.07.03 18:12 
hmm

klingt sehr nach arbeit :roll:
mit welchen funktionen / proceures mach denn sowas ?
bisher lade ich die bilder ja nur in die entsprechende komponente, mit 'bildbearbeitung' kenne ich mich noch nicht aus

das bild möchte ich mit einem beliebigen faktor verGRÖSSERN

greez und danke schonmal Takeshi
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8549
Erhaltene Danke: 478

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Di 22.07.03 21:58 
ja, manchmal muss man beim programmieren auch arbeiten... ;-)
welche Funktionen oder Prozeduren das machen, kann ich dir auf anhieb nicht sagen. Ich glaube aber auch nicht, dass bei Delphi vernünftige Interpolationsalgorithmen mitgeliefert werden. Da musst du wohl in den sauren Apfel beissen und das selbst programmieren.
du brauchst also eine Prozedur, die die einzelnen Pixelinformationen in ein (2dimensionales) Array schreibt, und dann eine, die aus diesem Array ein neues Array mit neuen Pixelinformationen schreibt. Und dieses Array muss dann wieder als Bild gespeichert werden. Bei dem mittleren Teil kann ich dir noch weiter helfen (hab mal die Vorlesung "Bildverarbeitung" teilweise gehört), wie du bei den verschiedenen Bildformaten (jpeg, gif, bmp, tiff...) die Pixelinfos ausliest, weiss ich nicht. Es sei denn, dir macht es nix aus, dich auf BMP mit einer Farbtiefe von 24bit zu beschränken...

Ich hab vor einiger Zeit ein Pascal-Programm geschrieben, was 24bit-Bitmaps liest und bearbeitet. Das hab ich damals ALLES per Hand gemacht. Pixel für Pixel aus der Datei geladen, und die neuen Werte in ne andre Datei geschrieben. BMP ist da ein sehr einfaches Format, da die Farbwerte der Pixel einfach hintereinander stehen. Bei komprimierten Formaten wie z.B. JPG ist das wesentlich komplizierter.

_________________
We are, we were and will not be.
Takeshi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 64



BeitragVerfasst: Do 24.07.03 14:48 
ich habe ja nichts gegen programmeirarbeit, nur die funktionen und prozeduren, die man so verwrendet, um all diese dinge per hand zu bewerkstelligen meinte ich.
die eigentliche arbeit besteht ja darin, zumindest für mich als informatikschüler, die grundlagen zu erlernen. das programmieren an sich ist ok.

ich verwende zwar jpgs, kann die aber als typ TBitmap aus einem Image auslesen
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8549
Erhaltene Danke: 478

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Do 24.07.03 20:05 
Also zuerst mal: natürlich wollte ich hier keinem unterstellen, dass hier jemand zu faul zum Programmieren ist ;-)
Und dann hab ich mich mal hingesetzt, und hab das proggen von Interpolation angefangen. Ist ne ziemliche Index-Wurschtelei geworden. Und viele Kommentare hab ich erst gar nicht hingeschrieben, da das komplizierteste wirklich das finden der richtigen Indexe (Inidzes?) ist.

Funktionsweise: Zuerst lese Pixel für Pixel die Farbinformationen raus und zerlege sie in die RGB-Werte.
Dann wird Pixel für Pixel das neue Bild aufgebaut, dabei werden aus einem Pixel 4. Die RGBWerte für die 3 neuen Pixel werden aus den umliegenden RGBWerten berechnet und zu einer neuen Tcolor zusammengesetzt.
Jetzt hab ich nur noch das Problem, dass das ziemlich langsam ist, was wohl an der Pixel-Operation von Canvas liegt. Hab aber im Moment keine Idee, wie man das anders machen könnte.
Und da ich grade nix besseres zu tun hatte, und die arbeit auch nicht sinnlos sein sollte, poste ich den fertigen Code mit.

ausblenden volle Höhe Delphi-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:
procedure TForm1.Button1Click(Sender: TObject);
var x,y,w,h:integer;
    bild,neu:tBitmap;
    tmp:tcolor;
    r,g,b: array of integer;
    rneu,gneu,bneu:array of integer;
begin
    bild:=TBitmap.Create;
    bild.loadfromfile('C:\dollesBild.BMP');
    w:=bild.Width;
    h:=bild.height;
    setlength(r,w*h*2);
    setlength(g,w*h*2);
    setlength(b,w*h*2);
    for y:=0 to h-1 do
        for x:=0 to w-1 do
        begin
            tmp:=bild.Canvas.Pixels[x,y];
            // Stück für Stück die RGB Werte auslesen
            // Guck dir die Definition von Tcolor an, dann sollte klar sein, was ich hier mache
            r[x + y*w]:=tmp MOD 256;
            tmp:=tmp DIV 256;
            g[x + y*w]:=tmp MOD 256;
            tmp:=tmp DIV 256;
            b[x + y*w]:=tmp MOD 256;
        end;

    // Neues Bild erzeugen
    neu:=tbitmap.Create;
    neu.Height:=2*h-1;
    neu.Width:=2*w-1;

    // Jetzt wirds ein bissel kompliziert mit den Indizes.
    for y:=0 to h-2 do
    begin
        for x:=0 to w-2 do
        begin
            neu.Canvas.Pixels[2*x,2*y]:=$00000000
                + 256*256*b[x + y*w]
                +     256*g[x + y*w]
                +         r[x + y*w];
            neu.Canvas.Pixels[2*x+1,2*y]:=$00000000
                +256*256* ((b[x + y*w] +  b[x+1 + y*w])DIV 2)
                    +256* ((g[x + y*w] +  g[x+1 + y*w])DIV 2)
                    +     ((r[x + y*w] +  r[x+1 + y*w])DIV 2);
            neu.Canvas.Pixels[2*x,2*y+1]:=$00000000
                +256*256* ((b[x + y*w] +  b[x + (y+1)*w])DIV 2)
                    +256* ((g[x + y*w] +  g[x + (y+1)*w])DIV 2)
                    +     ((r[x + y*w] +  r[x + (y+1)*w])DIV 2);
            neu.Canvas.Pixels[2*x+1,2*y+1]:=$00000000
                +256*256* ((b[x + y*w] +  b[x + (y+1)*w]+b[x+1 + y*w] +  b[x+1 + (y+1)*w])DIV 4)
                    +256* ((g[x + y*w] +  g[x + (y+1)*w]+g[x+1 + y*w] +  g[x+1 + (y+1)*w])DIV 4)
                    +     ((r[x + y*w] +  r[x + (y+1)*w]+r[x+1 + y*w] +  r[x+1 + (y+1)*w])DIV 4);
        end;
        // lezten Pixel der beiden neuen Zeilen schreiben
        neu.Canvas.Pixels[2*w-2,2*y]:=$00000000
                + 256*256*b[w-1 + y*w]
                +     256*g[w-1 + y*w]
                +         r[w-1 + y*w];
        neu.Canvas.Pixels[2*w-2,2*y+1]:=$00000000
                + 256*256* ((b[w-1 + y*w] +  b[w-1 + (y+1)*w])DIV 2)
                +     256* ((g[w-1 + y*w] +  g[w-1 + (y+1)*w])DIV 2)
                +          ((r[w-1 + y*w] +  r[w-1 + (y+1)*w])DIV 2);
    end;
    //Jetzt noch die Letzte Zeile
    y:=h-1;
    for x:=0 to w-2 do
    begin
        neu.Canvas.Pixels[2*x,2*y]:=$00000000
                + 256*256*b[x + y*w]
                +     256*g[x + y*w]
                +         r[x + y*w];
            neu.Canvas.Pixels[2*x+1,2*y]:=$00000000
                +256*256* ((b[x + y*w] +  b[x+1 + y*w])DIV 2)
                    +256* ((g[x + y*w] +  g[x+1 + y*w])DIV 2)
                    +     ((r[x + y*w] +  r[x+1 + y*w])DIV 2);
    end;
    // Und den Allerletzten Pixel
    neu.Canvas.Pixels[2*w-2,2*y]:=$00000000
            + 256*256*b[w-1 + y*w]
            +     256*g[w-1 + y*w]
            +         r[w-1 + y*w];

    neu.SaveToFile('C:\dollesgrossesBild.bmp');
    neu.Free;
    bild.Free;
end;

_________________
We are, we were and will not be.
Eggi
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 33

Win 2000, Win XP
D7 Prof
BeitragVerfasst: Do 24.07.03 20:18 
Gausi hat folgendes geschrieben:
Jetzt hab ich nur noch das Problem, dass das ziemlich langsam ist, was wohl an der Pixel-Operation von Canvas liegt. Hab aber im Moment keine Idee, wie man das anders machen könnte.
Und da ich grade nix besseres zu tun hatte, und die arbeit auch nicht sinnlos sein sollte, poste ich den fertigen Code mit.


Du hast recht das Geschwindigkeitsproblem liegt an der Pixeloperation des Canvas. Wenn du an deren Stelle Scanline verwendest wird's um einiges schneller. Anbei ein kleines Scanline-Beispiel das ein Bild in ein Graustufenbild umwandelt.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
function ColorToGray(var bild:tbitmap):integer;
var x,y:integer;
    p:pbytearray;
begin
 for y:=0 to bild.Height-1 do begin
  p:=bild.ScanLine[y];
  for x:=0 to bild.Width-1 do begin
   p[x*3]:=(p[x*3]+p[x*3+1]+p[x*3+2]) div 3;
   p[x*3+1]:=p[x*3];
   p[x*3+2]:=p[x*3];
  end;
 end;
end;


Eggi
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8549
Erhaltene Danke: 478

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Do 24.07.03 20:42 
hmm...hört sich ganz gut an. Um das ganze Bild dann "einzuscannen" müßte man sich dann ein "Array of pbytearray" anlegen..?
Weiterer Vorteil: Man hat nich so ein Rumgewurschtel mit den Indizes meines 1D-Pixel-Array.
Aber vollends zufrieden stellt mich die Scanline-Lösung auch nicht. Denn bei sehr hohen, sehr schmalen Bildern bringt scanline doch auch nix, oder? Oder ist scanline schneller als eine einzelne Pixeloperation?

Ach ja, nochwas: Von dem Gebrauch dieser ColorToGray Funktion bei proffessionell angehauchten Bildbearbeitungen würde ich dringend abraten, da diese Funktion nicht die unterschiedlich intensive Helligkeitswahrnehmung des menschlichen Auges von grün, rot und blau berücksichtigt, sondern alle drei Werte gleich behandelt. Aber das nur am Rande.

_________________
We are, we were and will not be.
Eggi
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 33

Win 2000, Win XP
D7 Prof
BeitragVerfasst: Fr 25.07.03 09:07 
Hallo

1. Scanline ist auch bei schmalen Bildern schneller als Canvas.Pixels, da Scanline nicht wie Canvas.Pixels auf der Zeichenfläche arbeitet, sondern direkt im Speicher wo das Bild liegt. Außerdem müssen die Farbwerte nicht erst berechnet werden.

2. Ein Array of Pbytearray brauchst du meistens nicht. Bei den meisten Bildverarbeitungsfunktionen werden nur wenige Bildzeilen benötigt.
Auch beim Vergrößern eines Bildes braucht man nicht alle Zeilen in einem Array abzulegen. Im ersten Schritt macht man die horizontale Vergrößerung, wofür nur eine einzige Bildzeile des Ausgangsbildes und eine Bildzeile des Ergebnisbildes benötigt wird. Bei der anschließenden vertikalen Vergrößerung brauchst du wieder nur eine Zeile des Ergebnisbildes (eine der zeilen die bei der horizontalen Vergrößerung weiss geblieben sind) und 2 Zeilen des Ausgangsbildes.

Deine Meinung zu der ColorToGray-Funktion angeht kann ich nicht ganz teilen. Die Funktion hat nämlich schon ihre Daseinsberechtigung. Denn nicht jedes Bild während einer Bildbearbeitung berechnet wird ist für den Menschen bestimmt. Ich nutze die Funktion zum Beispiel bei der Gewinnung von Texturmerkmalen. Auch wenn du ein Graustufenbild an die menschliche Wahrnehmung anpassen willst, hast du es nicht ganz so einfach. Ich kenne zum Beispiel 9 verschiedene Berechungsformeln die auf unterschiedlichste Art eine Anpassung an die menschliche Wahrnehmung vornehmen.

Eggi
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8549
Erhaltene Danke: 478

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Fr 25.07.03 10:57 
Ok, dann hab ich Scanline unterschätzt. Werde das bei meinem nächsten Bildverarbeitungsprogramm berücksichtigen. Oder dann, wenn ich mein altes Pascal-bmp-Programm nach Delphi umschreibe...

Zu ColortoGray nochmal: Klar, dass mit dem menschlichem Auge ist ein Problem, was man nicht 100%ig lösen kann, und jeder, der sich damit beschäftigt hält am Ende ne andre Formel für die Beste. Mit dem Hinweis wollte ich nur klarstellen, dass das z.b. für das nachträgliche "eingrauen" von Fotos o.ä. diese einfache, schnelle Funktion nicht unbedingt das nonplusultra ist.
Obwohl ich sagen muss, dass ich diese Funktion auch schon benutzt habe, und die Ergebnisse waren auch zufriedenstellend; aber professionelle Bildbearbeitungsprogramme, wie z.b. Corel Photopaint gehen die Sache etwa anders an. Nimm mal ein Bild, was die Farben (150,0,0) (0,150,0) und (0,0,150) hat und mach daraus mit Photopaint ein Graustufenbild...

_________________
We are, we were and will not be.
maximus
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 896

Win XP, Suse 8.1
Delphi 4/7/8 alles prof
BeitragVerfasst: Fr 25.07.03 11:12 
Zitat:
...
1. Scanline ist auch bei schmalen Bildern schneller als Canvas.Pixels, da Scanline nicht wie Canvas.Pixels auf der Zeichenfläche arbeitet, sondern direkt im Speicher wo das Bild liegt. Außerdem müssen die Farbwerte nicht erst berechnet werden....


In erster linie ist 'pixels' so langsam, weil jedesmal ein oder zwei funktions-aufrufe gemacht werden...intern arbeitet sie auch mit scanline!

d.h. scanline wäre bei sehr hohen/schmalen nicht vioel schneller, da ja für jede zeile die scanline funktion aufgerufen wird :wink:

_________________
mfg.
mâximôv
Eggi
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 33

Win 2000, Win XP
D7 Prof
BeitragVerfasst: Fr 25.07.03 11:54 
maximus hat folgendes geschrieben:

In erster linie ist 'pixels' so langsam, weil jedesmal ein oder zwei funktions-aufrufe gemacht werden...intern arbeitet sie auch mit scanline!

d.h. scanline wäre bei sehr hohen/schmalen nicht vioel schneller, da ja für jede zeile die scanline funktion aufgerufen wird :wink:


Wenn du dir den Quelltext mal angesehen hättest wüßtest du das das nicht stimmt.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
function TCanvas.GetPixel(X, Y: Integer): TColor;
begin
  RequiredState([csHandleValid]);
  GetPixel := Windows.GetPixel(FHandle, X, Y);
end;


Die Canvas.Pixels-Routine arbeitet nicht mit Scanline sondern mit Hilfe der Winapi Funktion GetPixel.
Takeshi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 64



BeitragVerfasst: Mo 28.07.03 23:08 
OK Hallo noch einmal!

ich habe noch so eine Frage bezüglich der Umwandlung der jpg nach bmp. Bisher habe ich das so mit Hilfe von einem Image und Canvas.StretchDraw gelöst:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
bild:=TPicture.Create;
bild.LoadFromFile(FileName);
Image1.Width:=bild.Width;
Image1.Height:=bild.Height;
Image1.Picture.Bitmap.Height:=Image1.Height;
Image1.Picture.Bitmap.Width:=Image1.Width;
Image1.Canvas.StretchDraw(Image1.ClientRect, bild.Graphic);

// 2 X AntiAliasing(Image1.Picture.Bitmap)

bild.Bitmap := Image1.Picture.Bitmap;
Image1.Width:=Round(bild.Width/2);
Image1.Height:=Round(bild.Height/2);
Image1.Picture.Bitmap.Height:=Image1.Height;
Image1.Picture.Bitmap.Width:=Image1.Width;
Image1.Canvas.StretchDraw(Image1.ClientRect, bild.Graphic);

Hinweis: Das AntiAliasing findet hier vor dem Ändern der Größe statt, da diese halbiert wird.

Zwar könnte es auch mit Hilfe von Canvas jetzt auch auf ein beliebiges Objekt gezeichnet werden, ohne Image, so ganz glücklich scheint mir das aber noch nicht. Vielleicht kann mir jemand sagen, ob es ne unkomplizierte bessere Lösung gibt, dann könnte ich es auch hübsch in eine Prozedur verpacken.

Gruß, Takeshi
Eggi
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 33

Win 2000, Win XP
D7 Prof
BeitragVerfasst: Di 29.07.03 11:17 
Hallo

ich mache das ganz ähnlich wie du. Nur das ich nicht StrechDraw sondern Draw verwende. Außerdem nutze ich für meine Bildverarbeitungsfunktionen keine TImage sondern ein TBitmap. Nur wenn ich ein Bild anzeigen will, zeichne ich mein TBitmap auf ein Image.

Eggi

Achja beim Laden setze ich das pixelformat auf pf24bit damit ich die Bildverabeitungsfunktionen nur für 24bit Bilder implementieren muss.