Autor |
Beitrag |
hRb
      
Beiträge: 292
Erhaltene Danke: 12
|
Verfasst: So 14.09.25 21:35
Man sollte meinen, dass nach vielen Jahren Compilerentwicklung die unterschiedlichen jpg-Formate korrekt erkannt werden. Dies scheint bei der Vielzahl der Kamerahersteller und vielfältigen Möglichkeiten wie jpg-Header aussehen können nicht der Fall.
Ich habe ein Programm mit dem Bilder angezeigt werden (Diabetrachter). In aller Regel jpg-Dateien.
Nun erhalte ich Bilder, die mit einem iPhone aufgenommen wurden und muss feststellen, dass hochkant-fotografierte Bilder nicht automatisch gedreht werden.
Mein Code:
Delphi-Quelltext 1: 2: 3: 4:
| uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.Grids, Vcl.Imaging.jpeg; |
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32:
| procedure TForm4.FormShow(Sender: TObject); var jpg: TJPEGImage; FiName: string; breite,hoehe: Integer; Begin ... ... ... Form4.Image1.Picture.LoadFromFile(FiName); end;
procedure TForm1.GrossbildStartMitRandClick(Sender: TObject); begin Form4.BorderStyle := bsSizeable; Form4.FormStyle := fsNormal; Form4.Windowstate := wsMaximized; Form4.Show; Form4.SetFocus; end;
procedure TForm1.GrossbildStartOhneRandClick(Sender: TObject); begin Form4.BorderStyle := bsNone; Form4.FormStyle := fsStayOnTop; Form4.BoundsRect := Monitor.BoundsRect; Form4.Show; Form4.SetFocus; end; |
Leider bringt mich die Bild-Vorababfrage nach Höhe und Breite auch nicht weiter, denn selbst bei Hochkant-Foto ist Breite > Hoehe. Auf Exit-Analyse habe ich jedoch keine Lust.
Frage: wer kann mir Code liefern, der bei allen Kameras/Handys funktioniert.
PS: arbeite mit Delphi RAD 12 (vor 3 Tagen installiert)
|
|
Th69
      

Beiträge: 4799
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Mo 15.09.25 10:49
hRb hat folgendes geschrieben : | Auf Exit-Analyse habe ich jedoch keine Lust. |
Du meinst sicherlich: Exif ;- )
Ohne diese wirst du aber nicht auskommen, da die Orientierung dort gespeichert ist, s. z.B. die Erklärung in JPEG Image Orientation and Exif.
PS: Dein 2. Thema habe ich gelöscht (anscheinend wolltest du editieren) und das neuere hier belassen.
|
|
hRb 
      
Beiträge: 292
Erhaltene Danke: 12
|
Verfasst: Mo 15.09.25 12:23
Sorry, ja natürlich Exif!
Zitat: | Ohne diese wirst du aber nicht auskommen, da die Orientierung dort gespeichert ist |
Aber muss denn jeder das Rad neu erfinden? Gibt es keine fertige Routine die besser ist als LoadfromFile?. Meine Canon-Bilder werden übrigens korrekt automatisch gedreht. Die Beschäftigung mit dem jpg-Header zeigt mir eine große Spielwiese für Hersteller. Ich will doch "nur" ein Bild anzeigen", nicht bearbeiten, nicht speichern. Das sollte doch im Standard sein.
Übrigens: es gibt kaum ein Codebeispiel das ich nicht versucht habe. Keines kann ich fehlerfrei compilieren. Entweder unbekannter Typ, keine Angabe zur uses, Lazerus-Compiler notwendig, etc...
Natürlich kenne ich Irfan und verwende ihn auch selbst. Leider wird heute oft nicht mehr fotografiert, sondern geknippst. Eine Bilderflut und selten wird gelöscht. Der Vorteil meines Bildbetrachters ist: er lädt die Bilder eines oder mehrerer Verzeichnisse in eine Stringgridliste und parallel in ein Listview. Dort kann - wenn gewünscht - sortiert werden und im Stringgrid wird markiert was später als Großbild angezeigt werden soll. Alles Bestens, selbst die iPhonebilder in der Vorschau - nur eben nicht mit Loadfromfile(nnn.jpg). Bin ich der Einzige der Bilder anzeigen will?
|
|
Gausi
      
Beiträge: 8549
Erhaltene Danke: 478
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Di 16.09.25 12:13
Der Punkt ist ja, dass man ein Bild im JPEG-Format auf zwei Arten speichern kann, und beides hat Vor- und Nachteile.
Einige Hersteller drehen die Bildinformationen direkt, wenn der Anwender die Kamera um 90° dreht. Andere schreiben diese Information nur in die Exif-Daten, so dass ein Bildbetrachter entscheiden kann, ob das Bild "original" oder "gedreht" angezeigt werden soll.
Die in Delphi eingebaute Funktion TGraphic.LoadfromFile (bzw. die Ableitung in TJpegImage)) scheint die Exif-Daten zu ignorieren und lädt das Bild so, wie es die Kamera abgespeichert hat. Wenn man Jpeg-spezifische Dinge unterstützen möchte, muss man das halt selber machen. Gibt es auch bestimmt schon irgendwo.
Die Exif-Daten auslesen sollte keine Zauberei sein. Aufpassen muss man ggf. wenn man das gedrehte Bild nicht nur anzeigen, sondern auch speichern möchte. Das sollte nämlich ohne den Umweg über TBitmap o.ä. gehen, sondern direkt auf den JPEG-Daten, damit das verlustfrei abläuft. Das ist wohl zumindest dann möglich, wenn die Ausmaße des Bildes ein Vielfaches von 8 oder 16 sind.
Ein weiterer Vorteil, wenn du Exif-Daten benutzen würdest: Soweit ich weiß können darin auch Thumbnails enthalten sein. Wenn man diese für die Übersichts-Liste nutzt, dürfte das die Performance deines Betrachters deutlich steigern - denn dann müssen nicht dutzende Megapixel-Bilder geladen werden, sondern nur die kleinen Vorschaubildchen. 
_________________ We are, we were and will not be.
Für diesen Beitrag haben gedankt: Th69
|
|
hRb 
      
Beiträge: 292
Erhaltene Danke: 12
|
Verfasst: Mi 17.09.25 02:05
Ok, bin ja schon seit einiger Zeit dran Exif-Daten zu lesen und auszuwerten.
1. Th69 hat einen Link geschickt, der zu einem Codebeispiel führt. Funktioniert aber nicht und scheint uralt zu sein. Mit BlockRead werden immer 12 Byte gelesen. Sehe ich mir jpg-Bilder im Hex-Editer an, ergibt dies überhaupt keinen Sinn. Ich hänge den Code mal unten an. Je nach Kamera sind die Blöcke unterschiedlich lang, d.h. man muss die genaue Header-Struktur kennen. Wo gibts eine übersichtliche Beschreibung?
2. Gibt es in RAD 12 keine uses z.B. Vcl.Exif um Info zu lesen?
3. Im iPhone-Bild steht an der Stelle wo üblich Exif steht JFIF. Scheint auch eine Variante der Jpeg-Norm zu sein.
Möchte ja schon die Exif-Daten auswerten, hoffte nur auf einige fertige Routinen um die Info-Daten zu lesen. (nochmals: nicht schreiben). Alles sehr komplex für mich.
Gibt es noch etwas was ich noch nicht kenne? Suche schon lange und finde nirgends brauchbaren Code.
Danke für Geduld. hRb
Exif-Unit-Code von einem unbekannten Japaner
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41:
| unit Unit1;
interface
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Exif;
type TForm1 = class(TForm) Panel1: TPanel; Button1: TButton; OpenDialog1: TOpenDialog; Memo1: TMemo; procedure Button1Click(Sender: TObject); private public end;
var Form1: TForm1; Daten: Texif;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin Daten:= TExif.Create; Memo1.Clear; opendialog1.Execute; Daten.ReadFromFile(opendialog1.FileName); Memo1.Lines.Add('fertig'); end;
end. |
//========================
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240:
| unit Exif;
interface
uses Classes, SysUtils;
type TExif = class(TObject) private FImageDesc : String; FMake : String; FModel : String; FOrientation : Byte; FOrientationDesk : String; FCopyright : String; FValid : Boolean; FDateTime : String; FDateTimeOriginal : String; FDateTimeDigitized : String; FUserComments : String; f : File; idfp : Cardinal; function ReadValue(const Offset, Count: Cardinal): String; procedure Init; public constructor Create; procedure ReadFromFile(const FileName: AnsiString);
property ImageDesc: String read FImageDesc; property Make: String read FMake; property Model: String read FModel; property Orientation: Byte read FOrientation; property OrientationDesk: String read FOrientationDesk; property Copyright: String read FCopyright; property Valid: Boolean read FValid; property DateTime: String read FDateTime; property DateTimeOriginal: String read FDateTimeOriginal; property DateTimeDigitized: String read FDateTimeDigitized; property UserComments: String read FUserComments; end;
implementation uses unit1;
type TMarker = record Marker : Word; Len : Word; Indefin : Array [0..4] of Char; Pad : Char; end;
TTag = record TagID : Word; TagType : Word; Count : Cardinal; OffSet : Cardinal; end;
TIFDHeader = record pad : Byte; ByteOrder : Word; i42 : Word; IFD0offSet : Cardinal; Interoperabil: Byte; end;
function TExif.ReadValue(const Offset, Count: Cardinal): String; var fp: LongInt; i: Word; begin SetLength(Result,Count); fp:=FilePos(f); Seek(f, Offset); try i:=1; repeat BlockRead(f,Result[i],1); inc(i); until (i>=Count) or (Result[i-1]=#0); if i<=Count then Result:=Copy(Result,1,i-1); except Result:=''; end; Result:=TrimRight(Result); Form1.Memo1.Lines.Add(Result); Seek(f,fp); end;
procedure TExif.Init; begin
idfp:=0;
FImageDesc:=''; FMake:=''; FModel:=''; FOrientation:=1; FOrientationDesk:='Normal'; FDateTime:=''; FCopyright:=''; FValid:=False; FDateTimeOriginal:=''; FDateTimeDigitized:=''; FUserComments:=''; end;
constructor TExif.Create; begin Init; end;
procedure TExif.ReadFromFile(const FileName: AnsiString); const ori: Array[1..8] of String= ('Normal','Mirrored', 'Rotated 180','Rotated 180, mirrored', 'Rotated 90 left, mirrored', 'Rotated 90 right', 'Rotated 90 right, mirrored', 'Rotated 90 left'); var j: TMarker; idf: TIFDHeader; off0: Cardinal; tag: TTag; i: Integer; SOI: Word; begin if not FileExists(FileName) then exit; Init;
AssignFile(f,FileName); reset(f,1);
BlockRead(f,SOI,2); if SOI=$D8FF then begin BlockRead(f,j,9);
if j.Marker=$E0FF then begin Seek(f,20); BlockRead(f,j,9); end;
if j.Marker=$E1FF then begin FValid:=True; off0:=FilePos(f)+1; BlockRead(f,idf,11); i:=0; repeat inc(i); BlockRead(f,tag,12); if tag.TagID=$010E then FImageDesc:=ReadValue(tag.OffSet+off0,tag.Count); if tag.TagID=$010F then FMake:=ReadValue(tag.OffSet+off0,tag.Count); if tag.TagID=$0110 then FModel:=ReadValue(tag.OffSet+off0,tag.Count); if tag.TagID=$8769 then idfp:=Tag.OffSet; if tag.TagID=$0112 then begin FOrientation:=tag.OffSet; if tag.OffSet in [1..8] then FOrientationDesk:=ori[tag.OffSet] else FOrientationDesk:='Unknown'; end; if tag.TagID=$0132 then FDateTime:=ReadValue(tag.OffSet+off0,tag.Count); if tag.TagID=$8298 then FCopyright:=ReadValue(tag.OffSet+off0,tag.Count); until (i>11);
if idfp>0 then begin Seek(f,idfp+12+2); i:=0; repeat inc(i); BlockRead(f,tag,12);
if tag.TagID=$9003 then FDateTimeOriginal:=ReadValue(tag.OffSet+off0,tag.Count); if tag.TagID=$9004 then FDateTimeDigitized:=ReadValue(tag.OffSet+off0,tag.Count); if tag.TagID=$9286 then FUserComments:=ReadValue(tag.OffSet+off0,tag.Count); until (i>23); end; end; end; CloseFile(f); end;
end. |
|
|
hRb 
      
Beiträge: 292
Erhaltene Danke: 12
|
Verfasst: Mi 17.09.25 02:10
Zitat: | Die Exif-Daten auslesen sollte keine Zauberei sein. |
Wie aus dem Text ersichtlich: Für mich schon.
|
|
jaenicke
      
Beiträge: 19322
Erhaltene Danke: 1749
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 17.09.25 07:00
Wie wäre es denn mit dieser Bibliothek?
github.com/sasgis/ccr-exif
|
|
|