Page 1 sur 1
Lire le tag EXIF de rotation d'image JPG
Publié : mer. 15/août/2018 8:33
par Marc56
Pour faire suite à
SaveImage creer une rotation
https://www.purebasic.fr/french/viewtop ... 46#p200946
Voici une bafouille pour lire le Tag d'orientation d'une image JPG dans les EXIFs.
Prenez quelques photos en mode vertical et horizontal avec un APN ou un téléphone et testez.
Oui, une lib existe sur le forum us, mais j'aime bien réinventer le roue (pour comprendre) et surtout ne pas charger des centaines de lignes là où une dizaine suffit.
Commentaires et liens dans le sources pour ceux que ça intéresse.
Optimisation bienvenue.
Code : Tout sélectionner
; Lire le Tag EXIF concernant l'orientation d'une image
; Marc56 - 15/08/18
; Tag ID Tag Name Writable Group
; 0x0112 Orientation int16u IFD0
; DEC 274
; Values / Notes
; 1 = Horizontal (normal)
; 2 = Mirror horizontal
; 3 = Rotate 180
; 4 = Mirror vertical
; 5 = Mirror horizontal And rotate 270 CW
; 6 = Rotate 90 CW
; 7 = Mirror horizontal And rotate 90 CW
; 8 = Rotate 270 CW
; Ressources:
; https://fr.wikipedia.org/wiki/Exchangeable_image_file_format
; http://owl.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
; Editeur Hexa https://mh-nexus.de/en/ ou http://frhed.sourceforge.net/
Declare Search_Orientation(Fichier$)
; Test
; Utiliser une photo prise à la verticale et une à l'horizontale
; Vérifier avant dans un viewer (ie: irfanview) que le tag orientation est rempli
; On devrait normalement trouver
; 1 pour une photo normale (horizontale)
; 6 ou 8 pour une photo prise à la verticale
Search_Orientation("C:\DataTest\EXIF_V6.JPG")
Search_Orientation("C:\DataTest\EXIF_H.JPG")
Search_Orientation("C:\DataTest\EXIF_V8.JPG")
; --- Procédures
Procedure Search_Orientation(Fichier$)
Debug "Fichier: " + Fichier$
If Not ReadFile(0, Fichier$, #PB_Unicode)
Debug "Fichier illisible"
ProcedureReturn
EndIf
length = Lof(0)
Debug "Taille : " + FormatNumber(length, 0) + " octets"
For i = 0 To length
FileSeek(0, i)
If ReadUnicodeCharacter(0) = $0112 ; ou 274 den décimal
Debug "Offset : " + i
FileSeek(0, 6, #PB_Relative)
ID = ReadCharacter(0)
Debug "Valeur : " + ID
Select ID
Case 1 : Debug "Image Horizontale"
Case 6 : Debug "Image à tourner vers Gauche"
Case 8 : Debug "Image à tourner vers Droite"
EndSelect
Break
EndIf
Next
CloseFile(0)
Debug ""
EndProcedure
Résultat
Code : Tout sélectionner
Fichier: C:\DataTest\EXIF_V6.JPG
Taille : 6,818,304 octets
Offset : 46
Valeur : 6
Image à tourner vers Gauche
Fichier: C:\DataTest\EXIF_H.JPG
Taille : 2,832,384 octets
Offset : 46
Valeur : 1
Image Horizontale
Fichier: C:\DataTest\EXIF_V8.JPG
Taille : 3,677,696 octets
Offset : 46
Valeur : 8
Image à tourner vers Droite
PS. Je n'ai pas compris pourquoi j'ai dû faire un FileSeek de 6 positions pour trouver la bonne valeur ? j'ai dû faire par approche.
Logiquement 1 position aurait suffit.
Peut-être que je m’embête aussi à scanner tout le fichier alors que l'offset est le même (46)
À voir sur d'autres exemples, selon la version d'EXIF générée par l'APN...

Re: Lire le tag EXIF de rotation d'image JPG
Publié : mer. 15/août/2018 9:35
par Marc56
Si on considère que l'info de rotation est toujours à l'offset 54, on peut simplifier
Code : Tout sélectionner
; Lire le Tag EXIF concernant l'orientation d'une image
; Read_Exif_Lite.pb
; Marc56 - 15/08/18
;{ Informations
; Tag ID Tag Name Writable Group
; 0x0112 Orientation int16u IFD0
; DEC 274
; Values / Notes
; 1 = Horizontal (normal)
; 2 = Mirror horizontal
; 3 = Rotate 180
; 4 = Mirror vertical
; 5 = Mirror horizontal And rotate 270 CW
; 6 = Rotate 90 CW
; 7 = Mirror horizontal And rotate 90 CW
; 8 = Rotate 270 CW
; Ressources:
; https://fr.wikipedia.org/wiki/Exchangeable_image_file_format
; http://owl.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
; Editeur Hexa https://mh-nexus.de/en/ ou http://frhed.sourceforge.net/
;}
Declare Search_Orientation(Fichier$)
Search_Orientation("C:\DataTest\EXIF_V6.JPG")
Search_Orientation("C:\DataTest\EXIF_H.JPG")
Search_Orientation("C:\DataTest\EXIF_V8.JPG")
; --- Procédures
Procedure Search_Orientation(Fichier$)
Debug "Fichier: " + Fichier$
If Not ReadFile(0, Fichier$, #PB_Unicode)
Debug "Fichier illisible"
ProcedureReturn
EndIf
Debug "Taille : " + FormatNumber(Lof(0), 0) + " octets"
FileSeek(0, 54)
ID = ReadCharacter(0)
Select ID
Case 1 : Debug "ID : " + ID + " -> Image Horizontale"
Case 6 : Debug "ID : " + ID + " -> Image à tourner vers Gauche"
Case 8 : Debug "ID : " + ID + " -> Image à tourner vers Droite"
EndSelect
CloseFile(0)
Debug ""
EndProcedure
Code : Tout sélectionner
Fichier: C:\DataTest\EXIF_V6.JPG
Taille : 6,818,304 octets
ID : 6 -> Image à tourner vers Gauche
Fichier: C:\DataTest\EXIF_H.JPG
Taille : 2,832,384 octets
ID : 1 -> Image Horizontale
Fichier: C:\DataTest\EXIF_V8.JPG
Taille : 3,677,696 octets
ID : 8 -> Image à tourner vers Droite
Il ne reste plus qu'à tourner l'image après chargement et avant redimensionnement avec la lib VectorDrawing
https://www.purebasic.com/french/docume ... nates.html

Re: Lire le tag EXIF de rotation d'image JPG
Publié : mer. 15/août/2018 10:09
par Micoute
Merci Marc56, c'est du très bon travail.
Re: Lire le tag EXIF de rotation d'image JPG
Publié : mer. 15/août/2018 10:14
par Marc56
Pas encore au point, car si je liste un répertoire, j'ai parfois des valeurs supérieures à 8
Code : Tout sélectionner
EnableExplicit
Enumeration
#hDir
#hFile
EndEnumeration
Define Repertoire$ = PathRequester("Répertoire à lister", GetUserDirectory(#PB_Directory_Pictures))
If ExamineDirectory(#hDir, Repertoire$, "*.jpg")
While NextDirectoryEntry(#hDir)
If DirectoryEntryType(#hDir) = #PB_DirectoryEntry_File
Define Fichier$ = DirectoryEntryName(#hDir)
Debug Fichier$ + " ?"
If ReadFile(#hFile, Repertoire$ + Fichier$, #PB_Unicode)
Debug " - Taille : " + FormatNumber(Lof(#hFile), 0) + " octets"
FileSeek(#hFile, 54)
Define ID = ReadCharacter(#hFile)
; 1 = Horizontal (normal)
; 2 = Mirror horizontal
; 3 = Rotate 180
; 4 = Mirror vertical
; 5 = Mirror horizontal And rotate 270 CW
; 6 = Rotate 90 CW
; 7 = Mirror horizontal And rotate 90 CW
; 8 = Rotate 270 CW
Select ID
Case 1 : Debug " - ID : " + ID + " -> Image Horizontale"
Case 2 : Debug " - ID : " + ID + " -> Image en Miroir Horizontale"
Case 6 : Debug " - ID : " + ID + " -> Image à tourner vers Gauche"
Case 8 : Debug " - ID : " + ID + " -> Image à tourner vers Droite"
Default : Debug " - ID : " + ID + " ?"
EndSelect
CloseFile(#hFile)
Else
Debug " - Pas d'EXIF"
EndIf
EndIf
Debug ""
Wend
FinishDirectory(#hDir)
EndIf

Re: Lire le tag EXIF de rotation d'image JPG
Publié : mer. 15/août/2018 10:22
par boby
Merci de te prendre la tête à notre place Marc :p
Si tu t'en sors dans la lecture des EXIF ça fera un bon code à se garder sous la main

Re: Lire le tag EXIF de rotation d'image JPG
Publié : mer. 15/août/2018 10:43
par Marc56
Marc56 a écrit :Pas encore au point, car si je liste un répertoire, j'ai parfois des valeurs supérieures à 8
J'suis bête

c'est quand il n'y a pas d'EXIF
Il faut tester avant, la présence du mot « Exif » à l'offset 6
Code : Tout sélectionner
EnableExplicit
Enumeration
#hDir
#hFile
EndEnumeration
Define Repertoire$ = PathRequester("Répertoire à lister", GetUserDirectory(#PB_Directory_Pictures))
If ExamineDirectory(#hDir, Repertoire$, "*.jpg")
While NextDirectoryEntry(#hDir)
If DirectoryEntryType(#hDir) = #PB_DirectoryEntry_File
Define Fichier$ = DirectoryEntryName(#hDir)
Debug Fichier$ + " ?"
If ReadFile(#hFile, Repertoire$ + Fichier$) ;, #PB_Unicode)
Debug " - Taille : " + FormatNumber(Lof(#hFile), 0) + " octets"
FileSeek(#hFile, 6)
If ReadString(#hFile) = "Exif"
FileSeek(#hFile, 54)
Define ID = ReadByte(#hFile)
Select ID
Case 1 : Debug " - ID : " + ID + " -> Horizontal (normal)"
Case 2 : Debug " - ID : " + ID + " -> Mirror horizontal"
Case 3 : Debug " - ID : " + ID + " -> Rotate 180"
Case 4 : Debug " - ID : " + ID + " -> Mirror vertical"
Case 5 : Debug " - ID : " + ID + " -> Mirror horizontal And rotate 270 CW"
Case 6 : Debug " - ID : " + ID + " -> Rotate 90 CW"
Case 7 : Debug " - ID : " + ID + " -> Mirror horizontal And rotate 90 CW"
Case 8 : Debug " - ID : " + ID + " -> Rotate 270 CW"
Default : Debug " - ID : " + ID + " ?"
EndSelect
Else
Debug " - Pas d'EXIF"
EndIf
CloseFile(#hFile)
Else
Debug " - Pas d'EXIF"
EndIf
EndIf
Debug ""
Wend
FinishDirectory(#hDir)
EndIf
End

Re: Lire le tag EXIF de rotation d'image JPG
Publié : mer. 15/août/2018 19:22
par Kwai chang caine
Merci Marc

Re: Lire le tag EXIF de rotation d'image JPG
Publié : mer. 15/août/2018 23:44
par Ar-S
Marc
Merci pour cette tentative.
Je viens de tester sur 8 photos que j'ai pris avec mon smartphone (Samsung J7)
Si je les redimensionne et les sauve, les verticales finissent horizontales.
Ton code n'affiche pourtant
aucun exif sur toutes les originales. Y'a une couille dans l'potage.
J'ai vérifié avec ExifViewer et il y a pourtant bien une info exif (rotation gauche) sur les images concernées.
0112 / Orientation / 8
Re: Lire le tag EXIF de rotation d'image JPG
Publié : jeu. 16/août/2018 7:52
par Marc56
Bien vu.
Je viens aussi de regarder celles prises avec mon S4, il affiche comme ID -98 pour des photos à la verticale alors qu'Irfanview affiche Right-Top (donc doit tourner vers la droite)
Je pense que c'est une question de version d'EXIF
On ne peut donc pas se fier à l'offset, il faut chercher la balise, comme indiqué dans la doc et dans mon premier exemple
Code : Tout sélectionner
; --- Procédures
Procedure Search_Orientation(Fichier$)
Debug "Fichier: " + Fichier$
If Not ReadFile(0, Fichier$, #PB_Unicode)
Debug "Fichier illisible"
ProcedureReturn
EndIf
length = Lof(0)
Debug "Taille : " + FormatNumber(length, 0) + " octets"
For i = 0 To length
FileSeek(0, i)
If ReadUnicodeCharacter(0) = $0112 ; ou 274 en décimal
Debug "Offset : " + i
FileSeek(0, 6, #PB_Relative)
ID = ReadCharacter(0)
Debug "Valeur : " + ID
Select ID
Case 1 : Debug "Image Horizontale"
Case 6 : Debug "Image à tourner vers Droite"
Case 8 : Debug "Image à tourner vers Gauche"
EndSelect
Break
EndIf
Next
CloseFile(0)
Debug ""
EndProcedure
Search_Orientation(" ")
J'avais aussi inversé gauche et droite.

Je vais reprendre mon
ACME Viewer et lui ajouter l'affichage des exifs et la rotation automatique des images verticales.

Re: Lire le tag EXIF de rotation d'image JPG
Publié : jeu. 16/août/2018 9:01
par Ar-S
C'est mieux ainsi

Du coup tu vas procéder comment ? Tu vires l'exif et tu appliques une rotation physique via vectordrawing ? (ça risque d'être long)
ou tu vas recréer l'info exif. Si tu optes pour cette dernière solution je veux bien savoir comment tu procèdes.
---edit---
sur ce
topic wichtel lit et crée un fichier ne contenant que l'exif qu'il regreffe ensuite à la nouvelle image.
Re: Lire le tag EXIF de rotation d'image JPG
Publié : jeu. 16/août/2018 14:52
par Marc56
Ar-S a écrit :C'est mieux ainsi

Du coup tu vas procéder comment ? Tu vires l'exif et tu appliques une rotation physique via vectordrawing ? (ça risque d'être long)
ou tu vas recréer l'info exif. Si tu optes pour cette dernière solution je veux bien savoir comment tu procèdes.
Vectordrawing va vite pour faire tourner l'image
1. Je charge l'image d'origine
2. Je créé une image cible de proportions inversées
3. Je déplace le point zéro
4. Je tourne le système de coordonnées (RotateCoordinates(0, 0, 90))
5. Je trace l'image
6. Je sauvegarde
Code : Tout sélectionner
EnableExplicit
Enumeration
#gImg
#Img_In
#Img_Out
EndEnumeration
#File_IN = "C:\DataTest\EXIF_V6.JPG" ; Image test: 4200x2800 6.5 Mo verticale avec exif
#File_OUT = "C:\DataTest\EXIF_V6_OUT.JPG"
UseJPEGImageDecoder()
UseJPEGImageEncoder()
Define Start_Time = ElapsedMilliseconds()
If LoadImage(#Img_In, #File_IN)
Debug "Image chargée. (" + #File_IN + ")"
Define W = ImageWidth(#Img_In) : Debug "Largeur: " + W
Define H = ImageHeight(#Img_In) : Debug "Hauteur: " + H
CreateImage(#Img_Out, H, W) ; Proportions inversées
If StartVectorDrawing(ImageVectorOutput(#Img_Out))
Debug "Rotation..."
MovePathCursor(H, 0)
RotateCoordinates(0, 0, 90)
DrawVectorImage(ImageID(#Img_In))
StopVectorDrawing()
Debug "Rotation Terminée."
Debug FormatNumber( (ElapsedMilliseconds() - Start_Time) / 1000.0, 2) + " Secondes"
If SaveImage(#Img_Out, #File_OUT, #PB_ImagePlugin_JPEG)
Debug "Sauvegarde OK"
Else
Debug "Sauvegarde KO"
EndIf
EndIf
Else
Debug "Je ne peux pas charger l'image :-("
End
EndIf
RunProgram(#File_OUT, "", "")
End
On doit pouvoir faire bien mieux, je ne suis pas un pro du binaire et des images.
Test simple, avec une image tournée à droite (il faut donc lire l'orientation avant pour tourner dans le bon sens)
Ici, avec une image de test de 4200x2800 pour 6.5 Mo, mon petit i3 met 1.5 secondes

ce qui me suffit car je ne fais pas en série
Évidement, SaveImage fait perdre tous les exifs, il faudrait donc les sauver avant puis les réécrire et modifier celui de l'orientation
C'est quand même bien de pouvoir faire tout ça avec ce qu'il y a en standard dans PB. Merci Fred
Les références du format EXIF sont là
https://fr.wikipedia.org/wiki/Exchangea ... ile_format
http://owl.phy.queensu.ca/~phil/exiftoo ... /EXIF.html

Re: Lire le tag EXIF de rotation d'image JPG
Publié : ven. 17/août/2018 3:23
par Ollivier
Question con : les 6 1ers octets ils ressemblent à quoi?
C'est pas un Byte Order Mark en unicode (3 * 2 octets)?
Re: Lire le tag EXIF de rotation d'image JPG
Publié : ven. 17/août/2018 7:06
par Marc56
Ollivier a écrit :Question con : les 6 1ers octets ils ressemblent à quoi?
C'est pas un Byte Order Mark en unicode (3 * 2 octets)?
Le 3 premiers sont FF D8 FF (lu avec un éditeur hexa)
C'est l'indicateur de fichier JPEG
https://www.filesignatures.net/index.ph ... 0&mode=SIG
Le reste est décrit dans les liens externes de Wikipedia sur les EXIF
y compris le PDF officiel (190 pages)
https://fr.wikipedia.org/wiki/Exchangea ... ile_format

Re: Lire le tag EXIF de rotation d'image JPG
Publié : ven. 31/août/2018 20:19
par Ar-S
@Marc56
Petit retour sur ce topic pour te dire que j'ai appliqué ton code à mon resizer gold et je m'occupe de pivoter les images exif 3, 6 et 8 dans le bon sens via la vectordrawing.
Le rendu est un peu plus lent vu qu'il y a quelques photos à retourner en générale dans un lot mais dans l'ensemble ça reste performant.
Merci pour tes contributions précieuses.
Re: Lire le tag EXIF de rotation d'image JPG
Publié : sam. 01/sept./2018 10:36
par Marc56
Spécial merci en retour à Dobro|Spock|Zorro

pour ses très nombreux exemples, certes parfois fouillis

mais ô combien utiles et abondamment commentés grâce auxquels j'ai pu comprendre et enfin oser "peekorer" (= avancer octet par octet et lire un bloc à chaque pas) directement dans les fichiers binaires plutôt que d'utiliser des libs tiers non
Fred' Certified.
(Quand on a qu'une seule info à extraire c'est plus simple de le faire directement plutôt que d'inclure des centaines de lignes.)

Pour la vitesse de recherche, il doit y avoir moyen d'aller directement à l'information en chargeant la taille de la structure. Information qu'on doit trouver dans la doc, mais mon anglais est trop mauvais pour tout comprendre
Merci aussi à l'ensemble du forum pour l'aide en général (ex: les conversion de fonction ascii/unicode)
