AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language GetJPGSize Funktion (wollen wir sie verbessern?)
Thema durchsuchen
Ansicht
Themen-Optionen

GetJPGSize Funktion (wollen wir sie verbessern?)

Ein Thema von MicMic · begonnen am 7. Feb 2020 · letzter Beitrag vom 10. Mär 2020
Antwort Antwort
MicMic

Registriert seit: 26. Mai 2018
296 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#1

AW: GetJPGSize Funktion (wollen wir sie verbessern?)

  Alt 15. Feb 2020, 21:20
Das PDF von aboutvb hatte ich auch mal kurz überflogen aber alles nicht so einfach zu verstehen um es in Delphi umzusetzen.
Also ich kannte es schon. Trotzdem Danke für den Link. Versuche mich gerade daran nochmal.
Deine Funktion versuche ich erst mal zu ignorieren und will meine Funktion abändern. Einfach um alles besser zu verstehen.
Inzwischen habe ich meine Funktion ein wenig geändert. Funktioniert soweit abgesehen von einer Sache (die bei dir wohl auch noch nicht funktioniert), nämlich Hoch/Querformat. Bei Hochformat werden die Angaben vertauscht (in Breite steht die Höhe, in Höhe die Breite).

Hier mal mein neuer Code und schnell mal bissl Kommentare dazu:
Delphi-Quellcode:
Procedure GetJPGSize(sFile: String; Out WW, WH: DWord);
Var
  FS: TFileStream;
  BD: Byte;
  WD : Word;
  RL: LongInt;
  HW : Array[0..3] Of Byte;
  LE : Array[0..1] Of Byte;
Begin
  sFile := '\\?\'+SFile;
  WW := 0;
  WH := 0;
  FS := TFileStream.Create(sFile, fmShareDenyNone);
  Try

    // Erste 2 Bytes lesen
    RL := FS.Read(WD, 2);
    // Wenn FFD8 nicht vorhanden, dann RL auf 0 setzen
    If (Lo(WD) <> $FF) And (Hi(WD) <> $D8) Then RL := 0;

    If RL > 0 Then
    Begin

      // Nächstes Byte lesen. Ist (oder sollte) immer FF sein
      RL := FS.Read(BD, 1);

      While (BD = $FF) And (RL > 0) Do
      Begin
        // Nächstes Byte lesen
        RL := FS.Read(BD, 1);

        // Wenn alles außer FF, dann...
        If BD <> $FF Then
        Begin
          // Prüfen auf diese C0,C1 usw.
          If BD In [$C0,$C1,$C2] Then // <- kommt noch mehr dazu
          Begin
            // 3 Bytes weiter. Längenangabe (2 Bytes) sowie die Bit-Tiefe (1 Byte)
            // wird nicht benötigt
            FS.Seek(3,soFromCurrent);
            FS.Read(HW,4); // 4 Bytes (2 für Breite und 2 für Höhe)
            WH := HW[0] Shl 8 + HW[1];
            WW := HW[2] Shl 8 + HW[3];

            // ...
            // Hier muss noch was rein, wegen Hoch/Querformat
            // ...

            // Breite/Höhe gespeichert. Vielleicht ja noch mal prüfen? Jedenfalls hier Abbrechen
            Break;
          End Else
          Begin
            If BD<>$DA Then
            Begin
              FS.Read(Le,2); // 2 Bytes lesen (Längenangabe)
              WD := LE[0] Shl 8 + Le[1]; // Länge in Little Endian zurück (soll ja im JPG immer in BIG-Endian abgespeichert sein)
              FS.Seek(WD - 2, soFromCurrent); // Position zum nächsten Segment (-2 wegen 2 Byte-Längenangabe)
              FS.Read(BD, 1); // Erstes $FF lesen
            End Else Break; // <- DA gefunden, also fertig.
          End;
        End;
      End;
    End;
  Finally
    FS.Free;
  End;
End;
Bei aboutvb ist nun auf Seite 764 erst mal eine Tabelle (Tabelle 27.13) mit diesen C1,C2,C3 usw. Hier wurden die 3 (C4,C8,CC) auch weggelassen... also so, wie ich es bei Wikipedia las. Soweit so gut. In Tabelle 27.14 geht es dann weiter. Dort wird aber wieder (erste Zeile in der Tabelle) von FFDxH gesprochen. Auch weiter unten im Text...

"Wegen der geforderten Baseline DCT Komprimierung sollte die Signatur FFD0H auftreten."

Meinte der Autor hier nicht "FFC0H"?

In meinem Code jedenfalls überspringe ich 3 Bytes mit Seek.
Kommentar in meinem Code dort "3 Bytes weiter. Längenangabe (2 Bytes) sowie die Bit-Tiefe (1 Byte) wird nicht benötigt"

Es funktioniert jedenfalls so. Wenn ich mir die Tabelle 27.14 (aboutvb) betrachte, scheint dies ja so richtig zu sein. Abgesehen von der Angabe dort "FFD0H". Was wohl "FFC0H" sein sollte. Oder bringe ich was durcheinander?

Jedenfalls müsste ich dann wohl noch folgendes einfügen... bei meinem Kommentar (Hier muss noch was rein, wegen Hoch/Querformat) im Code:

1. 2 Byte überspringen
2. 1 Byte lesen und Werte prüfen.
3. ggf. Variablen für Breite/Höhe vertauschen

Laut Tabelle: Wert 0-3 vertikal und 4-7 horizontal müsste ich ermittelt bekommen.

Ich erreiche aber Werte von 17 (Querformat), mal 34 und auch mal 33 für Hochformat. Also so ganz klappt's noch nicht aber wohl fast.

Am Ende müsste man sich noch fragen, ob man die richtigen Werte vom Bild bekommt. In JPG soll man ja auch kleinere Vorschaubilder speichern können. 1 Vorschaubild? Mehrere? Wohl mehrere. Ich denke damit sind diese "ID x. Komponenten" gemeint. Im PDF (aboutvb) ist ja schon von "ID 3. Komponente" die Rede. Wobei dort in der Tabelle (27.14) nicht mehr von Bildgrößen zu lesen ist, sondern nur noch (pro Komponente) von Hoch/Querformat (Abtastfaktor) und einer Nummer von einer Quantisierungstabelle. Was auch immer das nun wieder ist Aber wenn es kleinere Vorschaubilder geben kann, gibt es sicherlich auch für diese ganze 4 Bytes für das Abfragen der Breite/Höhe. Hoffentlich ist das 1. immer das Originalbild. Alles was danach kommt (ob nun mit Vorschaubilder oder nicht) könnte ja einem dann egal sein. Zumindest für dieses Thema (Ermitteln der JPG Bildgröße).
  Mit Zitat antworten Zitat
MicMic

Registriert seit: 26. Mai 2018
296 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#2

AW: GetJPGSize Funktion (wollen wir sie verbessern?)

  Alt 15. Feb 2020, 21:32
Nachtrag:

"Ich erreiche aber Werte von 17 (Querformat), mal 34 und auch mal 33 für Hochformat. Also so ganz klappt's noch nicht aber wohl fast."

Ach... da steht ja BIT 0-3 und 4-7 (für das Format)
Wird gleich funktionieren (aber muss trotzdem schauen wie ich da die erste 4 und letzten 4 Bits teile)
  Mit Zitat antworten Zitat
MicMic

Registriert seit: 26. Mai 2018
296 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#3

AW: GetJPGSize Funktion (wollen wir sie verbessern?)

  Alt 15. Feb 2020, 23:00
Ich bekomme noch die Kriese.
Dieser Wert "Abtastfaktor" hat gar nichts mit dem Format zu tun.
Ich las "vertikal/horizontal" und dachte...

Ich habe jedenfalls ein Bild im Hochformat aufgenommen, dass hier leider Breite/Höhe vertauscht. ExifTool gibt an (beim Orientation-Flag): Rotate 270 CW. Jetzt muss man wohl noch EXIF-Daten auslesen. Wobei es nicht immer EXIF-Daten gibt aber hier (ohne EXIF Daten bei Hochformat) vertauscht Windows selbst die Werte.
  Mit Zitat antworten Zitat
HolgerX

Registriert seit: 10. Apr 2006
Ort: Leverkusen
984 Beiträge
 
Delphi 6 Professional
 
#4

AW: GetJPGSize Funktion (wollen wir sie verbessern?)

  Alt 16. Feb 2020, 05:36
Hm..

Das ist halt die Krux, wenn Kamera Hersteller meinen es 'besser' machen zu wollen...
Die Höhle und Breite werden dann getauscht, wenn mit der Kamera um 90/270 Grad gedrehtes Bild gemacht wird.
Diese Drehung steht dann nur in den EXIF informationen, diese könnten jedoch mal entfernt werden, womit diese Angaben weg sind.

Nur mit Programmen, welche auch die EXIF (Segment $FFE1 = APP1) auslesen und interpretieren, können das erkennen.
Diese EXIF Daten können Binär oder XML sein.. Viel Spaß wenn Du diese selber auslesen willst ohne entsprechende Komponenten.

Ein Thumbnail kann entweder im $FFE0 = APP0 sein, wenn dies den Identifier "JFIF" hat.
Alternativ auch in den zusätzlichen JFIF extension APP0 mit einem anderen APP0 Identifier "JFXX".
Oder ich glaube auch in den EXIF Daten..
Ach ja, jeweils in anderem Bild-Format..


Wenn Du all diese Information haben möchtest (Größe/Drehung/Thumb..) dann kannst Du deine Funktion vergessen und musst, wie in meinem Beispiel, Segmentbasierend arbeiten. Bei meinem Source brauche ich nur im Case die weiteren Kinds hinzufügen und dann mit Casten die Daten per vorher definierten Records sauber auslesen. Nur so behältst Du die Übersicht in deinem Source.
(Ja ich Verwende Delphi 6 Pro und will NICHT wechseln!)
  Mit Zitat antworten Zitat
MicMic

Registriert seit: 26. Mai 2018
296 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#5

AW: GetJPGSize Funktion (wollen wir sie verbessern?)

  Alt 19. Feb 2020, 17:57
Ne, auf EXIF habe ich erst mal keine Lust
Der Fokus lag jetzt auf die Bildgröße und das verstehen des JPG-Headers. Zwar ein wenig blöd, wenn evtl. bei manchen Dateien die Breite/Höhe vertauscht ist aber das schieb ich mal nach hinten und kümmere mich um andere Codezeilen.
Also mal Danke an alle. Ich habe ein wenig dazugelernt.

Mike
  Mit Zitat antworten Zitat
Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.691 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: GetJPGSize Funktion (wollen wir sie verbessern?)

  Alt 10. Mär 2020, 06:51
Lang hat's gedauert, da ich pers. Jpeg absolut nicht mag.
Hier ist mein Versuch was schnelles draus zu basteln, keine Benchmarks durchgeführt, selbst ist der Mann/die Frau

Hier meine herangehensweise, in der Hoffnung das es tatsächlich hilft:
Ps: Übergeben werden muss ein gültiger kompletter Pfad.
(MyGetFiles holt aus dem Verzeichniss nur die Dateinamen ab)
Delphi-Quellcode:
procedure TfrmMain.ComputeData(const input: String);
type
  TJpgInfo = record
    IsJpeg: Boolean;
    Version : String;
    Dimension : String;
    Mode: String;
  end;
  function BytesToWord(HiByte, LoByte: Byte): Word;
    type
      TWord = record
        case integer of
          0 : (Both : Word);
          1 : (Lo, Hi : Byte);
      end;
    var
      Long : TWord;
  begin
    with Long do
      begin
        Hi := HiByte;
        Lo := LoByte;
        Result := Both;
      end;
  end; // BytesToWord
  function GetJpgInfo(const FS: TFileStream): TJpgInfo;
    var
      Buf: TBytes;
      i: Integer;
      checker: Boolean;
      LastPos: Integer;
      S: String;
      MaxCache: Int64;
  begin
    MaxCache := (20 * 1024); // ggf anpassen für noch dickere header...
    checker := True;
    Result.IsJpeg := False;
    LastPos := 0;
    FS.Position := LastPos;
    if (FS.Size >= MaxCache) then
      SetLength(Buf, MaxCache)
    else
      SetLength(Buf, FS.Size);
    FS.Read(Pointer(Buf)^, Length(Buf)); // daten puffern um es flott im RAM dynamisch auswerten zu können
                                         // dyn = wenn signaturen nicht direkt bei position 0 anfangen
                                         // Jpeg's mit exif header zBsp
    if checker then // Signatur Check
      begin
        checker := False;
        for I := Low(Buf) to High(Buf) do
          begin
            if i + 3 < High(Buf) then
              if ((Buf[I] = $ff) and (Buf[I+1] = $d8) and (Buf[I+2] = $ff) and (Buf[I+3] = $e0)) then
                begin
                  checker := True;
                  LastPos := i + 3;
                  Break;
                end;
          end;
      end;
    if checker then // prüfe ob JFIF vorhanden ist, erst ab hier akzeptiere ich es als Jpeg Datei
      begin
        checker := False;
        for I := LastPos to High(Buf) do
          begin
              if i + 3 < High(Buf) then
              if ((Buf[I] = $4a) and (Buf[I+1] = $46) and (Buf[I+2] = $49) and (Buf[I+3] = $46)) then
                begin
                  checker := True;
                  Result.IsJpeg := True;
                  // Application.MessageBox(PChar('gefunden'), PChar('gefunden'), MB_OK);
                  LastPos := i + 3;
                  Break;
                end;
          end;
      end;
    if Result.IsJpeg then // hole Version
      begin
        if LastPos + 3 < High(Buf) then
          begin
            if Buf[LastPos+3] < 10 then
              Result.Version := IntToStr(Buf[LastPos+2]) + '.' + '0' + IntToStr(Buf[LastPos+3])
            else
              Result.Version := IntToStr(Buf[LastPos+2]) + '.' + IntToStr(Buf[LastPos+3]);
            LastPos := LastPos + 3;
          end;
      end;
    if Result.IsJpeg then // hole Dimension und Farbmodus vom letzten C0 segment was sich hoffentlich im MaxCache bereich befindet...
                          // da diese operation den kompletten puffer betrifft
                          // kann man hier bestimmt noch mehr speed rausholen
      begin
        checker := False;
        for I := LastPos to High(Buf) do
          begin
            if i + 1 < High(Buf) then
              if ((Buf[I] = $ff) and (Buf[I+1] = $c0)) then
                begin
                  checker := True;
                  LastPos := i;
                end;
          end;
        if checker then
          if LastPos + 10 < High(Buf) then
            begin
              Result.Dimension := IntToStr(BytesToWord(Buf[LastPos + 7], Buf[LastPos + 8])) + ' x ' + IntToStr(BytesToWord(Buf[LastPos + 5], Buf[LastPos + 6]));
              case Buf[LastPos + 9] of
                $1: Result.Mode := 'Grey';
                $3: Result.Mode := 'YCbCr';
                $4: Result.Mode := 'CMYK';
              end;
            end;
      end;
  end; // GetJpgInfo
var
  FileList: TStringDynArray;
  lvItem: TListItem;
  i: Integer;
  fs: TFileStream;
  JpgInfo: TJpgInfo;
begin
  lvFolder.Clear;
  if Length(input) <= 3 then Exit;
  edtFolder.Text := input;
  FileList := MyGetFiles(input, '*.jpg;*.jpeg;*.jpe;*.jfif', False);
  if Length(FileList) > 0 then
    begin
      for I := Low(FileList) to High(FileList) do
        begin
          lvItem := lvFolder.Items.Add;
          lvItem.Caption := ExtractFileName(FileList[I]);
          fs := TFile.OpenRead(FileList[I]);
          lvItem.SubItems.Add(IntToStr(fs.Size));
          JpgInfo := GetJpgInfo(fs);
          if JpgInfo.IsJpeg then
            begin
              lvItem.SubItems.Add(JpgInfo.Version);
              lvItem.SubItems.Add(JpgInfo.Dimension);
              lvItem.SubItems.Add(JpgInfo.Mode);
            end;
        end;
      fs.Free;
    end;
end; // ComputeData
Ein kleines Testprogramm dem dieser Code entspringt ist angepappt.
Viel Spass


/edit
Mir ist gerade noch 'ne Speed optimierung eingefallen betreffend diesem Abschnitt:
Code:
    if Result.IsJpeg then // hole Dimension und Farbmodus vom letzten C0 segment was sich hoffentlich im MaxCache bereich befindet...
                          // da diese operation den kompletten puffer betrifft
                          // kann man hier bestimmt noch mehr speed rausholen
      begin
        checker := False;
        for I := LastPos to High(Buf) do
genau andersrum machen, rückwärts abarbeiten lassen und einen break beim ersten fund...
Angehängte Dateien
Dateityp: 7z JpegLister.7z (608,4 KB, 6x aufgerufen)
Gruß vom KodeZwerg

Geändert von KodeZwerg (10. Mär 2020 um 11:49 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:49 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz