Comment récuperer la taille d'une image JPEG [Résolu]

Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention - 20 juil. 2007 à 22:37 - Dernière réponse : Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention
- 27 juil. 2007 à 14:27
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" />
Afficher la suite 

Votre réponse

15 réponses

Meilleure réponse
WhiteHippo 1270 Messages postés samedi 14 août 2004Date d'inscription 5 avril 2012 Dernière intervention - 21 juil. 2007 à 14:47
3
Merci
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

Merci WhiteHippo 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 93 internautes ce mois-ci

Commenter la réponse de WhiteHippo
Meilleure réponse
Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention - 22 juil. 2007 à 11:14
3
Merci
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" />

Merci Cirec 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 93 internautes ce mois-ci

Commenter la réponse de Cirec
Meilleure réponse
Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention - 27 juil. 2007 à 14:16
3
Merci
Voici une solution qui fonctionne pour quasi tous les types d'Images
(enfin pour tous ceux que j'ai testé)

Encore une fois les puristes ne vont pas aimer ...
<hr size="2" width="100%" />// Cette idée m'est venue après un message de Loda :
//http://www.delphifr.com/infomsg_RECUPERER-TAILLE-DOSSIER_984578.aspx#7
unit Unit2;

interface
Uses SysUtils, StrUtils, Variants, Dialogs;
   Function GetImageSize(Const aFileName : TFileName; Out aWidth, aHeight: Integer):Boolean;
implementation
Uses ComObj;

Function ExcludeChars(Const S : String; const CS : TSysCharSet): String;
Var P, PR : PByte;
    BS : Set Of Byte Absolute CS;
    Size, I    : Integer;
Begin
  P    := PByte(S);
  Size := Length(S);
  SetLength(Result, Size);
  PR   := PByte(Result);
  I := 0;
  While P^ <> $0 do Begin
    If Not (P^ in BS) Then Begin
      Pr^ := P^;
      Inc(Pr);
    End
    Else Inc(I);
    Inc(P);
  End;
  If I > 0 Then SetLength(Result, Size - I)
End;

Function GetImageSize(Const aFileName : TFileName; Out aWidth, aHeight: Integer):Boolean;
Var FDir, FName, TMP : String;
    Index : Integer;
    FShellApp, FShellFolder : Variant;
Begin
  Result := False;
  If FileExists(aFileName) Then Try
    FDir  := ExtractFileDir(aFileName);
    FName := ExtractFileName(aFileName);
    FShellApp := CreateOleObject('Shell.Application');
    Try
      FShellFolder := FShellApp.NameSpace(Variant(FDir));
      TMP := FShellFolder.GetDetailsOf(FShellFolder.ParseName(Variant(FName)), 26);
      TMP := ExcludeChars(TMP, #32);
      Index := Pos('x', TMP);
      If Index > 0 Then Begin
        aWidth  := StrToInt(Copy(TMP, 1, Index - 1));
        aHeight := StrToInt(Copy(TMP, Index + 1, High(Integer)));
        Result := True;
      End;

    Except On E : Exception Do
      Begin
        MessageDlg(Format('Une erreur %s avec le message : %s'#13#10'est survenue', [E.ClassName, E.Message]), mtINFORMATION,
        [mbOk], 0);
      End;
    End;

  Finally
    FShellApp := Unassigned;
    FShellFolder := Unassigned;
  End;
End;
end.

{Et son utilisation}
procedure TForm1.Button3Click(Sender: TObject);
Var aW, aH : Integer;
begin
  If GetImageSize('E:\Mes Images\000_0066.jpg', aW, aH) Then
    Label1.Caption := Format('Taille : %d x %d', [aW, aH]);
end;

 
@+
Cirec

<hr size="2" />

Merci Cirec 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 93 internautes ce mois-ci

Commenter la réponse de Cirec
cincap 490 Messages postés dimanche 5 décembre 2004Date d'inscription 6 avril 2009 Dernière intervention - 20 juil. 2007 à 23:21
0
Merci
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
Commenter la réponse de cincap
Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention - 20 juil. 2007 à 23:40
0
Merci
@ 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" />
Commenter la réponse de Cirec
cincap 490 Messages postés dimanche 5 décembre 2004Date d'inscription 6 avril 2009 Dernière intervention - 21 juil. 2007 à 08:43
0
Merci
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
Commenter la réponse de cincap
florenth 1105 Messages postés dimanche 1 août 2004Date d'inscription 17 août 2008 Dernière intervention - 21 juil. 2007 à 12:05
0
Merci
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
Commenter la réponse de florenth
florenth 1105 Messages postés dimanche 1 août 2004Date d'inscription 17 août 2008 Dernière intervention - 21 juil. 2007 à 12:19
0
Merci
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.
Commenter la réponse de florenth
Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention - 21 juil. 2007 à 12:19
0
Merci
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" />
Commenter la réponse de Cirec
florenth 1105 Messages postés dimanche 1 août 2004Date d'inscription 17 août 2008 Dernière intervention - 21 juil. 2007 à 12:21
0
Merci
Et ma solution GraphicEx, elle te convient pas alors
Commenter la réponse de florenth
Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention - 21 juil. 2007 à 12:26
0
Merci
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" />
Commenter la réponse de Cirec
cincap 490 Messages postés dimanche 5 décembre 2004Date d'inscription 6 avril 2009 Dernière intervention - 21 juil. 2007 à 13:06
0
Merci
@ Cirec, le code proposé ne lit que le jpg ou bmp dans le filelistbox donc bien avant de l'installer.

@+,

Cincap

[url]mailto:/url
Commenter la réponse de cincap
WhiteHippo 1270 Messages postés samedi 14 août 2004Date d'inscription 5 avril 2012 Dernière intervention - 22 juil. 2007 à 08:13
0
Merci
T'as étais faire un tour ici ?
  http://www.delphifr.com/codes/LECTURE-PARAMETRES-EXIF-IMAGE-JPEG_23257.aspx

Cordialement.
<hr />"L'imagination est plus importante que le savoir." Albert Einstein
Commenter la réponse de WhiteHippo
florenth 1105 Messages postés dimanche 1 août 2004Date d'inscription 17 août 2008 Dernière intervention - 27 juil. 2007 à 14:22
0
Merci
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
Commenter la réponse de florenth
Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention - 27 juil. 2007 à 14:27
0
Merci
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" />
Commenter la réponse de Cirec

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.