begin process at 2013 05 20 07:54:40
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Graphique

 > RÉÉCHANTILLONNAGE BICUBIQUE VS STRETCHBLT

RÉÉCHANTILLONNAGE BICUBIQUE VS STRETCHBLT


 Information sur la source

Note :
10 / 10 - par 2 personnes
10,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10
Catégorie :Graphique Classé sous :Bicubic, Bicubique, StretchBlt, Halftone Niveau :Initié Date de création :03/08/2012 Date de mise à jour :19/08/2012 16:23:26 Vu / téléchargé :2 026 / 137

Auteur : cirec

Ecrire un message privé
Commentaire sur cette source (50)
Ajouter un commentaire et/ou une note

 Description

Cliquez pour voir la capture en taille normale
Suite à une question de Sisi231 sur le ré-échantillonnage bicubique
http://www.delphifr.com/infomsg.aspx?ID=1603600
e t comme j'ai rien trouvé en Pascal sur ce sujet j'ai décidé de traduire une procédure écrite en C par Paul Bourke et de la comparer à la bonne vielle méthode du StretchBlt en mode HALFTONE.

Les résultats montre que StretchBlt est quasi toujours la meilleur méthode.

Néanmoins la méthode bicubique présente un avantage qui peut avoir son poids ... elle conserve le canal alpha des bitmap 32bits contrairement à StretchBlt qui l'ignore totalement.

les versions ASMBicubic (de Barbichette), pré-calculéeBicubic et StretchBmpAlpha3 (de Pseudo3)  ont étés ajoutées au code

Source

  • Bicubic Interpolation for Image Scaling
  • http://paulbourke.net/texture_colour/imageprocess/
  • Original C code Written by Paul Bourke
  • http://paulbourke.net/libraries/bitmaplib.c
  • Pascal version by Cirec 2012
  • add: Callback methode to step a ProgressBar
Bicubic Interpolation for Image Scaling
http://paulbourke.net/texture_colour/imageprocess/

Original C code Written by Paul Bourke
http://paulbourke.net/libraries/bitmaplib.c

Pascal version by Cirec 2012
  add: Callback methode to step a ProgressBar


 Fichier Zip

Les Membres Club peuvent télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip


 Historique

03 août 2012 21:56:21 :
correction dans le titre
16 août 2012 23:54:07 :
Ajouts des méthodes de Pseudo3 & Barbichette
17 août 2012 17:11:46 :
suppression de la variable globale TBitmap sur les remarques très judicieuses de Caribensila.
18 août 2012 11:20:42 :
Remplacement du type Extended par le type Double pour un gain de vitesse ... pointé par Pseudo3 (cf. message du 17/08/2012 10:23:17)
18 août 2012 12:56:01 :
correction: Remplacement du type Extended par le type Single (et non Double) pour un gain de vitesse ... pointé par Pseudo3 (cf. message du 17/08/2012 10:23:17) Ajout d'un bout de code permettant d'afficher la version de Delphi utilisé pour compiler le code ...
19 août 2012 16:23:27 :
ajout d'une compilation conditionnelle (cf. commentaires)

 Sources du même auteur

Source avec Zip Source avec une capture EXEMPLE DE MANUAL DOCKING OU COMMENT DONNER DE LA SOUPLESSE ...
Source avec Zip Source avec une capture BITMAP 32BITS INTÉGRÉ À LA VCL (TIMAGE, TPICTURE, TBITMAP, T...
Source avec Zip Source avec une capture [ASTUCE] COMMENT VOIR UN ITEM, D'UNE LISTBOX, TRONQUÉ DANS U...
Source avec Zip Source avec une capture TEXTE GRAPHIQUE AVEC CONTOUR, OMBRE ET TEXTURE EN API VERSIO...
Source avec Zip Source avec une capture TEXTE GRAPHIQUE AVEC CONTOUR, OMBRE ET TEXTURE EN API

 Sources de la même categorie

Source avec Zip Source avec une capture SEAM CARVING V2 par barbichette
Source avec Zip Source avec une capture SEAM CARVING par barbichette
Source avec Zip Source avec une capture EFFET MATRIX DANS UN PANEL par soldier8514
Source avec Zip Source avec une capture REDIMENSIONNEMENT XBR AVEC DES FACTEURS D'ÉCHELLE QUELCONQUE... par pseudo3
Source avec Zip Source avec une capture DÉFORMATION D'UN VISAGE par barbichette

 Sources en rapport avec celle ci

Source avec Zip Source avec une capture EFFETS ZOOM & MIROIR SUR BITMAP par cirec

Commentaires et avis

Commentaire de pseudo3 le 04/08/2012 14:16:04

Bonjour,

"Les résultats montrent que StretchBlt est quasi toujours la meilleur méthode." :
Elle est effectivement bigrement plus rapide que la Bicubique et en cas de très fort agrandissement qui laisse apparaître une qualité visuelle légèrement moins bonne que celle du résultat obtenu avec la bicubique il suffit (vérification faite) d'y appliquer ensuite un filtre de flou horizontal + vertical avec une trainée de 2 pixels et la StretchBlt + le flou restent ensemble toujours plus rapide que la bicubique avec une qualité visuelle équivalente.

+.

Commentaire de pseudo3 le 05/08/2012 14:44:23

Bonjour,

Comme l'extrême lenteur de la Bicubique m'a intrigué et que d'autre part la StretchBlt ignore totalement le canal Alpha j'ai tricoté le code suivant qui gère le canal Alpha et dans lequel l'interpolation Bicubique est remplacée par un dégradé de couleurs entre les 4 couleurs-source encadrant la maille, et voici la fonction StretchBmpAlpha qui pourrait remplacer la StretchBlt si on a besoin du canal Alpha et d'éviter la lenteur de la Bicubique :

function StretchBmpAlpha(const BmpS: tBitMap; k: single; const ProgressCallBack: TObjectProc = nil): tBitMap;
var
  // Le BitMap-Source :
  WS, HS: integer;
  Scan0S: Integer; //Valeur du pointeur d'entrée dans le Bitmap-Source.
  ScanS: Integer; //Pointeur temporaire destiné à être incrémenté.
  MLSS: Integer; //Memory Line Size (en bytes) du Bitmap-Source.
  // Le BitMap-result :
  WR, HR: integer;
  Scan0R: Integer; //Valeur du pointeur d'entrée dans le Bitmap-Source.
  ScanR: Integer; //Pointeur temporaire destiné à être incrémenté.
  MLSR: Integer; //Memory Line Size (en bytes) du Bitmap-Source.
  Bpp: Integer; //Byte per pixel des Bitmap's
  cl0, cl1, cl2, cl3, cl30, cl21: tRGBQuad;

  xr, yr, xo1, xo2, yo1, yo2: integer;

  function rgbQuadDegradee(cl1, cl2: tRGBQuad; d1, d12: single): tRGBQuad;
  //       Renvoie la couleur dégradée du point distant de d1 par rapport à celui de couleur cl1
  //       et situé entre les points de couleurs cl1 et cl2 séparés de d12
  var k: single;
  begin if d12 <> 0 then k := abs(d1 / d12) else k := 0.5;
    Result.rgbRed := round(k * cl1.rgbRed + (1 - k) * cl2.rgbRed);
    Result.rgbGreen := round(k * cl1.rgbGreen + (1 - k) * cl2.rgbGreen);
    Result.rgbBlue := round(k * cl1.rgbBlue + (1 - k) * cl2.rgbBlue);
    Result.rgbReserved := round(k * cl1.rgbReserved + (1 - k) * cl2.rgbReserved);
  end;

  function rgbQuadDegra4: tRGBQuad;
  //       Renvoie la couleur interpolée entre celle des 4 points
  begin cl30 := rgbQuadDegradee(cl0, cl3, xr - k * xo1, k);
    cl21 := rgbQuadDegradee(cl1, cl2, xr - k * xo1, k);
    Result := rgbQuadDegradee(cl30, cl21, yr - k * yo1, k);
  end;

begin WS := BmpS.width; HS := bmpS.Height; BmpS.PixelFormat := pf32bit; Bpp := 4;
  Scan0S := Integer(BmpS.ScanLine[0]);
  MLSS := Integer(BmpS.ScanLine[1]) - Scan0S;

  Result := tBitMap.Create;
  WR := trunc(k * BmpS.width); HR := trunc(k * bmpS.Height);
  with Result do begin PixelFormat := pf32bit; width := WR; height := HR end;
  Scan0R := Integer(Result.ScanLine[0]);
  MLSR := Integer(Result.ScanLine[1]) - Scan0R;

  for yr := 0 to HR - 1 do begin
    yo1 := round(yr / k); yo2 := round((yr + k) / k); if yo2 = yo1 then inc(yo2);
    for xr := 0 to WR - 1 do begin
      xo1 := round(xr / k); xo2 := round((xr + k) / k); if xo2 = xo1 then inc(xo2);
      if (yo2 < HS) and (xo2 < WS) then begin
        ScanS := Scan0S; Inc(ScanS, yo1 * MLSS + xo1 * Bpp);
        with cl0 do begin
          rgbReserved := PRGBQuad(scanS)^.rgbReserved;
          rgbRed := PRGBQuad(scanS)^.rgbRed;
          rgbGreen := PRGBQuad(scanS)^.rgbGreen;
          rgbBlue := PRGBQuad(scanS)^.rgbBlue;
        end;
        ScanS := Scan0S; Inc(ScanS, yo1 * MLSS + xo2 * Bpp);
        with cl3 do begin
          rgbReserved := PRGBQuad(scanS)^.rgbReserved;
          rgbRed := PRGBQuad(scanS)^.rgbRed;
          rgbGreen := PRGBQuad(scanS)^.rgbGreen;
          rgbBlue := PRGBQuad(scanS)^.rgbBlue;
        end;
        ScanS := Scan0S; Inc(ScanS, yo2 * MLSS + xo1 * Bpp);
        with cl1 do begin
          rgbReserved := PRGBQuad(scanS)^.rgbReserved;
          rgbRed := PRGBQuad(scanS)^.rgbRed;
          rgbGreen := PRGBQuad(scanS)^.rgbGreen;
          rgbBlue := PRGBQuad(scanS)^.rgbBlue;
        end;
        ScanS := Scan0S; Inc(ScanS, yo2 * MLSS + xo2 * Bpp);
        with cl2 do begin
          rgbReserved := PRGBQuad(scanS)^.rgbReserved;
          rgbRed := PRGBQuad(scanS)^.rgbRed;
          rgbGreen := PRGBQuad(scanS)^.rgbGreen;
          rgbBlue := PRGBQuad(scanS)^.rgbBlue;
        end;
        ScanR := Scan0R; Inc(ScanR, yr * MLSR + xr * Bpp);
        PRGBQuad(scanR)^ := rgbQuadDegra4;
      end; // if yo2
    end; // for  xr
    if Assigned(ProgressCallBack) then ProgressCallBack;
  end; // for  yr
end; // StretchBmpAlpha

... et voici pour son utilisation :

procedure TFrmDemoMain.btnStretchBmpAlphaClick(Sender: TObject);
begin LabMis.caption := ''; LabMis.Update;
  GTC := GetTickCount;
  imgResized.Picture.Bitmap.Assign(StretchBmpAlpha(imgOriginal.Picture.Bitmap, ScaleValue, ProgressBar2.StepIt));
  LabMis.caption := 'StretchBmpAlpha Mis : ' + intToStr(GetTickCount - GTC) + ' ms';
  imgResized.Invalidate;
  ProgressBar2.Position := 0;
end;

Résultats de tests comparatifs de la vitesse pour un agrandissement de 400% d'un bitmap-source de 683x1024 qui passe donc à 2732x4096 :
- avec la StretchBlt      : mis   140 ms,
- avec la StretchBmpAlpha : mis  1030 ms,
- avec la Bicubique       : mis 11279 ms.

A+.

Commentaire de barbichette le 06/08/2012 07:46:53 10/10

Salut,
Je pense que StretchBlt gagne aussi du temps car c'est codé en parti en assembleur.
Pour les maîtres du domaines, il faudrait reprendre l'unité de Cirec pour passer des morceaux en assembleur. Il me semble que certains calculs peuvent être envoyé au coproc mathématique pour améliorer les choses.
J'aime bien réfléchir à ce genre d'optimisation. Je vais donc essayer de trouver du temps pour y jeter un coup d'½il...
Et puis 10/10, c'est quand même pas mal...

Barbichette

Commentaire de pseudo3 le 06/08/2012 10:52:33

Bonjour,

1) "Je pense que StretchBlt gagne aussi du temps car c'est codé en partie en assembleur. Pour les maîtres du domaines, il faudrait reprendre l'unité de Cirec pour passer des morceaux en assembleur" : Exact qu'avec de l'Asm on peut gagner en speed, mais pour ma part je suis nul en Asm.

2) Rectification d'une petite erreur dans le code de StretchBmpAlpha du  05/08/2012 14:44:23 : Remplacer la sous-fonction encapsulée rgbQuadDegradee par la suivante :

function rgbQuadDegradee(cl1, cl2: tRGBQuad; d1, d12: single): tRGBQuad;
  //       Renvoie la couleur dégradée du point distant de d1 par rapport à celui de couleur cl1
  //       et situé entre les points de couleurs cl1 et cl2 séparés de d12
  var k: single;
  begin if d12 <> 0 then k := abs(d1/d12) else k := 0.5;
    Result.rgbRed := round((1-k)*cl1.rgbRed + k*cl2.rgbRed);
    Result.rgbGreen := round((1-k)*cl1.rgbGreen + k*cl2.rgbGreen);
    Result.rgbBlue := round((1-k)*cl1.rgbBlue + k*cl2.rgbBlue);
    Result.rgbReserved := round((1 - k)*cl1.rgbReserved + k*cl2.rgbReserved);
  end;

... j'avais inversé l'affectation des pondérations (1-k) et k des composantes R,G,B

3) Résultats de tests comptant le pourcentage de pixels différents obtenus avec les différentes méthodes : Bicubique/Stretch : 2,28 % et Bicubique/StretchBmpAlpha 2,38 % avec l'agrandissement de 400% d'un bitmap-source de 683x1024 qui passe à 2732x4096 pixels.

A+.

Commentaire de pseudo3 le 07/08/2012 15:06:11

Bonjour,

Voici une deuxième version de StretchBmpAlpha basée sur le même principe du dégradé de couleurs avec une simplification qui améliore un peu la vitesse et une correction qui améliore la qualité du rendu visuel : le rendu soutient les comparaisons avec la Bicubique et s'obtient environ 13 fois plus rapidement qu'avec la Bicubique lors d'un agrandissement de 400%.      

function StretchBmpAlpha2(const BmpS: tBitMap; k: single; const ProgressCallBack: TObjectProc = nil): tBitMap;
// k = coefficient multiplicateur d'échelle
var
  // Le BitMap-Source :
  WS, HS: integer;
  Scan0S: Integer; // Valeur du pointeur d'entrée dans le Bitmap-Source.
  ScanS: Integer; // Pointeur temporaire à incrémenter.
  MLSS: Integer; // Memory Line Size (en bytes) du Bitmap-Source.
  // Le BitMap-result :
  WR, HR: integer;
  Scan0R: Integer; // Valeur du pointeur d'entrée dans le Bitmap-Result.
  ScanR: Integer; // Pointeur temporaire à incrémenter.
  MLSR: Integer; // Memory Line Size du Bitmap-Result.
  Bpp: Integer; // Bytes par pixel des 2 Bitmap's
  cl0, cl1, cl2, cl3: tRGBQuad; // Couleurs des pixels des 4 angles
  cl30, cl21: tRGBQuad; // Couleurs intermédiaires interpolées entre cl3,cl0 d'une part et entre cl2,cl1 d'autre part
  xr, yr, xo1, xo2, yo1, yo2: integer; dxo, dyo: single; // Coordonnées

  function rgbQuadDegra2(cl1, cl2: tRGBQuad; d1, d12: single): tRGBQuad;
  //       Dégradé entre deux couleurs : Renvoie la couleur dégradée du point distant de d1 par rapport à celui de couleur cl1
  //       et situé entre les points de couleurs cl1 et cl2 séparés de d12
  var k: single;
  begin if d12 <> 0 then k := d1 / d12 else k := 0.5;
    Result.rgbRed := round((1 - k) * cl1.rgbRed + k * cl2.rgbRed);
    Result.rgbGreen := round((1 - k) * cl1.rgbGreen + k * cl2.rgbGreen);
    Result.rgbBlue := round((1 - k) * cl1.rgbBlue + k * cl2.rgbBlue);
    Result.rgbReserved := round((1 - k) * cl1.rgbReserved + k * cl2.rgbReserved);
  end;

  function rgbQuadDegra4: tRGBQuad;
  //       Renvoie la couleur interpolée entre celle des 4 points d'angle
  begin cl30 := rgbQuadDegra2(cl0, cl3, dxo, 1);
    cl21 := rgbQuadDegra2(cl1, cl2, dxo, 1);
    Result := rgbQuadDegra2(cl30, cl21, dyo, 1);
  end;

begin
  k:=abs(k);
  WS := BmpS.width; HS := bmpS.Height; BmpS.PixelFormat := pf32bit; Bpp := 4;
  Scan0S := Integer(BmpS.ScanLine[0]);
  MLSS := Integer(BmpS.ScanLine[1]) - Scan0S;

  Result := tBitMap.Create;
  WR := trunc(k * BmpS.width); HR := trunc(k * bmpS.Height);
  with Result do begin PixelFormat := pf32bit; width := WR; height := HR end;
  Scan0R := Integer(Result.ScanLine[0]);
  MLSR := Integer(Result.ScanLine[1]) - Scan0R;

  for yr := 0 to HR - 1 do begin
    yo1 := trunc(yr / k); yo2 := trunc((yr + k) / k); if yo2 = yo1 then inc(yo2);
    dyo := frac(yr / k);
    for xr := 0 to WR - 1 do begin
      xo1 := trunc(xr / k); xo2 := trunc((xr + k) / k); if xo2 = xo1 then inc(xo2);
      dxo := frac(xr / k);
      // Arrivé ici xo1,yo1 et xo2,yo2 sont les coord's des 4 pixels du bmp-source formant un carré de 2x2
      // et à partir desquels on fera un dégradé de couleurs en fonction de dxo,dyo dans le carré de taille k*k correspondant du bmp-result.
      if (yo2 < HS) and (xo2 < WS) then begin
        // Identification des 4 couleurs de base :
        ScanS := Scan0S; Inc(ScanS, yo1 * MLSS + xo1 * Bpp); cl0 := PRGBQuad(scanS)^;
        ScanS := Scan0S; Inc(ScanS, yo1 * MLSS + xo2 * Bpp); cl3 := PRGBQuad(scanS)^;
        ScanS := Scan0S; Inc(ScanS, yo2 * MLSS + xo1 * Bpp); cl1 := PRGBQuad(scanS)^;
        ScanS := Scan0S; Inc(ScanS, yo2 * MLSS + xo2 * Bpp); cl2 := PRGBQuad(scanS)^;
        // Le dégradé de couleurs :
        ScanR := Scan0R; Inc(ScanR, yr * MLSR + xr * Bpp);
        PRGBQuad(scanR)^ := rgbQuadDegra4;
      end; // if yo2
    end; // for  xr
    if Assigned(ProgressCallBack) then ProgressCallBack;
  end; // for  yr
end; // StretchBmpAlpha2

A+.

Commentaire de barbichette le 07/08/2012 20:10:58

Bon, voilà une petite amélioration en assembleur...
C'est la fonction BiCubicR mais en assembleur.
On vois une légère amélioration sur les grandes images.

function BiCubicRAsm(const x: Extended): Extended;
Const
C2:extended=2;
C4:extended=4;
C6:extended=6;
asm
  FLDZ // on charge 0 sur la pile
// ============================
// ========================= xp2
  FLD x       // charge x
  FLD C2      // charge 2
  FADDP       // addition

  FTST        // x+2>0 ?
  FSTSW   AX
  FWAIT
  SAHF
  JBE      @xp2_zero

  FLD ST    //xp2=xp2^3
  FMUL ST,ST
  FMULP
  FADDP      // on ajoute au résultat
  JMP @suite_xp2

@xp2_zero:
  FSTP   ST(0) // retire le résultat x+2 de la pile

@suite_xp2:

// ============================
// ========================= xp1
  FLD x       // charge x
  FLD1        // charge 1
  FADDP       // addition

  FTST        // x+1>0 ?
  FSTSW   AX
  FWAIT
  SAHF
  JBE      @xp1_zero

  FLD ST    //xp1=-4* xp1^3
  FMUL ST,ST
  FMULP
  FLD C4
  FMULP

  FSUBP        // on soustrait au résultat
  JMP @suite_xp1

@xp1_zero:
  FSTP   ST(0) // retire le résultat x+1 de la pile

@suite_xp1:


// ============================
// ========================= x
  FLD x       // charge x

  FTST        // x+1>0 ?
  FSTSW   AX
  FWAIT
  SAHF
  JBE      @x_zero

  FLD ST    //xx=6* x^3
  FMUL ST,ST
  FMULP
  FLD C6
  FMULP

  FADDP        // on ajoute au résultat
  JMP @suite_x

@x_zero:
  FSTP   ST(0) // retire le résultat x+1 de la pile

@suite_x:

// ============================
// ========================= xm1
  FLD x       // charge x
  FLD1        // charge 1
  FSUBP       // addition

  FTST        // x+1>0 ?
  FSTSW   AX
  FWAIT
  SAHF
  JBE      @xm1_zero

  FLD ST    //xp1=-4* xp1^3
  FMUL ST,ST
  FMULP
  FLD C4
  FMULP

  FSUBP  // on soustrait au résultat

  JMP @suite_xm1

@xm1_zero:
  FSTP ST(0) // retire le résultat x+1 de la pile

@suite_xm1:


// r /6
  FLD C6
  FDIV

  FWAIT
end;

Commentaire de pseudo3 le 08/08/2012 09:21:34

Bonjour,

1) Tests avec utilisation de BiCubicRAsm lors d'un agrandissement de 400% d'un bitmap-source de 683x1024 qui passe à 2732x4096 :
- avec la StretchBlt      : mis  141 ms,
- avec la StretchBmpAlpha : mis  796 ms,
- avec la BicubiqueAsm    : mis 9111 ms contre les 11279 ms de Bicubique sans Asm.
Conclusions :
C'est dans la BiCubicScale (partie sans assembleur) qu'il y a des trucs qui ralentissent.
Cela m'encourage à chercher des gains de speed avec la StretchBmpAlpha.

2) J'en profite pour rectifier ce que j'ai dit le 06/08/2012 10:52:33 à propos des pourcentages de pixels différents obtenus avec les différentes méthodes : les 2% sont faux car j'avais utilisé sans vérification un code de comptage trouvé sur le net qui était complètement vérolé.
Et dans un deuxième temps j'ai constaté que ce mode de comparaison n'était absolument pas significatif car les BitMap-Result obtenus avec les trois méthodes étaient légèrement décalés les uns par rapport aux autres : Le centre d'un point brillant sur l'oeil du portrait que j'avais utilisé pour l'agrandissement à 400% était situé aux coordonnées :
- 505,1002 sur le bitMap obtenu avec StretchBlt,
- 504,1000 sur celui obtenu avec StretchBmpAlpha,
- 504,1004 avec la Bicubique.

A+.

Commentaire de MAURICIO le 08/08/2012 10:35:17 administrateur CS

Il serait interessant de faire des tests de réduction.
J' utilise StretchBlt ici:
http://www.delphifr.com/codes/COMPOSANT-TCYBOOK-ALBUM-PHOTO-VIRTUEL_54443.aspx

A+

Commentaire de cirec le 08/08/2012 13:43:20 administrateur CS

Salut à tous,

merci de l'intérêt que vous portez à ce code.

<< C'est dans la BiCubicScale (partie sans assembleur) qu'il y a des trucs qui ralentissent. >>
Que le méthode Bicubique soit la plus lente et tout à fait normal ... elle utilise une matrice 4x4 et donc, chaque pixels de l'image finale est calculé avec les 16 pixels les plus proche de l'image originale. Donc plus on agrandit l'image finale plus le temps d'exécution en prend une claque ... par contre pour la réduction les temps sont tout à fait acceptables et plus on réduit moins elle met de temps.

Maintenant si on veut un résultat de qualité ... j'ai trouvé un autre code:
http://forums.getpaint.net/index.php?/topic/23601-2d-image-scaling-algorithms/#entry366643

voir les résultat ici:
http://forums.getpaint.net/index.php?/topic/23601-2d-image-scaling-algorithms/#entry367822

Les résultats visuels sont stupéfiants mais pour l'adaptation c'est pas encore gagné ... si quelqu'un se sent l'envie d'essayer il ne faut pas se gêner ... hein :D

Je veux remercier tout particulièrement Pseudo3 et Barbichette pour leurs contributions ... je vais inclure leurs codes au zip afin que tout le monde puisse tester et juger.

@++

Commentaire de pseudo3 le 08/08/2012 14:02:38

Bonjour,

1) "Je vais inclure leurs codes au zip afin que tout le monde puisse tester et juger." :

Ok, dans ce cas voici la version 3 de StretchBmpAlpha qui est un peu plus rapide que les versions précédentes (j'ai modifié entre-autres la sous fonction rgbQuadDegra4 qui calcule directement les interpolations sans faire appel à une autre sous-fonction): le temps d'exécution pour l'agrandissement de 400% est descendu de 796 ms à 577 ms.
(j'ai aussi viré le ProgressCallBack pensant qu'il mangeait du temps, mais on peut le ré-ajouter car ça ne bouffe que des clopinettes).

function StretchBmpAlpha3(const BmpS: tBitMap; k: single): tBitMap;
// k = coefficient multiplicateur d'échelle
var
  // Le BitMap-Source :
  WS, HS: integer;
  Scan0S: Integer; // Valeur du pointeur d'entrée dans le Bitmap-Source.
  ScanS: Integer; // Pointeur temporaire à incrémenter.
  MLSS: Integer; // Memory Line Size (en bytes) du Bitmap-Source.
  // Le BitMap-result :
  WR, HR: integer;
  Scan0R: Integer; // Valeur du pointeur d'entrée dans le Bitmap-Result.
  ScanR: Integer; // Pointeur temporaire à incrémenter.
  MLSR: Integer; // Memory Line Size du Bitmap-Result.
  Bpp: Integer; // Bytes par pixel des 2 Bitmap's
  c0, c1, c2, c3: tRGBQuad; // Couleurs des pixels des 4 angles
  xr, yr, xo1, xo2, yo1, yo2: integer; dxo, dyo: single; // Coordonnées
  xrSurk, yrSurk: single; // Rapports

  function rgbQuadDegra4: tRGBQuad;
  //       Renvoie la couleur interpolée entre celle des 4 points d'angle
  var unMoinsDxo, DyoDxo: single;
  begin unMoinsDxo := 1.0 - dxo; DyoDxo := dyo * dxo;
    Result.rgbRed := trunc(unMoinsDxo * (c0.rgbRed - dyo * (c0.rgbRed - c1.rgbRed)) + dxo * c3.rgbRed - DyoDxo * (c3.rgbRed - c2.rgbRed));
    Result.rgbGreen := trunc(unMoinsDxo * (c0.rgbGreen - dyo * (c0.rgbGreen - c1.rgbGreen)) + dxo * c3.rgbGreen - DyoDxo * (c3.rgbGreen - c2.rgbGreen));
    Result.rgbBlue := trunc(unMoinsDxo * (c0.rgbBlue - dyo * (c0.rgbBlue - c1.rgbBlue)) + dxo * c3.rgbBlue - DyoDxo * (c3.rgbBlue - c2.rgbBlue));
    Result.rgbReserved := trunc(unMoinsDxo * (c0.rgbReserved - dyo * (c0.rgbReserved - c1.rgbReserved)) + dxo * c3.rgbReserved - DyoDxo * (c3.rgbReserved - c2.rgbReserved));
  end;

begin
  k := abs(k);
  WS := BmpS.width; HS := bmpS.Height; BmpS.PixelFormat := pf32bit; Bpp := 4;
  Scan0S := Integer(BmpS.ScanLine[0]);
  MLSS := Integer(BmpS.ScanLine[1]) - Scan0S;

  Result := tBitMap.Create;
  WR := trunc(k * BmpS.width); HR := trunc(k * bmpS.Height);
  with Result do begin PixelFormat := pf32bit; width := WR; height := HR end;
  Scan0R := Integer(Result.ScanLine[0]);
  MLSR := Integer(Result.ScanLine[1]) - Scan0R;

  for yr := 0 to HR - 1 do begin
    yrSurk := yr / k; yo1 := trunc(yrSurk); yo2 := trunc((yr + k) / k); dyo := frac(yrSurk);
    for xr := 0 to WR - 1 do begin
      xrSurk := xr / k; xo1 := trunc(xrSurk); xo2 := trunc((xr + k) / k); dxo := frac(xrSurk);
      // Arrivé ici xo1,yo1 et xo2,yo2 sont les coord's des 4 pixels du bmp-source formant un carré de 2x2 (dont les centres forment un carré de 1x1 pixel)
      // et à partir desquels on fera un dégradé de couleurs en fonction de dxo,dyo dans le carré de taille k*k correspondant du bmp-result.
      if (yo2 < HS) and (xo2 < WS) then begin
        // Identification des 4 couleurs de base :
        ScanS := Scan0S; Inc(ScanS, yo1 * MLSS + xo1 * Bpp); c0 := PRGBQuad(scanS)^; inc(ScanS, Bpp); c3 := PRGBQuad(scanS)^;
        ScanS := Scan0S; Inc(ScanS, yo2 * MLSS + xo1 * Bpp); c1 := PRGBQuad(scanS)^; inc(ScanS, Bpp); c2 := PRGBQuad(scanS)^;
        // Le dégradé de couleurs :
        ScanR := Scan0R; Inc(ScanR, yr * MLSR + xr * Bpp);
        PRGBQuad(scanR)^ := rgbQuadDegra4;
      end; // if yo2
    end; // for  xr
    //if Assigned(ProgressCallBack) then ProgressCallBack;
  end; // for  yr
end; // StretchBmpAlpha3

2) Maintenant si on veut un résultat de qualité ... j'ai trouvé un autre code ... Les résultats visuels sont stupéfiants mais pour l'adaptation c'est pas encore gagné ... si quelqu'un se sent l'envie d'essayer il ne faut pas se gêner"
... Ok, je vais y jeter un coup d'oeil pour soupeser les difficultés d'adaptation.

A+.

Commentaire de pseudo3 le 08/08/2012 15:16:05

Re-bonjour,

1) A Mauricio "Il serait interessant de faire des tests de réduction" :
... Quels types de tests ? En principe les réductions posent moins de problèmes et sont plus rapides que les agrandissements.
"J'utilise StretchBlt ici " :
... Ok, vu. Mais StretchBlt est également utilisé dans le code de Cirec.

2) A Cirec : Vu les liens http://forums.getpaint.net/etc, les résultats visuels sont effectivement stupéfiants mais vu que le seul code que j'y ai trouvé est écrit en C pour ma part je n'y comprends rien. Dommage que l'on n'ait pas accès aux bases mathématiques de son algorithme ou à un pseudo-code.

A+.

Commentaire de MAURICIO le 08/08/2012 15:23:00 administrateur CS

"Quels types de tests ?"
Bem les mêmes!
Est-ce que StretchBtlt est encore le plus rapide par exemple ... dans les mêmes proportions?

A+

Commentaire de pseudo3 le 08/08/2012 15:37:46

Re-bonhjour,

"Est-ce que StretchBtlt est encore le plus rapide par exemple ... dans les mêmes proportions?" :
En réduisant à 25% un bitMap de 2732x4096 pour en obtenir un de 683x1024 :
- avec la StretchBlt      : mis  78 ms,
- avec la StretchBmpAlpha : mis  63 ms,
- avec la BicubiqueAsm    : mis 593 ms.
... mais les 78 et 63 ms c'est environ kif-kif car mon chrono c'est GetTickCount et pour les durées inférieures à 100 ms c'est un peu piffométrique.

A+.

Commentaire de Caribensila le 08/08/2012 15:41:27 10/10

Salut,

Il est difficile de comparer le rééchantillonnage bicubique et l'algo du lien que propose Cirec. Ce sont deux approches différentes.

Le rééchantillonnage bicubique est un algo non-adaptatif.  C'est à dire qu'il traite tous les pixels de la même façon, avec la même formule.

Un algo adaptatif partage l'image en zones différentes selon la texture, les contours, les couleurs, etc... Et chaque zone est traitées différemment. C'est ce que fait l'algo du lien de Cirec.
Autant dire que les possibilités en fonction du choix des zones sont infinies. Ce sont des algos utilisés par les logiciels pour pros (Qimage, PhotoZoom Pro, Genuine Fractals, etc...).

Les algos non-adaptatifs (interpolation bilinéaire, bicubique, au plus proche, splyne, sinc, lanczos, etc...) sont généralement les meilleurs quand on effectue une distorsion ou une rotation de l'image. En revanche, les algos adaptatifs sont meilleurs quand on agrandit une image.

Dans tous les cas, le résultat est fortement dépendant de l'image source. On n'agrandit pas une toile de Mondrian comme celle d'un Monnet. Et le résultat dépendra du choix + ou - pertinent de l'utilisateur, pas de l'algo !

Reste donc encore à coder l'algo qui fera le meilleur choix de traitement en fonction de l'image source... Un algo qui choisirait le meilleur algo, en quelque sorte. ^ ^


PS: Comme je n'avais jamais trouvé cet algo en Delphi sur le Net, je mets un 10/10 sans hésiter et je dis merci Cirec.

Commentaire de MAURICIO le 08/08/2012 15:59:02 administrateur CS

Je vais faire un algo pour savoir si mes critères de notation sont déterminés par l' auteur ou par le type de source ...

En attendant, bonnes vacances!

Commentaire de pseudo3 le 08/08/2012 16:37:00

Re-salut,

Caribensila : "Un algo adaptatif partage l'image en zones ... Et chaque zone est traitées différemment. C'est ce que fait l'algo du lien de Cirec. En revanche, les algos adaptatifs sont meilleurs quand on agrandit une image" :
... Bin on est justement dans le cas d'agrandissements ... donc ça vaudrait bien le coup de pouvoir coder sous Delphi l'algo du lien de Cirec.

A+.

Commentaire de Caribensila le 08/08/2012 17:15:49

Salut PSEUDO3,

C'est sûr que ça vaudrait le coup.

Mais ce que je voulais dire c'est qu'il faudra comparer la qualité du résultat plutôt que les temps de traitement.
On ne compare pas un MacDo avec une Poularde demi-deuil. ^ ^

L'algo en question ne doit pas être d'un abord facile. De plus, en C, ça n'arrange rien pour moi...  :'(

Commentaire de barbichette le 08/08/2012 20:26:19

il y a encore une petite optimisation à faire dans le code en assembleur.
Je viens de voir que si x+2<0 alors tous les autres If sont faux aussi (x+1<0, x<0 et x-1<0) donc je vais faire une petite modif et l'envoyer en perso à Cirec afin de sortir de la fonction dès qu'un if est faux.

Bon courage pour l'algo adaptatif...

Commentaire de pseudo3 le 09/08/2012 11:54:10

Bonjour,

1) Caribensila "Mais ce que je voulais dire c'est qu'il faudra comparer la qualité du résultat plutôt que les temps de traitement." :
... Bin, c'est pas interdit de comparer les deux, et ta remarque "On n'agrandit pas une toile de Mondrian comme celle d'un Monnet" m'incite d'ailleurs à faire ce type de comparaisons.
"L'algo en question ne doit pas être d'un abord facile. De plus, en C, ça n'arrange rien pour moi" : C'est pareil pour moi, tout ce que sais du C c'est qu'il faut traduire les {} par begin et end. (lol).

2) Barbichette : De mon côté je viens de voir que dans BiCubicRAsm(x) comme dans sa cousine BiCubicR(x) les valeurs du paramètre d'entrée x varient entre xmin = -1,75 xmax = 2 avec un pas constant de 0.25 donc j'ai utilisé les deux d'abord pour comparer leurs résultats (ils sont kif-kif) et ensuite j'ai stocké les résultats dans la constante pré-calculée BiCubicRPrecal: array[1..16] of Extended de la nouvelle procedure BiCubicScale ci-dessous.

Résultats de tests lors d'un agrandissement de 400% d'un bitmap-source de 683x1024 qui passe à 2732x4096 :
- avec la StretchBlt      : mis  142 ms,
- avec la StretchBmpAlpha : mis  797 ms,
- avec la BicubiqueAsm    : mis 9112 ms contre les 11279 ms de Bicubique sans Asm.
- avec la Bicubique et sa constante BiCubicRPrecal : 3885 ms : 2,34 fois plus rapide qu'en l'absence de pré-calcul.

Et voici le code :

procedure BiCubicScale(const bmSrc: TBitmap; const WS, HS: Integer;
  const bmDst: TBitmap; const WD, HD: Integer;
  const ProgressCallBack: TObjectProc = nil);

var xd, yd, xs, ys, ii, jj, n, m, index: Integer;
  cx, cy, dx, dy, weight, red, green, blue, alpha: Extended;
  col: RGBQuad;
  PbmSrc, PbmDst: Cardinal; Ind1, Ind2: integer;
const BiCubicRPrecal: array[1..16] of Extended
  = (0.00260416666666667, 0.0208333333333333, 0.0703125, 0.166666666666667,
    0.315104166666667, 0.479166666666667, 0.611979166666667, 0.666666666666667,
    0.611979166666667, 0.479166666666667, 0.315104166666667, 0.166666666666667,
    0.0703125, 0.0208333333333333, 0.00260416666666667, 0.0);
begin
  bmSrc.PixelFormat := pf32bit;
  bmDst.PixelFormat := pf32bit;
  PbmSrc := Cardinal(bmSrc.ScanLine[HS - 1]);
  PbmDst := Cardinal(bmDst.ScanLine[HD - 1]);

  for yd := 0 to HD - 1 do
  begin
    for xd := 0 to WD - 1 do
    begin xs := (xd * WS) div WD;
      ys := (yd * HS) div HD;
      cx := xd * WS / WD;
      cy := yd * HS / HD;
      dx := cx - xs;
      dy := cy - ys;
      red := 0;
      green := 0;
      blue := 0;
      alpha := 0;
      for m := -1 to 2 do
        for n := -1 to 2 do
        begin
          ii := xs + m;
          jj := ys + n;
          if ii < 0 then ii := 0;
          if ii >= WS then ii := WS - 1;
          if jj < 0 then jj := 0;
          if jj >= HS then jj := HS - 1;
          index := jj * WS + ii;
          Ind1 := round(4 * (m - dx)) + 8; // Indice de correspondance avec la table précalculée
          Ind2 := round(4 * (n - dy)) + 8; // Idem
          weight := BiCubicRPrecal[ind1] * BiCubicRPrecal[ind2]; // remplace weight := BiCubicRAsm(m - dx) * BiCubicRAsm(n - dy);
          with PRGBQuad(index * 4 + PbmSrc)^ do
          begin red := red + weight * rgbRed;
            green := green + weight * rgbGreen;
            blue := blue + weight * rgbBlue;
            alpha := alpha + weight * rgbReserved;
          end;
        end;
      col.rgbRed := Trunc(red);
      col.rgbGreen := Trunc(green);
      col.rgbBlue := Trunc(blue);
      col.rgbReserved := Trunc(alpha);
      PRGBQuad((yd * WD + xd) * 4 + PbmDst)^ := col;
    end;
    if Assigned(ProgressCallBack) then ProgressCallBack;
  end;
end;

A+.

Commentaire de pseudo3 le 10/08/2012 09:47:58

Bonjour,

Oups! Prière de ne pas prendre en compte mon code du 09/08/2012 11:54:10 car je viens de me rendre compte que sa constante pré-calculée (BiCubicRPrecal: array[1..16] of Extended) n'est mathématiquement valable que pour des agrandissements d'environ 400% bien qu'on ne le remarque pas visuellement dans le cas d'agrandissements de 200% ou 300% ni dans le cas de réductions ... mais pour des agrandissements s'écartant trop des 400% le résultat n'est plus vraiment du Bicubique : Dommage car le gain de speed obtenu grâce au pré-calcul était intéressant.

A+.

Commentaire de barbichette le 10/08/2012 21:46:36

Bon, pour les fous de redimensionnement, j'ai converti l'algo du lien de Cirec en pascal.
Je fait un bout de code d'exemple avec et je le poste.
A+

Commentaire de pseudo3 le 11/08/2012 12:01:18

Bonjour Barbichette,

"j'ai converti l'algo du lien de Cirec en pascal. Je fais un bout de code d'exemple avec et je le poste" :
... Excellente idée. Impatient de voir les résultats visuels "stupéfiants".

A+.


Commentaire de barbichette le 11/08/2012 12:34:55

Voilà le résultat...
http://www.delphifr.com/codes/REDIMENSIONNEMENT-XBR_54505.aspx
A+

Commentaire de barbichette le 11/08/2012 12:36:45

Remarque :
Cette algorithme fonctionnement bien sur des images à fort contrastes (cartoon, dessin...)
Il est un peu moins impressionnant sur des vrais photos.
enfin, d'après moi...

Commentaire de cirec le 11/08/2012 22:40:53 administrateur CS

ouahhh !!!

'tin les gars vous avez fait très fort ....
merci pour votre implication sur ce source.

Désolé de ne pas encore avoir posté de mise à jour ... pour la petite histoire, un problème musculaire m'a, pour ainsi dire, cloué au lit pour 3 jours ... rien de grave mais c'est très handicapant et douloureux :D.

je pense que d'ici la fin du weekend je posterai le code avec vos changements ;)

et Merci tout particulièrement à Barbichette pour la traduction du code xBR en Pascal. Bravo.
@++

Commentaire de cirec le 17/08/2012 00:17:52 administrateur CS

Bon ...
la mise à jour est enfin en ligne ..... wouah ^^

les procédures de Pseudo3 et Barbichette sont ajoutées au code et les temps d'exécution sont aussi affichés.

j'ai du apporter quelques petites modifications au code de Pseudo3 afin d'éviter des fuites de mémoire et un débordement de pile.

@Tous:
ne jamais utiliser le résultat d'une fonction qui crée un objet sans garder une trace de ce dernier sous peine de ne pas pouvoir libérer la mémoire qui lui a été alloué.

ainsi cette ligne de code ci-dessous, crée un objet TBitmap et l'assigne au TImage mais ce TBitmap n'est jamais libéré ... et à chaque appel la fuite se répète:
  imgResized.Picture.Bitmap.Assign(StretchBmpAlpha3(imgOriginal.Picture.Bitmap, ScaleValue, ProgressBar1.StepIt));

la bonne méthode est:
TmpBMP := StretchBmpAlpha3(imgOriginal.Picture.Bitmap, ScaleValue, ProgressBar1.StepIt);
try
  imgResized.Picture.Bitmap.Assign(TmpBMP);
finally
  TmpBMP.Free; // <----- ici le Bitmap est libéré correctement
end;

le reste vous le verrez dans le code :D

Par contre j'ai remarque une chose assez étrange:
j'ai compilé le code sous D7 et D2009
sous D7 ma version est plus rapide que la version ASM de Barbichette et quasi aussi rapide que la pré-calculée de Pseudo3 !!!
sous D2009 j'ai le même ordre de rapidité que celui donné par Pseudo3 dans son message du 09/08/2012 11:54:10

comprendra qui pourra ...
sur ce...
@++

Commentaire de pseudo3 le 17/08/2012 10:23:17

Bonjour,

1) "ne jamais utiliser le résultat d'une fonction qui crée un objet sans ...' : Oups j'avais oublié la libération. Merci pour le rappel.

2) Résultats de tests sous D5 lors d'un agrandissement de 400% d'un bitmap-source de 683x1024 qui passe à 2732x4096 :
- StretchBlt & Halftone : 118 ms,
- StretchBmpAlpha3 : 594 ms,
- Bicubique & precalc : 2994 ms (version modifiée utilisant des valeurs de type Single) (*),
- Bicubique & precalc : 4713 ms (version d'origine utilisant des valeurs de type Extended) (*),
- Bicubique ASM : 6169 ms
- Bicubique Original : 8089 ms.
(*) : vu le gain de vitesse autant profiter du type single dont la précision est amplement suffisante sachant que la taille d'un pixel d'un écran de 100 PixelsPerInch fait 0,254 millimètre donc on n'a pas besoin d'une précision microscopique.

3) A Cirec, à propos de "Que le méthode Bicubique soit la plus lente est tout à fait normal ... elle utilise une matrice 4x4 et donc, chaque pixel de l'image finale est calculé avec les 16 pixels les plus proches de l'image originale" :

Ce qui m'intrigue : voici un schéma d'agrandissement où chaque lettre majuscule représente un pixel de couleur connue

AB <- ici 4 pixels contigüs du BiMap-Original dont les centres forment un carré de 1x1 pixel
CD

et ici l'agrandissement où les quelques tirets '-' représentent les pixels de l'image agrandie dont on cherche les couleurs interpolées entre celles de A,B,C,D :

A------B
--------
--------
C------D

donc si chaque pixel de l'image finale est calculé avec les 16 pixels les plus proches de l'image originale, ces pixels les plus proches sont forcément des pixels situés à l'extérieur du carré ABCD de l'image originale dont les pixels sont contigüs, d'où la petite question qui m'intrigue :
Quelle est la raison scientifique (de colorimétrie, ou d'optique visuelle, ou autre) qui justifierait le fait de tenir compte des couleurs des pixels situés à l'extérieur du carré ABCD pour calculer les couleurs situées à l'intérieur de ce carré ???  

A+.

Commentaire de Caribensila le 17/08/2012 11:32:05

Ce n'est pas un problème de colorimétrie ou d'optique. C'est un problème mathématique plus général. C'est en fait le principe de l'interpolation.

Sans pouvoir faire de petits croquis, ça ne va pas être facile à expliquer, mais je vais essayer...


Si on te demande de 'deviner' une valeur qui se situe entre seulement deux valeurs données, en général tu feras la moyenne de ces deux valeurs. En revanche, si tu as deux suites de valeurs qui encadrent la(les) valeur(s) à 'deviner', tu pourras calculer et dessiner la courbe que décrivent ces valeurs.
Ensuite, il te suffira de choisir des points situés entre les deux suites et se trouvant sur cette courbe pour avoir une assez bonne approximation.
On sent bien intuitivement que plus les suites données seront longues, plus la courbe calculée sera exacte.

Dans une image, ces suites sont les valeurs situées à l'extérieur du carré ABCD.


On sent bien aussi intuitivement que cette approche est surtout valable pour des courbes continues (au sens mathématique) comme, par exemple, un relevé de température. Mais en graphisme c'est loin d'être toujours le cas. Dans une image il peut y avoir de brusque changement de colorimétrie. C'est pour cette raison que les algos adaptatifs donnent généralement de meilleur résultats car ils décèlent ces brusques changement de continuité dans les courbes et les reproduisent à une autre échelle.

Voilà. J'espère avoir un peu éclairé les lanternes. ^ ^

Commentaire de Caribensila le 17/08/2012 12:47:13

Cirec nous dit très justement qu'il faut toujours libérer les objets que l'on crée.
Mais parfois il n'est même pas nécessaire de les créer, et donc de les libérer.
Une maladresse fréquente, même chez les plus grands ^ ^, est d'oublier que le compos TImage contient déjà un TBitmap dans son TPicture. On peut donc simplifier le code ainsi :


procedure TFrmDemoMain.FormCreate(Sender: TObject);
begin
  //aBmp := TBitmap.Create;             <--- Inutile.
  //imgResized.Picture.Bitmap := aBmp;  <--- Inutile.
  aBmp := imgResized.Picture.Bitmap;    <--- suffisant.
  ...
end;


procedure TFrmDemoMain.FormDestroy(Sender: TObject);
begin
  //aBmp.Free;   <--- Inutile. Le TImage libèrera lui-même son TBitmap.
end;


On peut donc même supprimer la variable globale aBmp et utiliser à la place le imgResized.Picture.Bitmap.

Commentaire de Caribensila le 17/08/2012 13:13:42

... Pendant que j'y suis, avec le TImage...
Eviter de se mélanger les pinceaux avec ses TCanvas. TImage dispose en effet de deux Canvas :
- Image1.Canvas
- Image1.Picture.Bitmap.Canvas

J'ai parfois vu :

image1.Canvas.Draw(0,0,UnTGraphic);

au lieu de

image1.Picture.Bitmap.Canvas.Draw(0,0,UnTGraphic);


C'est particulièrement traitre car les deux actions sont acceptées mais le résultat est différent.

Commentaire de pseudo3 le 17/08/2012 14:26:48

Re-bonjour Caribensila,

1) A propos de l'interpolation :
OK sur le principe dans le cas des courbes continues (au sens mathématique) telles qu'un relevé de température pris dans une variation qui est elle même un phénomène continu (au sens physique).
Par contre mes 4 pixels ABCD peuvent être entourés de bruit avec de brusques variations et je n'ai toujours pas d'explication scientifique qui justifierait le fait qu'il faudrait en tenir compte pour calculer les couleurs situées à l'intérieur du carré.

Tu dis "C'est pour cette raison que les algos adaptatifs donnent généralement de meilleur résultats car ils décèlent ces brusques changement de continuité dans les courbes et les reproduisent à une autre échelle" : OK, mais ça c'est plutôt une astuce corrective d'informaticien et même dans ce cas j'aimerais que cette "reproduction à une autre échelle" suive une loi scientifique. (j'ai un faible pour les sciences exactes)

2) A propos de "TImage dispose en effet de deux Canvas" :
Et il parait que le vrai Canvas du TImage soit celui-ci : With TPublicGraphicControl(Image1).Canvas do ...
(lu sur le site de Developpez).

A+.





Commentaire de Caribensila le 17/08/2012 15:19:26

Scientifiquement parlant, il n'y a en effet aucune raison qui puisse faire dire qu'un algo sera plus exact qu'un autre puisque dans tous les cas il s'agit d'"inventer" des données dont on ne dispose pas au départ.
Mais il faut plutôt voir cela sous un aspect probabiliste. Le calcul de probabilité est bien une science dont les résultats n'ont rien de scientifique. Après avoir jeté une pièce de monnaie 20 fois en l'air, elle est tombée 20 fois sur "face". Au prochain lancement, les probabilités nous disent que se sera plutôt un "pile" qui sortira. Et pourtant nous savons tous que "face" ou "pile" ont autant de chances l'un que l'autre de sortir au prochain lancement...

En d'autres termes, l'interpolation ne donne pas des résultats exact sur la couleur des pixels, mais donne cependant un résultat proche d'une réalité scientifiquement calculée. Et ce calcul est basé sur le fait que sur une photo argentique la couleur des "grains" est influencée par la couleur des grains voisins. On n'a en effet jamais un grain noir collé à un grain blanc sur une surface argentique. C'est pour cette raison que l'interpolation est plus efficace sur de vraies photos. Dès qu'on veut agrandir une image informatique pixellisée, le problème est différent car on a souvent un pixel noir côtoyant un pixel blanc. Et on peut être sûr que, sur un agrandissement, les pixels "inventés" devront être soit blancs, soit noirs; et certainement pas gris !
Je le répète donc, l'algo à utiliser dépend beaucoup de l'image source.

Mais aussi, la démo de Cirec nous apprend que certains algos traitent mieux que d'autres ces différents cas et ceux basés sur l'interpolation sont de ceux-là.


Pour le 2), je ne sais pas.
TPublicGraphicControl ?
- J'en ai jamais mangé...  ^ ^

Commentaire de Caribensila le 17/08/2012 15:36:30

... Ah! J'ajoute que pour les images d'infographie il existe une méthode qui résout tous ces problèmes... Ce sont les images vectorisées.

Elles sont en générale plus légères...
Mais aussi plus longues à afficher.  ( Pfff! Y'a toujours un truc qui flotte dans l'bouillon ! )

Commentaire de pseudo3 le 17/08/2012 15:49:07

Re-salut Caribesila,

"Scientifiquement parlant, il n'y a en effet aucune raison qui puisse faire dire qu'un algo sera plus exact qu'un autre puisque dans tous les cas il s'agit d'"inventer" des données dont on ne dispose pas au départ. Mais il faut plutôt voir cela sous un aspect probabiliste" :
Je dirais plutôt qu'un algo sera plus exact qu'un autre si son résultat est probablement plus proche d'une réalité physique qu'un autre.

" ... sur une photo argentique la couleur des "grains" est influencée par la couleur des grains voisins" : OK et idem pour le cas des pixels-voisins du bitMap original dont on peut supposer que les couleurs A,B,C,D ont été influencés par celles de ses voisins et donc ceux qui sont à l'intérieur du carré ABCD agrandi sont à leur tour influencés seulement par les couleurs ABCD et pas par ceux situés à l'extérieur.

"Je le répète donc, l'algo à utiliser dépend beaucoup de l'image source" : Ce qui va bien nous compliquer encore davantage le vie.

"Mais aussi, la démo de Cirec nous apprend que certains algos traitent mieux que d'autres ces différents cas et ceux basés sur l'interpolation sont de ceux-là" :
Bin, ils sont bien tous basés sur une méthode d'interpolation : les uns sur la Bicubique et StretchBmpAlpha3 est basée sur deux interpolations linéaires horizontales entre AB et entre CD suivie d'une troisième interpolation linaire verticale entre les interpolations précédentes.


"TPublicGraphicControl ? J'en ai jamais mangé" : Cela n'interdit pas d'en goûter.

A+.

Commentaire de pseudo3 le 17/08/2012 15:59:55

Re-salut,

"Ah! J'ajoute que pour les images d'infographie il existe une méthode qui résout tous ces problèmes... Ce sont les images vectorisées. Elles sont en général plus légères... Mais aussi plus longues à afficher" :
OK, plus légères en mémoire et sur disque, mais pour les afficher il faut à chaque fois tout re-calculer sans oublier que c'est la galère pour les coder.

A+.

Commentaire de cirec le 17/08/2012 17:27:51 administrateur CS

@Pseudo3:
Salut,
certes j'ai traduit le code en Pascal mais je ne prétend pas avoir compris toutes les finesses de cette méthode. J'en ai saisi les grandes lignes mais de grandes zones d'ombres persistent encore ^^
il faudra te contenter des explications de Caribensila ;)

@Caribensila:

Salut,
tu as entièrement raison .. en fait le problème n'est pas d'avoir oublié que le TImage contient déjà un TBitmap mais que ce dernier soit déjà créé ... mais on arrive très vite à des fuites de mémoire en utilisant ces composants, en fonction de ce que l'on fait et surtout comment, et de ce fait je travaille machinalement comme ceci ce qui me garanti une libération dans tous les cas de figure ... puisque c'est moi qui l'a fait :D

quand à ceci "Draw(0,0,UnTGraphic);"
je ne m'étendrai pas sur le sujet puisque les deux ont leurs utilités ^^
chacun voit midi à sa porte :D

ps: j'ai modifié le code ;)

Commentaire de pseudo3 le 18/08/2012 09:13:09

Bonjour,

A Cirec :

1) OK, je vais me contenter des explications de Caribensila.

2) A propos de "ps: j'ai modifié le code" : J'ai lu dans l'historique que tu as supprimé la variable globale TBitmap mais en as tu également profité pour remplacer le type Extended par le type Single des variables locales et de la constante pré-calculée dans FastBiCubicScale vue le gain de vitesse signalé dans mon message du 17/08/2012 10:23:17 ??? Ce serait dommage de ne pas en faire profiter tout le monde.

A+.

Commentaire de cirec le 18/08/2012 13:33:17 administrateur CS

re,

voilà les modifications sont faites
j'ai ajouté un bout de code pour tous ceux qui ont plusieurs versions de delphi (comme moi) ... il permet de savoir avec laquelle le code a été compilé. Pour le désactiver il suffit de mettre un point avant la directive comme ceci:
{.$define CompilerVersion}

@Pseudo3:
as-tu remarqué la modification apporté à ton code (StretchBmpAlpha3) ?
pour éviter que le code plante en dehors d'un redimensionnement "plein" 200, 300, 400%

c'est 3 fois rien mais comme tu sembles affectionner le 400% tu es certainement passé à coté de ça ;)

@++

Commentaire de pseudo3 le 18/08/2012 14:22:59

Re-bonjour,

A Cirec :

1) "as-tu remarqué la modification apporté à ton code (StretchBmpAlpha3) ? pour éviter que le code plante en dehors d'un redimensionnement "plein" 200, 300, 400%" :
Si tu veux parler de cette modif :
// *** Modification pour éviter un bug Indice Hors limites
          Ind1 := Max(Min(Ind1, 16), 1);
          Ind2 := Max(Min(Ind2, 16), 1);
alors celle-ci se trouve dans FastBiCubicScale
car dans StretchBmpAlpha3 je n'ai trouvé que celle-ci :
//  WR := trunc(k * BmpS.width); HR := trunc(k * bmpS.Height);
  WR := Round(k * BmpS.width);
  HR := Round(k * bmpS.Height);
mais je n'ai pas pigé le rapport que la "modif pour éviter un bug Indice Hors limites" avait avec un redimensionnement autre que "plein" ?


2) "...tu sembles affectionner le 400% ": En fait j'utilise toujours le 400% et le même bitMap-source lors des tests comparatifs de vitesse ça permet de mieux s'y retrouver dans le schmilblik des résultats lorsqu'il y des changements dans un code.

A+.

Commentaire de cirec le 18/08/2012 15:42:32 administrateur CS

pardon,
oui je parle bien de ceci
// *** Modification pour éviter un bug Indice Hors limites
          Ind1 := Max(Min(Ind1, 16), 1);
          Ind2 := Max(Min(Ind2, 16), 1);

ben si tu redimensionnes à 101% sans la correction ... le code plante

je viens de re-tester et le code plante bien avec le type Extended
et donne un résultat surprenant avec le type Single !!!
dans tous les cas la correction est utile
ps: ce qui est bizarre c'est que le message d'erreur ne soit plus le même ... maintenant j'obtiens
Opération en virgule flottante incorrecte. !!!


et le deuxième (mais c'est du chipotage ça):
//  WR := trunc(k * BmpS.width); HR := trunc(k * bmpS.Height);
  WR := Round(k * BmpS.width);
  HR := Round(k * bmpS.Height);

le trunc induit un décalage qui n'a pas grande incidence sur le code mais qui donne un résultat différent entre la taille annoncée et la taille réelle ... du coup la ProgressBar ne se remplit pas jusqu'au bout.

@++

Commentaire de pseudo3 le 18/08/2012 17:19:26

Re-salut,

"ben si tu redimensionnes à 101% sans la correction ... le code plante"
... OK, je teste en neutralisant temporairement la correction.

"je viens de re-tester et le code plante bien avec le type Extended et donne un résultat surprenant avec le type Single" :
...Moi j'ai été surpris que le simple passage du Extended vers le Single apporte une augmentation de vitesse décelable avec GetTickCount !!!.

"le trunc induit un décalage qui n'a pas grande incidence sur le code mais qui donne un résultat différent entre la taille annoncée et la taille réelle ... du coup la ProgressBar ne se remplit pas jusqu'au bout" :
... Je n'avais pas remarqué car j'avais modifié le code de sorte que la ProgressBar se remette illico à zéro en fin d'exécution.

A+.

Commentaire de cirec le 18/08/2012 18:03:33 administrateur CS

re,
"Moi j'ai été surpris que le simple passage du Extended vers le Single apporte une augmentation de vitesse décelable avec GetTickCount !!!."

oui j'ai également été surpris ... surtout que les gains ne sont pas négligeables !!!

au début j'ai utilisé le type Extended avec les résultats que l'on connait !
puis je suis passé au Double (pour coller au code original) ... avec déjà un gain de temps remarquable

et là avec Single c'est encore plus fort ...
à la base je ne pensais même pas observer une différence sur le temps d'exécution
par contre je m'attendais à une différence sur la qualité ou sur le décalage de l'image. (mais rien de décelable à l'oeil nu)

surprenant quand même !!!

en ce qui concerne le décalage de l'image
il semble avoir été légèrement atténué grace à ceci (-0.5 + ...):
i_in := Floor(-0.5 + i_out * nx / nnx);
j_in := Floor(-0.5 + j_out * ny / nny);
cx := -0.5 + i_out * nx / nnx;
cy := -0.5 + j_out * ny / nny;

@++

Commentaire de Caribensila le 18/08/2012 18:33:41

Ca n'a rien d'étonnant sur une machine en 32 bits car un Single est codé sur 4 octets, comme l'Integer.

Le Double, lui, fait 8 octets (comme l'Int64), et l'Extended 10 octets !


Et il est inutile de travailler avec une telle précision car tous les calculs seront arrondis en entiers codés sur 8 bits (les canaux d'un TRGBTriple) pour être affiché, et une éventuelle différence de 1 sur un ou plusieurs canaux n'est pas perceptible à l'oeil.

De toute façon, l'Extended est le moins portable de tous, et il est à éviter quand on travaille pour plusieurs plates-formes.

Commentaire de Caribensila le 18/08/2012 18:38:27

... En revanche, je pense que sur une machine en 64 bits, le Double doit avoir de meilleures performances. Mais c'est à vérifier car j'ai pas une telle machine. M'enfin, ce serait logique...

Commentaire de cirec le 18/08/2012 19:08:17 administrateur CS

bon sang mais c'est bien sur ....
comment ai-je pu oublier une telle chose ...

Merci à toi de me rafraichir la mémoire ;)
c'est à cause de mon age avancé ... je perd la vue, la mémoire ... et après ? mdr

Commentaire de Caribensila le 18/08/2012 19:23:32

Franchement, ça m'étonnait aussi beaucoup de ta part.
(Mais j'serais toi, je changerais quand même d'avatar...)


Enfin, on voit bien que t'as rien perdu de ton talent et si je pouvais, je remettrais un nouveau 10/10 pour cet excellent travail.

Commentaire de cirec le 19/08/2012 16:25:36 administrateur CS

"Mais j'serais toi, je changerais quand même d'avatar..."
mais j'ai pas de photo plus récente ^^

Bon, j'ai eu l'occasion de tester et je peux confirmer les propos de Caribensila
"je pense que sur une machine en 64 bits, le Double doit avoir de meilleures performances."

effectivement le Double arrive en tête sur une 64 bits suivi par Extended et loin derrière le Single

La version ASM ne compile pas sous 64bits problème de registre .. ce qui semble normale ...

j'ai donc modifié le code pour permettre de le recompiler rapidement avec le type choisi (Extended, Double ou Single) grace à la compilation conditionnelle ... et il affiche dans la barre de titre la
version du compilateur utilisé (D7, D2009, XE2) le type d'os (32/64 bits) et le type choisi (Extended, Double ou Single) ... j'en avais marre de douter de la version, quand on en teste plusieurs ça aide beaucoup ;)

Maintenant comme le code est devenu plus difficile à lire, à cause de la compilation conditionnelle, et tout le monde ne veux pas forcément ceci, j'ai mis cette version dans un répertoire nommé "Version Custom"

pour changer de type il suffit d'éditer le fichier "Types.inc" et d'activer/désactiver le type voulu
et reconstruire le projet ... simple non !

@++

Commentaire de Caribensila le 19/08/2012 17:19:33

« mais j'ai pas de photo plus récente »
Bon. Alors, SURTOUT, laisse tomber.



En théorie, sur une machine 64bits il devrait être possible de doubler les performances. En effet, les couleurs restent codées de la même façon (sur 3 ou 4 bytes), et le processeur permet de traiter 8 bytes à la fois, soit 2 pixels. Or, ce qui prend du temps, c'est d'aller chercher ou de changer les composantes de la couleur des pixels en mémoire. Le faisant à chaque fois pour 2 pixels, ça doit forcément aller plus vite.

Bien sûr, il faudra alors faire deux codes. Un pour les machine 32bits et un autre pour les 64bits. C'est pour cette raison que la compilation conditionnelle m'intéresse beaucoup et je vais y jeter un oeil car j'y connais encore rien.
Merci pour cette MAJ, Cirec !


PS: Ma prochaine machine sera à coup sûr une 64bits car trop impatient de tester tout ça...

Commentaire de barbichette le 23/08/2012 14:45:54

Ben les filles... ça papote, ça papote, ça papote...
On ne peut pas partir une semaine sans avoir un roman photo au retour...

Je reviens de loin avec le Timage.Canvas et Timage.Picture.Bitmap.Canvas. En regardant (sous D7), c'est la même chose.
Timage.Canvas renvoi le canvas de Timage.Picture.Bitmap si il existe déjà.
Sinon, il crée ce fameux Picture.Bitmap et renvoi ce canvas.

Pour le reste, continuez à optimiser, on va arriver à des temps de calcul négatifs...

Commentaire de Caribensila le 23/08/2012 16:09:05

Moi, je pensais que le Timage.Picture.Bitmap était déjà créé mais avec ses Width et Height à zéro...

Sinon, un lien intéressant sur le canevas du TImage :

http://delphi.developpez.com/faq/?page=compopropri#dessinersurcanvas2timage

 Ajouter un commentaire


Discussions en rapport avec ce code source dans le forum

Problème avec StretchBlt [ par florenth ] Salut à tous !Je poste sur le bar car le problème que je rencontre ne me semble pas lié à un langage spécifique mais plus à l'API Win32Je vous expliqu redimensionnement en utilisant le rééchantillonnage bicubique [ par sisi231 ] Bonjour, J'ai utilisé le redimensionnement d'images par défaut de l'API Win32 mais il déforme beaucoup les images. En utilisant Photoshop CS5 on arriv


Nos sponsors


Sondage...

CalendriCode

Mai 2013
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Photothèque

A découvrir



 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 4,633 sec (3)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales