2

I'm running Python 3.10 on Windows 11. I need to extract metadata from .heic image files. Here is what I tried:

1. ExifRead

I tried with ExifRead (see https://pypi.org/project/ExifRead/) but that failed:

>>> import exifread
>>> f = open("path/to/img.heic", 'rb')
>>> tags = exifread.process_file(f)
Traceback (most recent call last):
  File "C:\Python310\lib\site-packages\exifread\heic.py", line 171, in get_parser
    return defs[box.name]
KeyError: 'hdlr'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python310\lib\site-packages\exifread\__init__.py", line 137, in process_file
    offset, endian, fake_exif = _determine_type(fh)
  File "C:\Python310\lib\site-packages\exifread\__init__.py", line 109, in _determine_type
    offset, endian = heic.find_exif()
  File "C:\Python310\lib\site-packages\exifread\heic.py", line 268, in find_exif
    meta = self.expect_parse('meta')
  File "C:\Python310\lib\site-packages\exifread\heic.py", line 159, in expect_parse
    return self.parse_box(box)
  File "C:\Python310\lib\site-packages\exifread\heic.py", line 177, in parse_box
    probe(box)
  File "C:\Python310\lib\site-packages\exifread\heic.py", line 195, in _parse_meta
    psub = self.get_parser(box)
  File "C:\Python310\lib\site-packages\exifread\heic.py", line 173, in get_parser
    raise NoParser(box.name) from err
exifread.heic.NoParser: hdlr

2. pyheif

I tried to install the pyheif module, but there is no build for Windows.

3. pillow

I tried with the pillow module (aka PIL):

>>> from PIL import Image
>>> img = Image.open("path/to/img.HEIC")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python310\lib\site-packages\PIL\Image.py", line 3280, in open
    raise UnidentifiedImageError(msg)
PIL.UnidentifiedImageError: cannot identify image file 'C:/Backup/Pictures_2023/IMG_0620.HEIC'

4 Answers 4

1

I found a way to extract the metadata in Python 3.10 on Windows 11:

import subprocess

def get_photo_metadata(filepath):
    filepath = filepath.replace('/', '\\')
    filepath = filepath.replace('\\', '\\\\')
    cmd = f'cmd.exe /c wmic datafile "{filepath}" list full'
    output = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        shell=True,
    ).communicate()[0]
    output_utf = output.decode('utf-8', errors='ignore')
    return output_utf

print(
    get_photo_metadata("path/to/img.HEIC")
)

This prints:

AccessMask=1507775
Archive=TRUE
Caption=C:\Backup\Pictures_2023\CHINA\202308_b\IMG_0620.HEIC
Compressed=FALSE
CompressionMethod=
CreationClassName=CIM_LogicalFile
CreationDate=20230817080632.333836+120
CSCreationClassName=Win32_ComputerSystem
CSName=SKIKK-2022
Description=C:\Backup\Pictures_2023\CHINA\202308_b\IMG_0620.HEIC
Drive=c:
EightDotThreeFileName=c:\backup\pictures_2023\china\202308_b\img_06~3.hei
Encrypted=FALSE
EncryptionMethod=
Extension=HEIC
FileName=IMG_0620
FileSize=1823601
FileType=HEIC File
FSCreationClassName=Win32_FileSystem
FSName=NTFS
Hidden=FALSE
InstallDate=20230817080632.333836+120
InUseCount=
LastAccessed=20230904163653.526369+120
LastModified=20230808123918.000000+120
Manufacturer=
Name=C:\Backup\Pictures_2023\CHINA\202308_b\IMG_0620.HEIC
Path=\backup\pictures_2023\china\202308_b\
Readable=TRUE
Status=OK
System=FALSE
Version=
Writeable=TRUE

Note: I made the following script to extract dates from the photo:

import subprocess
import re
import datetime

p = re.compile(r'(20\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)\.(\d*)')

def get_photo_dates(filepath) -> datetime.datetime:
    filepath = filepath.replace('/', '\\')
    filepath = filepath.replace('\\', '\\\\')
    cmd = f'cmd.exe /c wmic datafile "{filepath}" list full'
    output = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        shell=True,
    ).communicate()[0]
    output_utf = output.decode('utf-8', errors='ignore')
    dates = []
    for m in p.finditer(output_utf):
        dates.append(
            datetime.datetime(
                year        = int(m.group(1)),
                month       = int(m.group(2)),
                day         = int(m.group(3)),
                hour        = int(m.group(4)),
                minute      = int(m.group(5)),
                second      = int(m.group(6)),
                microsecond = int(m.group(7)),
            )
        )
    return sorted(dates)
Sign up to request clarification or add additional context in comments.

Comments

1

I too have been struggling with this. Exiftool works well, but only supports windows (I'm tearing my hair out trying to find an OS-agnostic alternative that can be installed easily from requirements.txt).

Anyhoo, here you go:

import exiftool               # Install PyExifTool
exiftool_path = os.path.abspath("./exiftool.exe")
os.environ['EXIFTOOL_PATH'] = exiftool_path

def get_metadata(files):
    metadata = []
    with exiftool.ExifToolHelper() as et:
        metadata = et.get_metadata(files)
        return metadata

Download & rename exiftool as per https://exiftool.org/install.html but instead of putting it in your windows folder, put it in the same folder as your script. My import statement takes care of the 'PATH' stuff.

The 'files' variable can be a path, or a list of paths (for working with batches). It returns a list of dicts - one for each file.

Comments

0

Apparently downgrading to exifread 2.x also solves this issue. See comment here: https://github.com/ianare/exif-py/issues/184#issuecomment-1856296220vn

Comments

-1

For a more OS-agnostic approach, this piece of code retrieves the original date and time from the file while also returning all EXIF tags.

from PIL import Image
from PIL.ExifTags import TAGS
from pillow_heif import register_heif_opener

def get_date_taken_from_heic(heic_file_path):
    """
    Extracts the 'Date Taken' (DateTimeOriginal) from a HEIC file.

    Args:
        heic_file_path (str): The path to the HEIC file.

    Returns:
        str or None: The date and time string if found, otherwise None.
    """
    try:
        # Register the HEIF opener to allow Pillow to open HEIC files
        register_heif_opener()

        with Image.open(heic_file_path) as img:
            exif_data = img.getexif()

            if exif_data:
                # Print all EXIF tags and values
                for key, value in exif_data.items():
                    tag_name = TAGS.get(key, key)
                    print(f"Tag: {tag_name}, Value: {value}")

                # Find the tag for DateTime
                for tag_id, value in exif_data.items():
                    tag_name = TAGS.get(tag_id, tag_id)
                    # if tag_name == 'DateTimeOriginal':
                    if tag_name == 'DateTime':
                        return value
            return None
    except Exception as e:
        print(f"Error processing {heic_file_path}: {e}")
        return None

# Example usage:
heic_file = "your_image.heic"  # Replace with the actual path to your HEIC file
date_taken = get_date_taken_from_heic(heic_file)

if date_taken:
    print(f"Date Taken for '{heic_file}': {date_taken}")
else:
    print(f"Date Taken information not found in '{heic_file}'.")

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.