Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cannot build naive datetime, reason: :invalid_date #163

Closed
sergiotapia opened this issue Sep 5, 2024 · 8 comments
Closed

cannot build naive datetime, reason: :invalid_date #163

sergiotapia opened this issue Sep 5, 2024 · 8 comments

Comments

@sergiotapia
Copy link

** (ArgumentError) cannot build naive datetime, reason: :invalid_date
    (elixir 1.16.2) lib/calendar/naive_datetime.ex:340: NaiveDateTime.new!/8
    (image 0.53.0) lib/image/exif/decode.ex:25: Image.Exif.Decode.tag/3
    (image 0.53.0) lib/image/exif.ex:127: Image.Exif.read_tags/5
    (image 0.53.0) lib/image/exif.ex:92: Image.Exif.extract_exif/1
    (image 0.53.0) lib/image.ex:3545: Image.exif/1
    (sell_sight 0.1.0) lib/sell_sight/batches.ex:113: SellSight.Batches.extract_exif_date_time_original/1
    iex:1: (file)

The app crashes when I reach the Image.exif/1 call:

with {:ok, file_binary} <- File.read("image.jpg"),
     image <- Image.from_binary!(file_binary) do
  case Image.exif(image) do

The image causing this bug is, I'm not sure if the exif is preserved through Github:

542

image

Let me know if there is a better way to troubleshoot this. Thank you for the awesome library!

@kipcole9
Copy link
Collaborator

kipcole9 commented Sep 5, 2024

Thanks for the issue. I've published a image version 0.54.3 with the following changelog entry:

Bug Fixes

  • Fix parsing invalid date time in Exif data. When invalid, the raw underlying string is returned.

  • Trim strings in Exif data. As a result, empty strings will be returned now as nil rather than "".

  • Replace a <<0, ....>> sequence in an Exif string value with nil.

  • Decode additional tags:

    • Exif tag 0xA005, Interop Offset. The value is in internal file pointer so has no meaningful interpretation in image.

    • TIFF tag 0xC4A5, Print Image Matching. According to this source the field has no standardised values so the raw value is returned.

@kipcole9
Copy link
Collaborator

kipcole9 commented Sep 5, 2024

Let me know if that works for you now? A couple of other comments/questions:

  1. Would you be OK if I add your image to my test suite?
  2. Your example shows:
with {:ok, file_binary} <- File.read("image.jpg"),
     image <- Image.from_binary!(file_binary) do
  case Image.exif(image) do
      ...
  end
end

This is quite inefficient in image terms. It will read the whole image file when it doesn't need to. libvips is a streaming implementation and it only renders pixels when it has to. When opening a file with Image.open/2, the image file is opened, and only header information is read.

The result is less memory consumption, and a lot less CPU utilisation since libvips doesn't have to decode the whole image - which is has to do if you do Image.from_binary/1.

I would strongly suggest:

with {:ok, image} <- Image.open("image.jpg") do
  case Image.exif(image) do
    ...
  end
end

@sergiotapia
Copy link
Author

Of course please feel free to use it in your test suite. Also amazing tip, thank you I wasn't aware

I will update and try it now, thank you @kipcole9!

@sergiotapia
Copy link
Author

with {:ok, image} <- Image.open(file_path) do
  try do
    case Image.exif(image) do
      {:ok, exif_data} ->
        datetime_original = get_in(exif_data, [:exif, :datetime_original])
        IO.inspect(datetime_original, label: "EXIF DATETIME ORIGINAL")
        {:ok, datetime_original}

This is inspecting to "0000:00:00 00:00:00"

My Ecto field where I am saving the date is: field :exif_date_time_original, :naive_datetime

So the validation is currently failing. I'll have to check for that "0000" value and if it's present just set to nil. I wonder why the exif string is returning like that when in Windows properties I can see the date there.

@kipcole9
Copy link
Collaborator

kipcole9 commented Sep 6, 2024

That’s “interesting”. I’ll check what exiftool returns when I’m back at my desk. It’s possible windows is using another field when the exif fields are invalid.

im out and about for an hour or so but will dig deeper when I’m back.

@kipcole9 kipcole9 reopened this Sep 6, 2024
@kipcole9
Copy link
Collaborator

kipcole9 commented Sep 6, 2024

The "good news" is that exiftool also returns the same information as image does:

% exiftool /Users/kip/Desktop/364959210-4892c3cf-b9b4-4c7c-8f5d-9fcd86b6e42b.jpg
Modify Date                     : 0000:00:00 00:00:00
.....
Date/Time Original              : 0000:00:00 00:00:00
Create Date                     : 0000:00:00 00:00:00

Therefore my working assumption is that the dates that Windows is showing are the file create/modify date times, not the image shoot times. And indeed if I look at the output from exiftool I see:

File Modification Date/Time     : 2024:09:06 08:04:34+10:00
File Access Date/Time           : 2024:09:06 09:00:59+10:00
File Inode Change Date/Time     : 2024:09:06 08:04:35+10:00

Which are the dates/times of the file - on my machine. Of course they will be different on your machine.

@sergiotapia
Copy link
Author

So in my code I need to write if the exif date is "0000" I need to sniff out the create date time, not the image shoot times. Thanks @kipcole9 🙏

@kipcole9
Copy link
Collaborator

kipcole9 commented Sep 6, 2024

I think the best approach is to check based upon the type. If the date is valid then the type will be a NaiveDateTime.t, if it's invalid it will be a String.t.

Whether the file creation date time is relevant depends on your use case. If you're reading images directly from a camera card then the file creation date and the shooting date will likely be the same.

In other cases, its quite likely the file creation date is uncorrelated with the shooting date.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants