Autor Beitrag
Andreas Pfau
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 997



BeitragVerfasst: Mo 08.12.03 17:39 
Hallo,

ich habe einen Code entwickelt, mit dem man ein Bitmap mit bilinearer Interpolation stretchen kann. Ich habe darauf geachtet, dass der Code möglichste schnell und effizient ist, daher scheint er evtl. stellenweise etwas verwirrend, daher habe ich ihn gut kommentiert:
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:
procedure ResampleBilinear(Src, Dst: TBitmap);
var
  X10, Y10, X11, Y11: Integer;
  X1, Y1: Single;
  Fx, Fy: Single;
  F: Single;
  I: Integer;
  X2, Y2: Integer;
  W1, H1, W2, H2: Integer;
  SrcRow0, SrcRow1, DstRow: PByteArray;
begin
  // Bitmaps auf 24-Bit RGB setzen
  Src.PixelFormat := pf24Bit;
  Dst.PixelFormat := pf24Bit;

  // Größe der Bitmaps speichern
  W1 := Src.Width  - 1;
  H1 := Src.Height - 1;
  W2 := Dst.Width  - 1;
  H2 := Dst.Height - 1;

  // Umrechnungsfaktoren berechnen
  Fx := W1 / W2;
  Fy := H1 / H2;

  // Zeilen durchgehen
  for Y2 := 0 to H2 do
  begin
    { Y1   Position des Pixels im Bereich 0..1
      Y10  Erste Scanline
      Y11  Zweite Scanline }

    Y1 := Y2 * Fy;
    Y10 := Trunc(Y1);
    Y11 := Y10;
    Y1 := Y1 - Y10;
    if (Y1 > 0then
      Y11 := Y11 + 1;
    { Entspricht folgendem Code:
      Y1  :=  Frac(Y2 * Fy);
      Y10 := Floor(Y2 * Fy);
      Y11 :=  Ceil(Y2 * Fy); }


    // Scanlines laden
    SrcRow0 := Src.ScanLine[Y10];
    SrcRow1 := Src.ScanLine[Y11];
    DstRow  := Dst.ScanLine[Y2];

    for X2 := 0 to W2 do
    begin
      { X1   Position des Pixels im Bereich 0..1
        X10  Erster Pixel
        X11  Zweiter Pixel }

      X1 := X2 * Fx;
      X10 := Trunc(X1);
      X1 := X1 - X10;
      X10 := X10 * 3;
      X11 := X10;
      if (X1 > 0then
        X11 := X11 + 3;
      { Entspricht folgendem Code:
        X1  :=  Frac(X2 * Fy);
        X10 := Floor(X2 * Fy) * 3;
        X11 :=  Ceil(X2 * Fy) * 3; }


      // Pixel setzen (R/G/B)
      DstRow[X2 * 3    ] := Round(
        SrcRow0[X10    ] * (1 - X1) * (1 - Y1) +
        SrcRow0[X11    ] *      X1  * (1 - Y1) +
        SrcRow1[X10    ] * (1 - X1) *      Y1  +
        SrcRow1[X11    ] *      X1  *      Y1);
      DstRow[X2 * 3 + 1] := Round(
        SrcRow0[X10 + 1] * (1 - X1) * (1 - Y1) +
        SrcRow0[X11 + 1] *      X1  * (1 - Y1) +
        SrcRow1[X10 + 1] * (1 - X1) *      Y1  +
        SrcRow1[X11 + 1] *      X1  *      Y1);
      DstRow[X2 * 3 + 2] := Round(
        SrcRow0[X10 + 2] * (1 - X1) * (1 - Y1) +
        SrcRow0[X11 + 2] *      X1  * (1 - Y1) +
        SrcRow1[X10 + 2] * (1 - X1) *      Y1  +
        SrcRow1[X11 + 2] *      X1  *      Y1);
    end;
  end;
end;

Zur Geschwindigkeit: sie (pro Pixel) ist nahezu unabhängig von der Größe des Quell- und Zielbitmaps, auf meinem P4-1800 etwa 6MPix/s, auf meinem P1-200MMX etwa 500kPix/s.

Wie funktioniert's?

Im Bild (siehe Anhang) zu sehen sind vier Pixel (rot, gelb, blau, grün), die einen Ausschnitt des Quellbitmaps darstellen sollen. Das schwarze Quadrat ist ein Pixel des Zielbitmaps, der berechnet werden soll. Die schwarzen Punkte stellen je die Mitte der Pixel dar. Nun liegt der Mittelpunkt des zu berechnenen Pixel zwischen vier Pixeln des Quellbitmaps. Legen wir den Mittelpunkt des roten Pixels als Nullpunkt fest, und den des grünen Pixels als 100%. Dann wäre der neue Pixel auf der X-Achse bei etwa 57% und auf der Y-achse bei etwa 84%.
Nun berechnen wir erstmal die "Mischfarben" auf der X-Achse. Nehmen wir Farbe1 für die beiden oberen Pixel - sie bestünde zu 43% (100% - 57%) aus rot und zu 57% aus gelb. Farbe2 nehmen wir für die unteren Pixel, das wären dann 43% blau und 57% grün.
Mit diesen beiden Farben können wir schon die Farbe des Zielpixels berechnen - nämlich 16% (100% - 84%) von Farbe1 und 84% von Farbe2. So erhalten wir genau die Farbe, die ein bilinearer Farbverlauf (also in 2 Richtungen - X und Y) an dieser Stelle hätte. Und so verfahren wir dann mit allen Pixeln des Bitmaps.

Ich hoffe, das war einigermaßen verständlich. Auf jeden Fall tut der Code, und das relativ schnell.
Moderiert von user profile iconjasocul: Bild-Link durch Anhang ersetzt


BilinearExample.jpg  (3.49 KB) Download (Rev 0)
 (923x, 923x gesamt)
Beschreibung: Beispiel-Bild
_________________
Life is a bad adventure, but the graphic is really good!