Comment récuperer la taille d'une image JPEG

Résolu
Signaler
Messages postés
3827
Date d'inscription
vendredi 23 juillet 2004
Statut
Modérateur
Dernière intervention
16 décembre 2021
-
Messages postés
3827
Date d'inscription
vendredi 23 juillet 2004
Statut
Modérateur
Dernière intervention
16 décembre 2021
-
Salut à tous,

Je fais appel à vos lumières parce que là je coince grave.

Je cherche le moyen de récuperer la taille (en pixels) d'une Image JPEG et PNG
sans devoir la charger (sinon ce serait trop simple) dans un TImage ou TJPEGImage

J'ai trouvé des codes qui font cela mais uniquement si le fichier comporte des données Exif
ce qui n'est pas forcément le cas pour tous les fichiers

J'ai également été faire un tour sur l'exellent site  www.WotSit.org ... mais c'est pas gagné

Donc si vous avez une idée ... une solution ... un truc ... un bout de code peut être ...

je suis ouvert à toutes propositions

Faites chauffer vos neurones 
 
@+
Cirec

<hr size="2" />

13 réponses

Messages postés
1154
Date d'inscription
samedi 14 août 2004
Statut
Membre
Dernière intervention
5 avril 2012
2
Bonjour

Voici un petit bout de code fait à TROP rapidement, donc pas mal d'améliorations quand à la forme et au fond sont à apporter mais le principe est là  (Si quelqu'un veux se dévouer pour l'améliorer, le compléter, allez au boulot, moi j'ai autre chose à faire ce week end ) :

procedure JPEGWidthHeight( AStream : TStream ; out Width, Height : Integer ) ;
const
  SOI_MARKER  = #$FF#$D8;
  SOF_MARKER  = #$FF#$C0; // Start of Frame
  JPEG_FORMAT = SOI_MARKER + #$FF#$E0 ;
  EXIF_FORMAT = SOI_MARKER + #$FF#$E1 ;
  JFIF_MARKER : array[0..4] of char = 'JFIF'#00 ;
  EXIF_MARKER : array[0..5] of char = 'Exif'#00 ;
var
  BlockPos : Integer ;
  BlockLength : Integer ;
  Datas : record
    case byte of
      0 : ( Raw        : array[0..15] of char ) ;
      1 : ( Marker     : array[0..3] of char  ) ;
      2 : ( StartOf    : array[0..1] of char  ) ;
      3 : ( BlocLength     : array[0..1] of byte
          ) ;
      4 : ( JPEGLength     : array[0..1] of byte
          ; JPEGIdentifier : array[0..4] of char
          ; JPEGVersion    : array[0..1] of byte
          ) ;
      5 : ( ExifLength     : array[0..1] of byte
          ; ExifIdentifier : array[0..5] of char
          ) ;
  end ;



  procedure BlockUpdate;
  begin
    AStream.Seek(BlockPos,soBeginning);
    AStream.ReadBuffer(Datas,sizeof(Datas));
  end ;



begin
  Width  := -1 ;
  Height := -1 ;



  BlockPos := 0 ;
  BlockUpdate;



  if ( Datas.Marker = JPEG_FORMAT ) then
  begin
    Inc( BlockPos, 4 ) ;
    BlockUpdate;
    BlockLength := 255 * Ord( Datas.BlocLength[0] ) + Ord( Datas.BlocLength[1] );



    if ( Datas.JPEGIdentifier = JFIF_MARKER ) then
    begin
      while( BlockPos < AStream.Size ) do
      begin
        // Déplacement sur le bloc suivant
        Inc( BlockPos, BlockLength ) ;
        if ( BlockPos >= AStream.Size ) then Exit ;



        BlockUpdate;
        if ( Datas.StartOf = SOF_MARKER ) then
        begin
          Height := 256 * Ord( Datas.Raw[5] ) + Ord( Datas.Raw[6] ) ;
          Width  := 256 * Ord( Datas.Raw[7] ) + Ord( Datas.Raw[8] ) ;
          Exit ;
        end else
        begin
          Inc(BlockPos,2);
          BlockUpdate;
          BlockLength := 255 * Ord( Datas.BlocLength[0] ) + Ord( Datas.BlocLength[1] );
        end ;
      end ;
    end ;
  end else if ( Datas.Marker = EXIF_FORMAT ) then
  begin
    //
    Inc( BlockPos, 4 ) ;
    BlockUpdate;



    // Vérification entête du JPEG ( 'JFIF'#0 )
    if ( Datas.ExifIdentifier = EXIF_MARKER ) then
    begin
      // etc... pas le temps de le gérer
    end ;
  end ;
end ;



// Exemple d'utilisation

procedure TForm1.Button1Click(Sender: TObject);
var
  F : TFileStream ;
  W,H : Integer ;
begin
  F := TFileStream.Create('C:\test.jpg',fmOpenRead);
  try
    JPEGWidthHeight( F, W, H ) ;
    Caption := IntToStr(W)+'x'+IntToStr(H);
  finally
    FreeAndNil( F );
  end;
end;



N.B. Le format Exif n'est pas pris en charge, mais pas beaucoup plus compliqué à gérer.

Cordialement. <hr />"L'imagination est plus importante que le savoir." Albert Einstein
Messages postés
3827
Date d'inscription
vendredi 23 juillet 2004
Statut
Modérateur
Dernière intervention
16 décembre 2021
46
Salut,

Merci pour ces informations ...

J'en suis arrivé au même résultat que toi (avec beaucoup moin de classe) dans un style plus "brute"
mais qui me permet de lire la taille peut importe le format du JPEG ce que je n'ai pas réussi avec ton code.

j'ai testé ton code et fait une petite modif pour lire les fichiers Exifs et ça fonctionne mais j'ai des fichiers
avec entête JFIF contenant également un Exif et la il plante (enfin il ne trouve rien) ????

Voici le code que j'ai fais et qui fonctionne avec tous mes fichiers JPEG

Const SizeSignature = $1100C0FF;
      JPEGSignature = $FFD8FF;

Var FoundAt : Array[0..1] Of Int64;

Function ReadSignature(Stream: TStream; Model : LongInt; Const Size : Integer = 4): Boolean;
Var aSign : LongInt;
Begin
  Stream.Read(aSign, Size);  Result :Model aSign;
End ;

Function ReadSignatureEx(Stream: TStream): Boolean;
Var Count, I : Integer;
Begin
  Count : = 0;
  If  ReadSignature(Stream, JPEGSignature, 3) Then  Begin
    Stream.Seek(3, soFromCurrent);
    For I := 0 to 1 do
      Repeat
        Result : = ReadSignature(Stream, SizeSignature);
        If  Not Result Then Stream.Seek(-3, soFromCurrent)
        Else  Begin
          FoundAt[Count] := Stream.Position + 1;
          Inc(Count);
        End ;
      Until (Result) or (Stream.Position + SizeOf(LongInt) > Stream.Size);
  End;
  Result : = (FoundAt[0] > -1) or (FoundAt[1] > -1);
End;

Function SwapByte(aValue : Word):Word;
Begin
  Result := (aValue Shr 8) or (Byte(aValue) Shl 8)
End;

procedure GetJPEGSize(FileName : TFileName ; out Width, Height : Word ) ;
Var FS : TMemoryStream;
Begin
  FoundAt[0] : = -1;
  FoundAt[1] := -1;
  FS := TMemoryStream.Create;
  FS.LoadFromFile(FileName);
  If ReadSignatureEx(FS) Then  Begin
    If FoundAt[1] > -1 Then
    FS.Seek(FoundAt[1], soFromBeginning)
    Else  If FoundAt[0] > -1 Then
    FS.Seek(FoundAt[0], soFromBeginning);
    FS.ReadBuffer(Height, SizeOf(Word));
    FS.ReadBuffer(Width, SizeOf(Word));

    Height : = SwapByte(Height);
    Width := SwapByte(Width);
  End ;
  FS.Free;
End;

// Utilisation
procedure TForm1.Button1Click(Sender: TObject);
Var aWidth, aHeight : Word;
begin
  If OpenDialog1.Execute Then  Begin
    GetJPEGSize(OpenDialog1.FileName, aWidth, aHeight);
    Label1.Caption : = Format('Dimensions %d x %d', [aWidth, aHeight]);
  End;
end;

<hr size="2" width="100%" />Biensur il y a encore beaucoup d'optimisation à faire mais il est fonctionnel et me donne pleine satisfaction

@: WhiteHippo
Merci pour ton aide et pour ton code qui, faut le dire, est mieux construit que le mien

Je regarderai cela de plus près dès que le temps me le permettera, afin d'en faire un mix avec le mien
bref réunir fonctionalité et élégance

Bon dimanche à tous
 
@+
Cirec

<hr size="2" />
Messages postés
460
Date d'inscription
dimanche 5 décembre 2004
Statut
Membre
Dernière intervention
6 avril 2009
2
Bonsoir à toutes et à tous,

@ Cirec, sans devoir la charger, donc en les visualisant sur un TfileListBox par exemple et via un Tlabel ?

@+,

Cincap

[url]mailto:/url
Messages postés
3827
Date d'inscription
vendredi 23 juillet 2004
Statut
Modérateur
Dernière intervention
16 décembre 2021
46
@ Cincap : Merci pour ta réponse

mais je crois que je me suis mal exprimé :
J'entend par taille : Width & Height (Largeur & Hauteur) et pas la taille en Octets

ps : L'explorer le fait quand tu survoles une image avec la souris il te donne ses dimensions

 
@+
Cirec

<hr size="2" />
Messages postés
460
Date d'inscription
dimanche 5 décembre 2004
Statut
Membre
Dernière intervention
6 avril 2009
2
Bonjour à toutes et à tous,

@ Cirec, j'avais réalisé une unité qui me permettait de voir mes skins avant de les installer, avec un TflileListBox et un Tlabel je récupérais la hauteur et la largeur du skin.

A toutes fin voici une éventuelle solution.

var
    bmp1  : Tbitmap;    // bitmap lu


implementation
.....

Label1.caption := ExtractFilename(filelistbox1.filename)+ Format('  (L %d x H %d)', [bmp1.Width, bmp1.Height]);


N. B. il faut tenir compte de la conversion en cas du jpg.

Je peus te passer tout le code de l'unité "Lecture" en cas de besoin.

@+,


Cincap

[url]mailto:/url
Messages postés
1023
Date d'inscription
dimanche 1 août 2004
Statut
Membre
Dernière intervention
17 août 2008
2
Salut !

Il faudra forcément lire un bout du fichier pour trouver les infos voulues !
Mais si tu ne veux pas tout décompresser, alors la librairie GraphicEx permet de le faire sans aucun problème. Par contre,  aucun moyen de me souvenir comment on fait.

++
Flo
Messages postés
1023
Date d'inscription
dimanche 1 août 2004
Statut
Membre
Dernière intervention
17 août 2008
2
Après une petite recherche :

<hr size= "2" width="100%" /> uses
  Jpg; // unité de lecture des jpeg version GraphicEx.

[...]

procedure TForm1.Button1Click(Sender: TObject);
var
  W, H: Cardinal;
begin
  if OpenDialog1.Execute then
  begin
    GetJPEGInfo(OpenDialog1.FileName, W, H);    ShowMessageFmt('W %d - H %d', [W, H]);
  end;
end;
<hr size="2" width="100%" />
Ce que je peux t'assurer, c'est que le fichier n'est pas décompressé, mais il doit être lu en grande partie. A mon avis, il doit y avoir moyen d'aller encore plus vite.
Messages postés
3827
Date d'inscription
vendredi 23 juillet 2004
Statut
Modérateur
Dernière intervention
16 décembre 2021
46
Bon je vais répéter :
"Je cherche le moyen de récuperer la taille (en pixels) d'une Image JPEG et PNG
sans devoir la charger dans un TImage ou TJPEGImage (sinon ce serait trop simple)"

dans Le bout de code que tu proposes l'image (ici un BMP) est chargé dans un TBitmap afin d'en récuperer sa taille ...

le seul chargement autorisé pour moi c'est dans TMemoryStream ... pourquoi

Tout simplement parce que je charge les images (*.JPEG; *.PNG;  ...) sans l'utilisation d'unité dédié
comme "JPEG.PAS; PNGLIB.PAS ..."
d'ailleurs pour un BMP c'est très simple, il suffit de lire les informations dans le "bmiHeader "
 

 
@+
Cirec

<hr size="2" />
Messages postés
1023
Date d'inscription
dimanche 1 août 2004
Statut
Membre
Dernière intervention
17 août 2008
2
Et ma solution GraphicEx, elle te convient pas alors
Messages postés
3827
Date d'inscription
vendredi 23 juillet 2004
Statut
Modérateur
Dernière intervention
16 décembre 2021
46
Merci Florenth,

Mais j'aimerai évité d'utiliser une unité uniquement pour récuperer la taille ...

il devrait y avoir moyen de le faire en lisant directement les infos dans le fichier

le but étant, à terme, de pouvoir se passer de ces librairies (type GraphicEx)

 
@+
Cirec

<hr size="2" />
Messages postés
460
Date d'inscription
dimanche 5 décembre 2004
Statut
Membre
Dernière intervention
6 avril 2009
2
@ Cirec, le code proposé ne lit que le jpg ou bmp dans le filelistbox donc bien avant de l'installer.

@+,

Cincap

[url]mailto:/url
Messages postés
1023
Date d'inscription
dimanche 1 août 2004
Statut
Membre
Dernière intervention
17 août 2008
2
Ah ouais sympathique celle là. L'avantage de passer via scrippting c'est qu'on peut lire tous les fichiers images reconnus par windows.
ça sent le snippet tout ça ...

Bravo
Messages postés
3827
Date d'inscription
vendredi 23 juillet 2004
Statut
Modérateur
Dernière intervention
16 décembre 2021
46
peut être ... à voir
mais ceci n'est que le sommet de l'iceberg, les possibilités de ce script sont beaucoup plus nombreuse

Les valeurs de :
FShellFolder.GetDetailsOf(FShellFolder.ParseName(Variant(FName)), 26);

vont de -1 à 39
-1 étant le texte du Hint affiché dans l'explorer
 
@+
Cirec

<hr size="2" />