tiny userlib: Exif date/time information (UTC)

Developed or developing a new product in PureBasic? Tell the world about it.
sverson
Enthusiast
Enthusiast
Posts: 286
Joined: Sun Jul 04, 2004 12:15 pm
Location: Germany

tiny userlib: Exif date/time information (UTC)

Post by sverson »

Hi!

This is a tiny "one command userlib" to get Exif date/time information (UTC) of jpeg images:

ExifDate()

Download: ExifDate.zip

ExifDate -> PureLibraries/UserLibraries
ExifDate.chm -> Help

Have fun - and please tell me if you like it.
;-) sverson
sverson
Enthusiast
Enthusiast
Posts: 286
Joined: Sun Jul 04, 2004 12:15 pm
Location: Germany

Bugfix: tiny userlib: Exif date/time information (UTC)

Post by sverson »

BUGFIX

Download v1.1: ExifDateV11.zip
  • ExifDate -> PureLibraries/UserLibraries
    ExifDate.chm -> Help
;-) sverson
User avatar
GeoTrail
Addict
Addict
Posts: 2794
Joined: Fri Feb 13, 2004 12:45 am
Location: Bergen, Norway
Contact:

Post by GeoTrail »

Does anyone know how to write Exif info to a jpg file?
I Stepped On A Cornflake!!! Now I'm A Cereal Killer!
Bonne_den_kule
Addict
Addict
Posts: 841
Joined: Mon Jun 07, 2004 7:10 pm

Post by Bonne_den_kule »

Here is a reference to the meta data (exif) in jpg pictures :
http://www.kodak.com/global/plugins/acr ... ndard2.pdf
User avatar
GeoTrail
Addict
Addict
Posts: 2794
Joined: Fri Feb 13, 2004 12:45 am
Location: Bergen, Norway
Contact:

Post by GeoTrail »

Thanks for the link Bonne, but I didn't help me much.
Reference to the format is one thing, but being able to write the tags into an image without corrupting it is one thing ;)
I Stepped On A Cornflake!!! Now I'm A Cereal Killer!
Bonne_den_kule
Addict
Addict
Posts: 841
Joined: Mon Jun 07, 2004 7:10 pm

Post by Bonne_den_kule »

GeoTrail wrote:Thanks for the link Bonne, but I didn't help me much.
Reference to the format is one thing, but being able to write the tags into an image without corrupting it is one thing ;)
To change the tag you need to rebuild the image, since the header (meta-data) has a varibale size
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Post by PB »

> To change the tag you need to rebuild the image

Maybe not -- if the tag can be read at offsets, then perhaps (haven't tried) it
can be changed by writing new bytes to the image file... I'll have a look later.
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
User avatar
GeoTrail
Addict
Addict
Posts: 2794
Joined: Fri Feb 13, 2004 12:45 am
Location: Bergen, Norway
Contact:

Post by GeoTrail »

Kewl, thanks :)
I Stepped On A Cornflake!!! Now I'm A Cereal Killer!
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Post by PB »

Okay, I had a look, but I'm no expert on digital photos. This information might
apply only to my digital camera (Pentax Optio S).
When I load a photo into a
hex editor, there are 3 areas that the time/date are visible, and they are all
in the string format of : YYYY:MM:DD HH:MM:SS (24-hour time is used).

The three offsets in the file are at bytes 228, 694 and 714. So, for my photos,
I can change the stored date using this code (try it at your own risk, and use
backups of your photos!):

Code: Select all

; Modify Exif Date example by PB.  Feel free to use in any way. :)
; Works fine with photos taken with a Pentax Optio S digital camera.
; May work with photos by other cameras, but no guarantee is given!
;
; *** Any use of this procedure is entirely at your own risk! ***

Procedure ModifyExifDate(file$,ye,mo,da,ho,mi,se)
  If OpenFile(0,file$)=0
    ProcedureReturn 0 ; Couldn't open file, or it doesn't exist.
  Else
    a$=FormatDate("%yyyy:%mm:%dd %hh:%ii:%ss",Date(ye,mo,da,ho,mi,se)) ; Prepare new date to write.
    ; Next line writes new date into the 3 date offsets in the photo's Exif Data (228, 694 and 714).
    FileSeek(228) : WriteString(a$) : FileSeek(694) : WriteString(a$) : FileSeek(714) : WriteString(a$)
    CloseFile(0) : ProcedureReturn 1 ; Successful! :)
  EndIf
EndProcedure

Debug ModifyExifDate("C:\IMGP0695.JPG",2005,12,25,0,0,0) ; Modify date to Christmas Day for this example.
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
User avatar
GeoTrail
Addict
Addict
Posts: 2794
Joined: Fri Feb 13, 2004 12:45 am
Location: Bergen, Norway
Contact:

Post by GeoTrail »

Thanks for trying to help PB.
I tried that procedure with pictures I took with my S700i and PowerShot A60, but when I checked the file afterwards the time and date field was gone.

But when I check the files with a hex editor I can see that it is written.

Here I've taken screenshots of the hex source of the images.

This image is the one that I modified the date in using the procedure.
Image

and this image is the unmodified image:
Image
I Stepped On A Cornflake!!! Now I'm A Cereal Killer!
Sparkie
PureBatMan Forever
PureBatMan Forever
Posts: 2307
Joined: Tue Feb 10, 2004 3:07 am
Location: Ohio, USA

Post by Sparkie »

@GeoTrail: Try this one. Please make sure you work with a copy of the original image. This works with my Canon A75 but no guarantee it will work for you. ;)

Code: Select all

UseJPEGImageDecoder()
#Exif_DateTime = 306
#Header = 12
Global date_time
Procedure.s GetExifDateTime(jpg$, writeIt, newDateTime$)
  OpenFile(0, jpg$)
  ; --> Byte 0 of EXIF begins after JPEG header
  FileSeek(#Header)
  ; --> Bytes 0-1 is word order 18761 ($4949) is Intel and 19789 ($4D4D) is Motorola
  wordOrder = ReadWord()
  If wordOrder = $4949
    ; --> Bytes 2-3 is TIFF format, it's always 42 ($2A). If not, give up.
    tifFormat = ReadWord()
    If tifFormat = $2A
      ; --> Bytes 4-7 is starting offset for IFD (Image File Directory)
      ifd1 = ReadLong()
      ; --> Move to start of IFD
      FileSeek(ifd1 + #Header)
      ; --> First 2 bytes of IFD is number of field entries
      nFields = ReadWord()
      ; --> Loop through all fields to find Date/Time stamp
      date_time = #False
      For i = 1 To nFields
        ; --> Bytes 0-1 contain the Tag for the field.
        currentTag = ReadWord()
        If currentTag = #Exif_DateTime
          ; --> Bytes 2-3 contain the field Type.
          ; --> We know this will be 2 (ASCII) For Date/Time
          fieldType = ReadWord()
          ; --> Bytes 4-7 contain the  Length of the field.
          fieldLength = ReadLong()
          currentloc = Loc()
          ; --> Bytes 8-11 contain a pointer to ASCII Date/Time
          fieldValue = ReadLong()
          ; --> Move to that pointer
          FileSeek(fieldValue + #Header)
          ; --> This is the start point of Dat/Time ASCII string
          If writeIt And newDateTime$
            WriteString(newDateTime$)
            date_time$ = newDateTime$
            MessageRequester("Info", "Date and Time have been changed.")
          Else
            date_time$ = ReadString()
          EndIf
          date_time = #True
          Break
        Else
          ; --> Move to next field. Each field is 12 bytes.
          ; --> currentTag (2 bytes) is current Loc() so we add 10
          FileSeek(Loc()+10)
        EndIf
      Next
      If date_time <> #True
        MessageRequester("Sorry", "Date Time field was not found.")
      EndIf
    Else
      MessageRequester("Error", "Invalid file format!")
    EndIf
  Else
    MessageRequester("Sorry", "Invalid file format!")
  EndIf
  CloseFile(0)
  ProcedureReturn date_time$
EndProcedure

If OpenWindow(0, 10, 10, 300, 200, #PB_Window_SystemMenu, "Exif Date/Time") And CreateGadgetList(WindowID())
  ButtonGadget(0, 10, 10, 280, 20, "Open JPG")
  TextGadget(1, 10, 40, 280, 60, "", #PB_Text_Center)
  TextGadget(2, 10, 80, 280, 20, "", #PB_Text_Center)
  ButtonGadget(3, 10, 110, 280, 20, "Change Date")
  DisableGadget(3, 1)
  DateGadget(4, 10, 140, 280, 20, "%yyyy:%mm:%dd  %hh:%ii")
  Repeat
    event = WaitWindowEvent()
    If event = #PB_EventGadget
      Select EventGadgetID()
        Case 0
          fileOpen$ = (OpenFileRequester("Select JPEG", "C:\Documents and Settings\Owner\My Documents\My Pictures\", "JPG files (JPG)|*.jpg;*.jpeg", 0))
          If fileOpen$
            dt$ = GetExifDateTime(fileOpen$, 0, "")
            If date_time
              SetGadgetText(1, fileOpen$)
              SetGadgetText(2, "Original Date/Time is " + dt$)
              DisableGadget(3, 0)
            EndIf
          EndIf
        Case 3
          ; --> DateGadget has no support for seconds so add your own below
          newDT$ = GetGadgetText(4) + ":00"
          dt$ = GetExifDateTime(fileOpen$, 1, newDT$)
      EndSelect
    EndIf
  Until event = #PB_Event_CloseWindow
EndIf
End
What goes around comes around.

PB 5.21 LTS (x86) - Windows 8.1
User avatar
GeoTrail
Addict
Addict
Posts: 2794
Joined: Fri Feb 13, 2004 12:45 am
Location: Bergen, Norway
Contact:

Post by GeoTrail »

Nope, didn't work :shock:
Appriciate your efforts Sparkie :)
After I tried changing the date and checked the image file, the date was gone, along with all the other camera info like ISO format, flash etc.
I Stepped On A Cornflake!!! Now I'm A Cereal Killer!
Sparkie
PureBatMan Forever
PureBatMan Forever
Posts: 2307
Joined: Tue Feb 10, 2004 3:07 am
Location: Ohio, USA

Post by Sparkie »

I'm at work right now, but when I get home I'll see what else we can try.

I looked at your hex code ^up there^ and it looks very similar to my A75 jpg output. :?
What goes around comes around.

PB 5.21 LTS (x86) - Windows 8.1
Sparkie
PureBatMan Forever
PureBatMan Forever
Posts: 2307
Joined: Tue Feb 10, 2004 3:07 am
Location: Ohio, USA

Post by Sparkie »

Doh! We were working with the wrong timestamp. That's the one that specifies the timestamp of last file modification. I asume you are looking for the timestamp of when the pic was taken/digitized.

I'll have to do some reading to figure this one out. ;)
What goes around comes around.

PB 5.21 LTS (x86) - Windows 8.1
Sparkie
PureBatMan Forever
PureBatMan Forever
Posts: 2307
Joined: Tue Feb 10, 2004 3:07 am
Location: Ohio, USA

Post by Sparkie »

@GeoTrail: I'm almost done with Exif date change, but before proceeding I'd like to see what results you get with the code below. Since you're using a Canon A60, this should show 3 timestamps:

Date last modified
Date picture was taken
Date picture was digitized

This is just a modified section of the code I have so far. If it doesn't work, don't worry. It just means the your A60 and my A75 do things a little differently and I'll have to adjust the code a little. ;)

Code: Select all

UseJPEGImageDecoder() 
jpg$ = OpenFileRequester("Select JPEG", "C:\Documents and Settings\Owner\My Documents\My Pictures\", "JPG files (JPG)|*.jpg;*.jpeg", 0)
If jpg$
  OpenFile(0, jpg$) 
  FileSeek(188)
  dateInfo$ = "Modified: " + Chr(9)
  dateInfo$ + ReadString() + #CRLF$
  FileSeek(586)
  dateInfo$ + "Taken: " + Chr(9)
  dateInfo$ + ReadString() + #CRLF$
  FileSeek(606)
  dateInfo$ + "Digitized: " + Chr(9)
  dateInfo$ + ReadString()
  CloseFile(0) 
  MessageRequester("Exif Date Info", dateInfo$)
EndIf
End 
What goes around comes around.

PB 5.21 LTS (x86) - Windows 8.1
Post Reply