or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/pypi-piexif

Pure Python library for EXIF metadata manipulation in JPEG and WebP image files.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
pypipkg:pypi/piexif@1.1.x

To install, run

npx @tessl/cli install tessl/pypi-piexif@1.1.0

index.mddocs/

Piexif

Pure Python library for EXIF (Exchangeable Image File Format) metadata manipulation in JPEG and WebP image files. Piexif provides a simple and comprehensive API with five core functions for loading, dumping, inserting, removing, and transplanting EXIF data without external dependencies.

Package Information

  • Package Name: piexif
  • Package Type: pypi
  • Language: Python
  • Installation: pip install piexif
  • Supported Python Versions: 2.7, 3.5+, PyPy, IronPython
  • Supported Formats: JPEG (all functions), TIFF (load only), WebP (load, dump, insert, remove)

Core Imports

import piexif

For specific functionality:

from piexif import load, dump, insert, remove, transplant
from piexif import ImageIFD, ExifIFD, GPSIFD, InteropIFD, TYPES, TAGS
from piexif.helper import UserComment

Basic Usage

import piexif

# Load EXIF data from an image
exif_dict = piexif.load("image.jpg")

# Examine EXIF data structure
for ifd in ("0th", "Exif", "GPS", "1st"):
    for tag in exif_dict[ifd]:
        tag_name = piexif.TAGS[ifd][tag]["name"]
        print(f"{tag_name}: {exif_dict[ifd][tag]}")

# Modify EXIF data
exif_dict["0th"][piexif.ImageIFD.Software] = b"My Software"
exif_dict["Exif"][piexif.ExifIFD.DateTimeOriginal] = b"2023:12:25 10:30:00"

# Convert back to bytes and insert into a new image
exif_bytes = piexif.dump(exif_dict)
piexif.insert(exif_bytes, "input.jpg", "output.jpg")

Architecture

Piexif operates on the EXIF (Exchangeable Image File Format) data structure, which is embedded within image files as metadata. The EXIF format organizes metadata into multiple Image File Directories (IFDs), each containing related sets of tags:

  • 0th IFD (Main Image): Primary image metadata using ImageIFD tags (dimensions, orientation, camera make/model, etc.)
  • Exif IFD: Camera-specific settings using ExifIFD tags (exposure, ISO, focal length, date/time, etc.)
  • GPS IFD: Location metadata using GPSIFD tags (coordinates, altitude, direction, etc.)
  • Interop IFD: Interoperability data using InteropIFD tags (compatibility information)
  • 1st IFD (Thumbnail): Thumbnail image metadata using ImageIFD tags
  • Thumbnail Data: Embedded JPEG thumbnail image as raw bytes

This hierarchical structure allows piexif to preserve all metadata relationships while providing simple dictionary-based access to individual tags and values.

Capabilities

EXIF Data Loading

Load EXIF metadata from image files into a structured dictionary format.

def load(input_data, key_is_name=False):
    """
    Load EXIF data from JPEG, TIFF, or WebP files.

    Parameters:
    - input_data: str (file path) or bytes (image data)
    - key_is_name: bool, optional - Use tag names as keys instead of tag numbers (default: False)

    Returns:
    dict: EXIF data with IFD structure
    {
        "0th": dict,      # Main image metadata (ImageIFD tags)
        "Exif": dict,     # Camera-specific metadata (ExifIFD tags)
        "GPS": dict,      # GPS location data (GPSIFD tags)
        "Interop": dict,  # Interoperability data (InteropIFD tags)
        "1st": dict,      # Thumbnail image metadata (ImageIFD tags)
        "thumbnail": bytes # Thumbnail JPEG data or None
    }

    Raises:
    InvalidImageDataError: If image data is corrupted or invalid
    """

EXIF Data Serialization

Convert EXIF dictionary data back to binary format for storage in image files.

def dump(exif_dict):
    """
    Convert EXIF dictionary to bytes format.

    Parameters:
    - exif_dict: dict - EXIF data dictionary with IFD structure

    Returns:
    bytes: EXIF data in binary format with proper headers

    Raises:
    ValueError: If EXIF dictionary structure is invalid
    """

EXIF Data Insertion

Insert EXIF metadata into image files, supporting both file paths and binary data.

def insert(exif, image, new_file=None):
    """
    Insert EXIF data into JPEG or WebP image.

    Parameters:
    - exif: bytes - EXIF data in binary format (from dump())
    - image: str (file path) or bytes (image data) - Target image
    - new_file: str, optional - Output file path; if None, modifies original

    Returns:
    None: Modifies file in place or saves to new_file

    Raises:
    ValueError: If exif data is not valid EXIF data
    InvalidImageDataError: If image format is unsupported
    """

EXIF Data Removal

Remove all EXIF metadata from image files while preserving image quality.

def remove(src, new_file=None):
    """
    Remove EXIF data from JPEG or WebP image.

    Parameters:
    - src: str (file path) or bytes (image data) - Source image
    - new_file: str, optional - Output file path; if None, modifies original

    Returns:
    None: Modifies file in place or saves to new_file

    Raises:
    InvalidImageDataError: If image format is unsupported
    """

EXIF Data Transplantation

Copy EXIF metadata from one JPEG image to another, useful for preserving metadata during image processing.

def transplant(exif_src, image, new_file=None):
    """
    Copy EXIF data from one JPEG to another JPEG.

    Parameters:
    - exif_src: str (file path) or bytes (JPEG data) - Source JPEG with EXIF
    - image: str (file path) or bytes (JPEG data) - Target JPEG image
    - new_file: str, optional - Output file path; if None, modifies target

    Returns:
    None: Modifies file in place or saves to new_file

    Raises:
    ValueError: If source has no EXIF data or new_file not provided for bytes input
    InvalidImageDataError: If image format is not JPEG

    Note: Only supports JPEG format, not WebP or TIFF.
    """

EXIF Constants and Tag References

Data Types

class TYPES:
    """EXIF data type constants."""
    Byte = 1
    Ascii = 2
    Short = 3
    Long = 4
    Rational = 5
    SByte = 6
    Undefined = 7
    SShort = 8
    SLong = 9
    SRational = 10
    Float = 11
    DFloat = 12

Tag Dictionary

TAGS: dict
"""
Tag information dictionary mapping IFD names to tag definitions.

Structure:
{
    "0th": dict,     # Main image tags (same as "Image")
    "1st": dict,     # Thumbnail image tags (same as "Image")
    "Exif": dict,    # Camera-specific tags
    "GPS": dict,     # GPS location tags
    "Interop": dict, # Interoperability tags
    "Image": dict    # Standard TIFF/image tags
}

Each tag entry contains:
{
    tag_number: {
        "name": str,      # Human-readable tag name
        "type": int       # EXIF data type (from TYPES class)
    }
}
"""

Image IFD Tags

class ImageIFD:
    """Tag constants for main image metadata (0th and 1st IFD)."""
    # Core image properties
    ImageWidth = 256
    ImageLength = 257
    BitsPerSample = 258
    Compression = 259
    PhotometricInterpretation = 262
    Orientation = 274
    SamplesPerPixel = 277
    XResolution = 282
    YResolution = 283
    ResolutionUnit = 296
    
    # Image description
    ImageDescription = 270
    Make = 271
    Model = 272
    Software = 305
    DateTime = 306
    Artist = 315
    Copyright = 33432
    
    # Technical metadata
    WhitePoint = 318
    PrimaryChromaticities = 319
    YCbCrCoefficients = 529
    YCbCrSubSampling = 530
    YCbCrPositioning = 531
    ReferenceBlackWhite = 532
    
    # Pointers to other IFDs
    ExifTag = 34665      # Pointer to Exif IFD
    GPSTag = 34853       # Pointer to GPS IFD
    
    # Additional core tags
    ProcessingSoftware = 11
    NewSubfileType = 254
    SubfileType = 255
    Threshholding = 263
    CellWidth = 264
    CellLength = 265
    FillOrder = 266
    DocumentName = 269
    StripOffsets = 273
    RowsPerStrip = 278
    StripByteCounts = 279
    PlanarConfiguration = 284
    GrayResponseUnit = 290
    GrayResponseCurve = 291
    T4Options = 292
    T6Options = 293
    TransferFunction = 301
    HostComputer = 316
    Predictor = 317
    ColorMap = 320
    HalftoneHints = 321
    TileWidth = 322
    TileLength = 323
    TileOffsets = 324
    TileByteCounts = 325
    SubIFDs = 330
    InkSet = 332
    InkNames = 333
    NumberOfInks = 334
    DotRange = 336
    TargetPrinter = 337
    ExtraSamples = 338
    SampleFormat = 339
    SMinSampleValue = 340
    SMaxSampleValue = 341
    TransferRange = 342
    ClipPath = 343
    XClipPathUnits = 344
    YClipPathUnits = 345
    Indexed = 346
    JPEGTables = 347
    OPIProxy = 351
    
    # JPEG-specific tags
    JPEGProc = 512
    JPEGInterchangeFormat = 513
    JPEGInterchangeFormatLength = 514
    JPEGRestartInterval = 515
    JPEGLosslessPredictors = 517
    JPEGPointTransforms = 518
    JPEGQTables = 519
    JPEGDCTables = 520
    JPEGACTables = 521
    
    # Metadata and extensions
    XMLPacket = 700
    Rating = 18246
    RatingPercent = 18249
    ImageID = 32781
    CFARepeatPatternDim = 33421
    CFAPattern = 33422
    BatteryLevel = 33423
    ImageResources = 34377

Exif IFD Tags

class ExifIFD:
    """Tag constants for camera-specific metadata (Exif IFD)."""
    # Exposure settings
    ExposureTime = 33434
    FNumber = 33437
    ExposureProgram = 34850
    ISOSpeedRatings = 34855
    ShutterSpeedValue = 37377
    ApertureValue = 37378
    BrightnessValue = 37379
    ExposureBiasValue = 37380
    MaxApertureValue = 37381
    
    # Date and time
    ExifVersion = 36864
    DateTimeOriginal = 36867
    DateTimeDigitized = 36868
    OffsetTime = 36880
    OffsetTimeOriginal = 36881
    OffsetTimeDigitized = 36882
    SubSecTime = 37520
    SubSecTimeOriginal = 37521
    SubSecTimeDigitized = 37522
    
    # Camera settings
    MeteringMode = 37383
    LightSource = 37384
    Flash = 37385
    FocalLength = 37386
    SubjectDistance = 37382
    SubjectArea = 37396
    
    # Image properties
    ColorSpace = 40961
    PixelXDimension = 40962
    PixelYDimension = 40963
    ComponentsConfiguration = 37121
    CompressedBitsPerPixel = 37122
    
    # Additional metadata
    UserComment = 37510
    MakerNote = 37500
    FlashpixVersion = 40960
    RelatedSoundFile = 40964
    
    # Advanced technical settings
    SpectralSensitivity = 34852
    OECF = 34856
    SensitivityType = 34864
    StandardOutputSensitivity = 34865
    RecommendedExposureIndex = 34866
    ISOSpeed = 34867
    ISOSpeedLatitudeyyy = 34868
    ISOSpeedLatitudezzz = 34869
    Temperature = 37888
    Humidity = 37889
    Pressure = 37890
    WaterDepth = 37891
    Acceleration = 37892
    CameraElevationAngle = 37893
    
    # Image capture details
    FlashEnergy = 41483
    SpatialFrequencyResponse = 41484
    FocalPlaneXResolution = 41486
    FocalPlaneYResolution = 41487
    FocalPlaneResolutionUnit = 41488
    SubjectLocation = 41492
    ExposureIndex = 41493
    SensingMethod = 41495
    FileSource = 41728
    SceneType = 41729
    CFAPattern = 41730
    
    # Processing and quality
    CustomRendered = 41985
    ExposureMode = 41986
    WhiteBalance = 41987
    DigitalZoomRatio = 41988
    FocalLengthIn35mmFilm = 41989
    SceneCaptureType = 41990
    GainControl = 41991
    Contrast = 41992
    Saturation = 41993
    Sharpness = 41994
    DeviceSettingDescription = 41995
    SubjectDistanceRange = 41996
    ImageUniqueID = 42016
    
    # Camera and lens identification
    CameraOwnerName = 42032
    BodySerialNumber = 42033
    LensSpecification = 42034
    LensMake = 42035
    LensModel = 42036
    LensSerialNumber = 42037
    Gamma = 42240
    
    # Interoperability pointer
    InteroperabilityTag = 40965

GPS IFD Tags

class GPSIFD:
    """Tag constants for GPS location metadata (GPS IFD)."""
    GPSVersionID = 0
    GPSLatitudeRef = 1       # 'N' or 'S'
    GPSLatitude = 2          # Degrees, minutes, seconds
    GPSLongitudeRef = 3      # 'E' or 'W'
    GPSLongitude = 4         # Degrees, minutes, seconds
    GPSAltitudeRef = 5       # Above/below sea level
    GPSAltitude = 6          # Altitude in meters
    GPSTimeStamp = 7         # UTC time as hours, minutes, seconds
    GPSSatellites = 8        # Satellites used for measurement
    GPSStatus = 9            # Receiver status
    GPSMeasureMode = 10      # Measurement mode
    GPSDOP = 11             # Measurement precision
    GPSSpeedRef = 12        # Speed unit
    GPSSpeed = 13           # Speed of GPS receiver
    GPSTrackRef = 14        # Reference for direction of movement
    GPSTrack = 15           # Direction of movement
    GPSImgDirectionRef = 16 # Reference for direction of image
    GPSImgDirection = 17    # Direction of image when captured
    GPSMapDatum = 18        # Geodetic survey data
    GPSDestLatitudeRef = 19 # Reference for destination latitude
    GPSDestLatitude = 20    # Destination latitude
    GPSDestLongitudeRef = 21 # Reference for destination longitude
    GPSDestLongitude = 22   # Destination longitude
    GPSDestBearingRef = 23  # Reference for destination bearing
    GPSDestBearing = 24     # Destination bearing
    GPSDestDistanceRef = 25 # Reference for destination distance
    GPSDestDistance = 26    # Destination distance
    GPSProcessingMethod = 27 # GPS processing method
    GPSAreaInformation = 28 # GPS area information
    GPSDateStamp = 29       # GPS date
    GPSDifferential = 30    # Differential correction
    GPSHPositioningError = 31 # Horizontal positioning error

Interoperability IFD Tags

class InteropIFD:
    """Tag constants for interoperability metadata (Interop IFD)."""
    InteroperabilityIndex = 1

Helper Utilities

UserComment Encoding

Utility class for handling the UserComment EXIF field, which requires special encoding.

class UserComment:
    """Helper for UserComment EXIF field encoding/decoding."""
    
    # Supported encodings
    ASCII = 'ascii'
    JIS = 'jis'
    UNICODE = 'unicode'
    ENCODINGS = (ASCII, JIS, UNICODE)
    
    @classmethod
    def load(cls, data):
        """
        Convert UserComment EXIF field to string.
        
        Parameters:
        - data: bytes - UserComment field data from EXIF
        
        Returns:
        str: Decoded comment text
        
        Raises:
        ValueError: If data is invalid or encoding unsupported
        """
    
    @classmethod
    def dump(cls, data, encoding="ascii"):
        """
        Convert string to UserComment EXIF field format.
        
        Parameters:
        - data: str - Comment text to encode
        - encoding: str - Encoding to use ('ascii', 'jis', 'unicode')
        
        Returns:
        bytes: Encoded UserComment field data
        
        Raises:
        ValueError: If encoding is unsupported
        """

Exception Types

class InvalidImageDataError(ValueError):
    """Raised when image data is corrupted or in unsupported format."""
    pass

Usage Examples

Basic EXIF Manipulation

import piexif

# Load and examine EXIF data
exif_dict = piexif.load("photo.jpg")
print(f"Camera: {exif_dict['0th'].get(piexif.ImageIFD.Make, b'Unknown').decode()}")
print(f"Date: {exif_dict['Exif'].get(piexif.ExifIFD.DateTimeOriginal, b'Unknown').decode()}")

# Modify and save EXIF data
exif_dict["0th"][piexif.ImageIFD.Software] = b"Python Piexif"
exif_bytes = piexif.dump(exif_dict)
piexif.insert(exif_bytes, "photo.jpg", "photo_modified.jpg")

Working with GPS Data

import piexif

# Add GPS coordinates to an image
exif_dict = piexif.load("photo.jpg")

# GPS coordinates for New York City (40.7128° N, 74.0060° W)
exif_dict["GPS"] = {
    piexif.GPSIFD.GPSVersionID: (2, 0, 0, 0),
    piexif.GPSIFD.GPSLatitudeRef: b'N',
    piexif.GPSIFD.GPSLatitude: ((40, 1), (42, 1), (46, 1)),  # 40°42'46"
    piexif.GPSIFD.GPSLongitudeRef: b'W',
    piexif.GPSIFD.GPSLongitude: ((74, 1), (0, 1), (22, 1)),  # 74°0'22"
}

exif_bytes = piexif.dump(exif_dict)
piexif.insert(exif_bytes, "photo.jpg", "photo_with_gps.jpg")

UserComment Handling

import piexif
from piexif.helper import UserComment

# Read UserComment from EXIF
exif_dict = piexif.load("photo.jpg")
user_comment_bytes = exif_dict["Exif"].get(piexif.ExifIFD.UserComment)
if user_comment_bytes:
    comment = UserComment.load(user_comment_bytes)
    print(f"User comment: {comment}")

# Set UserComment in EXIF
new_comment = "Processed with Python"
exif_dict["Exif"][piexif.ExifIFD.UserComment] = UserComment.dump(new_comment, "unicode")
exif_bytes = piexif.dump(exif_dict)
piexif.insert(exif_bytes, "photo.jpg", "photo_with_comment.jpg")

Working with PIL/Pillow

from PIL import Image
import piexif

# Load image with PIL and extract EXIF
im = Image.open("photo.jpg")
if "exif" in im.info:
    exif_dict = piexif.load(im.info["exif"])
    
    # Modify EXIF data
    w, h = im.size
    exif_dict["0th"][piexif.ImageIFD.XResolution] = (w, 1)
    exif_dict["0th"][piexif.ImageIFD.YResolution] = (h, 1)
    
    # Save with modified EXIF
    exif_bytes = piexif.dump(exif_dict)
    im.save("photo_resized.jpg", "jpeg", exif=exif_bytes)