or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

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

0

# Piexif

1

2

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.

3

4

## Package Information

5

6

- **Package Name**: piexif

7

- **Package Type**: pypi

8

- **Language**: Python

9

- **Installation**: `pip install piexif`

10

- **Supported Python Versions**: 2.7, 3.5+, PyPy, IronPython

11

- **Supported Formats**: JPEG (all functions), TIFF (load only), WebP (load, dump, insert, remove)

12

13

## Core Imports

14

15

```python

16

import piexif

17

```

18

19

For specific functionality:

20

21

```python

22

from piexif import load, dump, insert, remove, transplant

23

from piexif import ImageIFD, ExifIFD, GPSIFD, InteropIFD, TYPES, TAGS

24

from piexif.helper import UserComment

25

```

26

27

## Basic Usage

28

29

```python

30

import piexif

31

32

# Load EXIF data from an image

33

exif_dict = piexif.load("image.jpg")

34

35

# Examine EXIF data structure

36

for ifd in ("0th", "Exif", "GPS", "1st"):

37

for tag in exif_dict[ifd]:

38

tag_name = piexif.TAGS[ifd][tag]["name"]

39

print(f"{tag_name}: {exif_dict[ifd][tag]}")

40

41

# Modify EXIF data

42

exif_dict["0th"][piexif.ImageIFD.Software] = b"My Software"

43

exif_dict["Exif"][piexif.ExifIFD.DateTimeOriginal] = b"2023:12:25 10:30:00"

44

45

# Convert back to bytes and insert into a new image

46

exif_bytes = piexif.dump(exif_dict)

47

piexif.insert(exif_bytes, "input.jpg", "output.jpg")

48

```

49

50

## Architecture

51

52

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:

53

54

- **0th IFD (Main Image)**: Primary image metadata using ImageIFD tags (dimensions, orientation, camera make/model, etc.)

55

- **Exif IFD**: Camera-specific settings using ExifIFD tags (exposure, ISO, focal length, date/time, etc.)

56

- **GPS IFD**: Location metadata using GPSIFD tags (coordinates, altitude, direction, etc.)

57

- **Interop IFD**: Interoperability data using InteropIFD tags (compatibility information)

58

- **1st IFD (Thumbnail)**: Thumbnail image metadata using ImageIFD tags

59

- **Thumbnail Data**: Embedded JPEG thumbnail image as raw bytes

60

61

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

62

63

## Capabilities

64

65

### EXIF Data Loading

66

67

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

68

69

```python { .api }

70

def load(input_data, key_is_name=False):

71

"""

72

Load EXIF data from JPEG, TIFF, or WebP files.

73

74

Parameters:

75

- input_data: str (file path) or bytes (image data)

76

- key_is_name: bool, optional - Use tag names as keys instead of tag numbers (default: False)

77

78

Returns:

79

dict: EXIF data with IFD structure

80

{

81

"0th": dict, # Main image metadata (ImageIFD tags)

82

"Exif": dict, # Camera-specific metadata (ExifIFD tags)

83

"GPS": dict, # GPS location data (GPSIFD tags)

84

"Interop": dict, # Interoperability data (InteropIFD tags)

85

"1st": dict, # Thumbnail image metadata (ImageIFD tags)

86

"thumbnail": bytes # Thumbnail JPEG data or None

87

}

88

89

Raises:

90

InvalidImageDataError: If image data is corrupted or invalid

91

"""

92

```

93

94

### EXIF Data Serialization

95

96

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

97

98

```python { .api }

99

def dump(exif_dict):

100

"""

101

Convert EXIF dictionary to bytes format.

102

103

Parameters:

104

- exif_dict: dict - EXIF data dictionary with IFD structure

105

106

Returns:

107

bytes: EXIF data in binary format with proper headers

108

109

Raises:

110

ValueError: If EXIF dictionary structure is invalid

111

"""

112

```

113

114

### EXIF Data Insertion

115

116

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

117

118

```python { .api }

119

def insert(exif, image, new_file=None):

120

"""

121

Insert EXIF data into JPEG or WebP image.

122

123

Parameters:

124

- exif: bytes - EXIF data in binary format (from dump())

125

- image: str (file path) or bytes (image data) - Target image

126

- new_file: str, optional - Output file path; if None, modifies original

127

128

Returns:

129

None: Modifies file in place or saves to new_file

130

131

Raises:

132

ValueError: If exif data is not valid EXIF data

133

InvalidImageDataError: If image format is unsupported

134

"""

135

```

136

137

### EXIF Data Removal

138

139

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

140

141

```python { .api }

142

def remove(src, new_file=None):

143

"""

144

Remove EXIF data from JPEG or WebP image.

145

146

Parameters:

147

- src: str (file path) or bytes (image data) - Source image

148

- new_file: str, optional - Output file path; if None, modifies original

149

150

Returns:

151

None: Modifies file in place or saves to new_file

152

153

Raises:

154

InvalidImageDataError: If image format is unsupported

155

"""

156

```

157

158

### EXIF Data Transplantation

159

160

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

161

162

```python { .api }

163

def transplant(exif_src, image, new_file=None):

164

"""

165

Copy EXIF data from one JPEG to another JPEG.

166

167

Parameters:

168

- exif_src: str (file path) or bytes (JPEG data) - Source JPEG with EXIF

169

- image: str (file path) or bytes (JPEG data) - Target JPEG image

170

- new_file: str, optional - Output file path; if None, modifies target

171

172

Returns:

173

None: Modifies file in place or saves to new_file

174

175

Raises:

176

ValueError: If source has no EXIF data or new_file not provided for bytes input

177

InvalidImageDataError: If image format is not JPEG

178

179

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

180

"""

181

```

182

183

## EXIF Constants and Tag References

184

185

### Data Types

186

187

```python { .api }

188

class TYPES:

189

"""EXIF data type constants."""

190

Byte = 1

191

Ascii = 2

192

Short = 3

193

Long = 4

194

Rational = 5

195

SByte = 6

196

Undefined = 7

197

SShort = 8

198

SLong = 9

199

SRational = 10

200

Float = 11

201

DFloat = 12

202

```

203

204

### Tag Dictionary

205

206

```python { .api }

207

TAGS: dict

208

"""

209

Tag information dictionary mapping IFD names to tag definitions.

210

211

Structure:

212

{

213

"0th": dict, # Main image tags (same as "Image")

214

"1st": dict, # Thumbnail image tags (same as "Image")

215

"Exif": dict, # Camera-specific tags

216

"GPS": dict, # GPS location tags

217

"Interop": dict, # Interoperability tags

218

"Image": dict # Standard TIFF/image tags

219

}

220

221

Each tag entry contains:

222

{

223

tag_number: {

224

"name": str, # Human-readable tag name

225

"type": int # EXIF data type (from TYPES class)

226

}

227

}

228

"""

229

```

230

231

### Image IFD Tags

232

233

```python { .api }

234

class ImageIFD:

235

"""Tag constants for main image metadata (0th and 1st IFD)."""

236

# Core image properties

237

ImageWidth = 256

238

ImageLength = 257

239

BitsPerSample = 258

240

Compression = 259

241

PhotometricInterpretation = 262

242

Orientation = 274

243

SamplesPerPixel = 277

244

XResolution = 282

245

YResolution = 283

246

ResolutionUnit = 296

247

248

# Image description

249

ImageDescription = 270

250

Make = 271

251

Model = 272

252

Software = 305

253

DateTime = 306

254

Artist = 315

255

Copyright = 33432

256

257

# Technical metadata

258

WhitePoint = 318

259

PrimaryChromaticities = 319

260

YCbCrCoefficients = 529

261

YCbCrSubSampling = 530

262

YCbCrPositioning = 531

263

ReferenceBlackWhite = 532

264

265

# Pointers to other IFDs

266

ExifTag = 34665 # Pointer to Exif IFD

267

GPSTag = 34853 # Pointer to GPS IFD

268

269

# Additional core tags

270

ProcessingSoftware = 11

271

NewSubfileType = 254

272

SubfileType = 255

273

Threshholding = 263

274

CellWidth = 264

275

CellLength = 265

276

FillOrder = 266

277

DocumentName = 269

278

StripOffsets = 273

279

RowsPerStrip = 278

280

StripByteCounts = 279

281

PlanarConfiguration = 284

282

GrayResponseUnit = 290

283

GrayResponseCurve = 291

284

T4Options = 292

285

T6Options = 293

286

TransferFunction = 301

287

HostComputer = 316

288

Predictor = 317

289

ColorMap = 320

290

HalftoneHints = 321

291

TileWidth = 322

292

TileLength = 323

293

TileOffsets = 324

294

TileByteCounts = 325

295

SubIFDs = 330

296

InkSet = 332

297

InkNames = 333

298

NumberOfInks = 334

299

DotRange = 336

300

TargetPrinter = 337

301

ExtraSamples = 338

302

SampleFormat = 339

303

SMinSampleValue = 340

304

SMaxSampleValue = 341

305

TransferRange = 342

306

ClipPath = 343

307

XClipPathUnits = 344

308

YClipPathUnits = 345

309

Indexed = 346

310

JPEGTables = 347

311

OPIProxy = 351

312

313

# JPEG-specific tags

314

JPEGProc = 512

315

JPEGInterchangeFormat = 513

316

JPEGInterchangeFormatLength = 514

317

JPEGRestartInterval = 515

318

JPEGLosslessPredictors = 517

319

JPEGPointTransforms = 518

320

JPEGQTables = 519

321

JPEGDCTables = 520

322

JPEGACTables = 521

323

324

# Metadata and extensions

325

XMLPacket = 700

326

Rating = 18246

327

RatingPercent = 18249

328

ImageID = 32781

329

CFARepeatPatternDim = 33421

330

CFAPattern = 33422

331

BatteryLevel = 33423

332

ImageResources = 34377

333

```

334

335

### Exif IFD Tags

336

337

```python { .api }

338

class ExifIFD:

339

"""Tag constants for camera-specific metadata (Exif IFD)."""

340

# Exposure settings

341

ExposureTime = 33434

342

FNumber = 33437

343

ExposureProgram = 34850

344

ISOSpeedRatings = 34855

345

ShutterSpeedValue = 37377

346

ApertureValue = 37378

347

BrightnessValue = 37379

348

ExposureBiasValue = 37380

349

MaxApertureValue = 37381

350

351

# Date and time

352

ExifVersion = 36864

353

DateTimeOriginal = 36867

354

DateTimeDigitized = 36868

355

OffsetTime = 36880

356

OffsetTimeOriginal = 36881

357

OffsetTimeDigitized = 36882

358

SubSecTime = 37520

359

SubSecTimeOriginal = 37521

360

SubSecTimeDigitized = 37522

361

362

# Camera settings

363

MeteringMode = 37383

364

LightSource = 37384

365

Flash = 37385

366

FocalLength = 37386

367

SubjectDistance = 37382

368

SubjectArea = 37396

369

370

# Image properties

371

ColorSpace = 40961

372

PixelXDimension = 40962

373

PixelYDimension = 40963

374

ComponentsConfiguration = 37121

375

CompressedBitsPerPixel = 37122

376

377

# Additional metadata

378

UserComment = 37510

379

MakerNote = 37500

380

FlashpixVersion = 40960

381

RelatedSoundFile = 40964

382

383

# Advanced technical settings

384

SpectralSensitivity = 34852

385

OECF = 34856

386

SensitivityType = 34864

387

StandardOutputSensitivity = 34865

388

RecommendedExposureIndex = 34866

389

ISOSpeed = 34867

390

ISOSpeedLatitudeyyy = 34868

391

ISOSpeedLatitudezzz = 34869

392

Temperature = 37888

393

Humidity = 37889

394

Pressure = 37890

395

WaterDepth = 37891

396

Acceleration = 37892

397

CameraElevationAngle = 37893

398

399

# Image capture details

400

FlashEnergy = 41483

401

SpatialFrequencyResponse = 41484

402

FocalPlaneXResolution = 41486

403

FocalPlaneYResolution = 41487

404

FocalPlaneResolutionUnit = 41488

405

SubjectLocation = 41492

406

ExposureIndex = 41493

407

SensingMethod = 41495

408

FileSource = 41728

409

SceneType = 41729

410

CFAPattern = 41730

411

412

# Processing and quality

413

CustomRendered = 41985

414

ExposureMode = 41986

415

WhiteBalance = 41987

416

DigitalZoomRatio = 41988

417

FocalLengthIn35mmFilm = 41989

418

SceneCaptureType = 41990

419

GainControl = 41991

420

Contrast = 41992

421

Saturation = 41993

422

Sharpness = 41994

423

DeviceSettingDescription = 41995

424

SubjectDistanceRange = 41996

425

ImageUniqueID = 42016

426

427

# Camera and lens identification

428

CameraOwnerName = 42032

429

BodySerialNumber = 42033

430

LensSpecification = 42034

431

LensMake = 42035

432

LensModel = 42036

433

LensSerialNumber = 42037

434

Gamma = 42240

435

436

# Interoperability pointer

437

InteroperabilityTag = 40965

438

```

439

440

### GPS IFD Tags

441

442

```python { .api }

443

class GPSIFD:

444

"""Tag constants for GPS location metadata (GPS IFD)."""

445

GPSVersionID = 0

446

GPSLatitudeRef = 1 # 'N' or 'S'

447

GPSLatitude = 2 # Degrees, minutes, seconds

448

GPSLongitudeRef = 3 # 'E' or 'W'

449

GPSLongitude = 4 # Degrees, minutes, seconds

450

GPSAltitudeRef = 5 # Above/below sea level

451

GPSAltitude = 6 # Altitude in meters

452

GPSTimeStamp = 7 # UTC time as hours, minutes, seconds

453

GPSSatellites = 8 # Satellites used for measurement

454

GPSStatus = 9 # Receiver status

455

GPSMeasureMode = 10 # Measurement mode

456

GPSDOP = 11 # Measurement precision

457

GPSSpeedRef = 12 # Speed unit

458

GPSSpeed = 13 # Speed of GPS receiver

459

GPSTrackRef = 14 # Reference for direction of movement

460

GPSTrack = 15 # Direction of movement

461

GPSImgDirectionRef = 16 # Reference for direction of image

462

GPSImgDirection = 17 # Direction of image when captured

463

GPSMapDatum = 18 # Geodetic survey data

464

GPSDestLatitudeRef = 19 # Reference for destination latitude

465

GPSDestLatitude = 20 # Destination latitude

466

GPSDestLongitudeRef = 21 # Reference for destination longitude

467

GPSDestLongitude = 22 # Destination longitude

468

GPSDestBearingRef = 23 # Reference for destination bearing

469

GPSDestBearing = 24 # Destination bearing

470

GPSDestDistanceRef = 25 # Reference for destination distance

471

GPSDestDistance = 26 # Destination distance

472

GPSProcessingMethod = 27 # GPS processing method

473

GPSAreaInformation = 28 # GPS area information

474

GPSDateStamp = 29 # GPS date

475

GPSDifferential = 30 # Differential correction

476

GPSHPositioningError = 31 # Horizontal positioning error

477

```

478

479

### Interoperability IFD Tags

480

481

```python { .api }

482

class InteropIFD:

483

"""Tag constants for interoperability metadata (Interop IFD)."""

484

InteroperabilityIndex = 1

485

```

486

487

## Helper Utilities

488

489

### UserComment Encoding

490

491

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

492

493

```python { .api }

494

class UserComment:

495

"""Helper for UserComment EXIF field encoding/decoding."""

496

497

# Supported encodings

498

ASCII = 'ascii'

499

JIS = 'jis'

500

UNICODE = 'unicode'

501

ENCODINGS = (ASCII, JIS, UNICODE)

502

503

@classmethod

504

def load(cls, data):

505

"""

506

Convert UserComment EXIF field to string.

507

508

Parameters:

509

- data: bytes - UserComment field data from EXIF

510

511

Returns:

512

str: Decoded comment text

513

514

Raises:

515

ValueError: If data is invalid or encoding unsupported

516

"""

517

518

@classmethod

519

def dump(cls, data, encoding="ascii"):

520

"""

521

Convert string to UserComment EXIF field format.

522

523

Parameters:

524

- data: str - Comment text to encode

525

- encoding: str - Encoding to use ('ascii', 'jis', 'unicode')

526

527

Returns:

528

bytes: Encoded UserComment field data

529

530

Raises:

531

ValueError: If encoding is unsupported

532

"""

533

```

534

535

## Exception Types

536

537

```python { .api }

538

class InvalidImageDataError(ValueError):

539

"""Raised when image data is corrupted or in unsupported format."""

540

pass

541

```

542

543

## Usage Examples

544

545

### Basic EXIF Manipulation

546

547

```python

548

import piexif

549

550

# Load and examine EXIF data

551

exif_dict = piexif.load("photo.jpg")

552

print(f"Camera: {exif_dict['0th'].get(piexif.ImageIFD.Make, b'Unknown').decode()}")

553

print(f"Date: {exif_dict['Exif'].get(piexif.ExifIFD.DateTimeOriginal, b'Unknown').decode()}")

554

555

# Modify and save EXIF data

556

exif_dict["0th"][piexif.ImageIFD.Software] = b"Python Piexif"

557

exif_bytes = piexif.dump(exif_dict)

558

piexif.insert(exif_bytes, "photo.jpg", "photo_modified.jpg")

559

```

560

561

### Working with GPS Data

562

563

```python

564

import piexif

565

566

# Add GPS coordinates to an image

567

exif_dict = piexif.load("photo.jpg")

568

569

# GPS coordinates for New York City (40.7128° N, 74.0060° W)

570

exif_dict["GPS"] = {

571

piexif.GPSIFD.GPSVersionID: (2, 0, 0, 0),

572

piexif.GPSIFD.GPSLatitudeRef: b'N',

573

piexif.GPSIFD.GPSLatitude: ((40, 1), (42, 1), (46, 1)), # 40°42'46"

574

piexif.GPSIFD.GPSLongitudeRef: b'W',

575

piexif.GPSIFD.GPSLongitude: ((74, 1), (0, 1), (22, 1)), # 74°0'22"

576

}

577

578

exif_bytes = piexif.dump(exif_dict)

579

piexif.insert(exif_bytes, "photo.jpg", "photo_with_gps.jpg")

580

```

581

582

### UserComment Handling

583

584

```python

585

import piexif

586

from piexif.helper import UserComment

587

588

# Read UserComment from EXIF

589

exif_dict = piexif.load("photo.jpg")

590

user_comment_bytes = exif_dict["Exif"].get(piexif.ExifIFD.UserComment)

591

if user_comment_bytes:

592

comment = UserComment.load(user_comment_bytes)

593

print(f"User comment: {comment}")

594

595

# Set UserComment in EXIF

596

new_comment = "Processed with Python"

597

exif_dict["Exif"][piexif.ExifIFD.UserComment] = UserComment.dump(new_comment, "unicode")

598

exif_bytes = piexif.dump(exif_dict)

599

piexif.insert(exif_bytes, "photo.jpg", "photo_with_comment.jpg")

600

```

601

602

### Working with PIL/Pillow

603

604

```python

605

from PIL import Image

606

import piexif

607

608

# Load image with PIL and extract EXIF

609

im = Image.open("photo.jpg")

610

if "exif" in im.info:

611

exif_dict = piexif.load(im.info["exif"])

612

613

# Modify EXIF data

614

w, h = im.size

615

exif_dict["0th"][piexif.ImageIFD.XResolution] = (w, 1)

616

exif_dict["0th"][piexif.ImageIFD.YResolution] = (h, 1)

617

618

# Save with modified EXIF

619

exif_bytes = piexif.dump(exif_dict)

620

im.save("photo_resized.jpg", "jpeg", exif=exif_bytes)

621

```