or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

tessl/pypi-exifread

Easy to use Python module to extract Exif metadata from digital image files

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
pypipkg:pypi/exifread@3.5.x

To install, run

npx @tessl/cli install tessl/pypi-exifread@3.5.0

0

# ExifRead

1

2

Easy to use Python module to extract Exif metadata from digital image files. Supports multiple image formats including TIFF, JPEG, JPEG XL, PNG, Webp, HEIC, and RAW files with zero external dependencies.

3

4

## Package Information

5

6

- **Package Name**: ExifRead

7

- **Language**: Python

8

- **Installation**: `pip install exifread`

9

- **Supported Python**: 3.7 to 3.13

10

- **License**: BSD-3-Clause

11

- **Dependencies**: None (pure Python)

12

13

## Core Imports

14

15

```python

16

import exifread

17

```

18

19

For specific components:

20

21

```python

22

from exifread import process_file

23

from exifread.core.exceptions import ExifError, InvalidExif, ExifNotFound

24

from exifread.core.ifd_tag import IfdTag

25

from exifread.utils import get_gps_coords, Ratio

26

from exifread.serialize import convert_types

27

```

28

29

## Architecture

30

31

ExifRead uses a multi-layered architecture for EXIF metadata extraction:

32

33

1. **File Format Detection**: Automatically identifies image format (JPEG, TIFF, PNG, WebP, HEIC, etc.) and locates EXIF data blocks within the file structure

34

2. **IFD Processing**: Parses Image File Directory (IFD) structures that contain organized metadata entries with tags, types, and values

35

3. **Tag Extraction**: Converts raw binary EXIF data into structured IfdTag objects with human-readable information and typed values

36

4. **Type Conversion**: Optionally converts IfdTag objects to standard Python types (int, float, str, list) for easier serialization and programmatic use

37

38

The core `process_file()` function orchestrates this pipeline with configurable options for performance optimization (quick mode, early termination) and output format control (builtin types, thumbnail extraction). The architecture supports both lenient processing (warnings for errors) and strict processing (exceptions on errors) to accommodate various use cases from casual metadata viewing to production data processing.

39

40

## Basic Usage

41

42

```python

43

import exifread

44

45

# Basic EXIF extraction

46

with open("image.jpg", "rb") as f:

47

tags = exifread.process_file(f)

48

49

# Iterate through tags (skip long/boring ones)

50

for tag, value in tags.items():

51

if tag not in ('JPEGThumbnail', 'TIFFThumbnail', 'Filename', 'EXIF MakerNote'):

52

print(f"Key: {tag}, value: {value}")

53

54

# Quick processing (skip MakerNotes and thumbnails)

55

with open("image.jpg", "rb") as f:

56

tags = exifread.process_file(f, details=False, extract_thumbnail=False)

57

58

# Convert to built-in Python types for JSON serialization

59

with open("image.jpg", "rb") as f:

60

tags = exifread.process_file(f, builtin_types=True)

61

import json

62

json_data = json.dumps(tags)

63

64

# Extract GPS coordinates

65

from exifread.utils import get_gps_coords

66

coords = get_gps_coords(tags) # Returns (latitude, longitude) or None

67

if coords:

68

lat, lng = coords

69

print(f"GPS: {lat}, {lng}")

70

```

71

72

## Capabilities

73

74

### EXIF Processing

75

76

Extract comprehensive EXIF metadata from digital image files with configurable processing options for performance optimization and data format conversion.

77

78

```python { .api }

79

def process_file(

80

fh,

81

stop_tag="UNDEF",

82

details=True,

83

strict=False,

84

debug=False,

85

truncate_tags=True,

86

auto_seek=True,

87

extract_thumbnail=True,

88

builtin_types=False

89

) -> dict:

90

"""

91

Process an image file to extract EXIF metadata.

92

93

Parameters:

94

- fh: Binary file handle opened in 'rb' mode

95

- stop_tag: Stop processing when this tag is retrieved (default: "UNDEF")

96

- details: If True, process MakerNotes for camera-specific metadata (default: True)

97

- strict: If True, raise exceptions on errors instead of logging warnings (default: False)

98

- debug: Output detailed debug information during processing (default: False)

99

- truncate_tags: If True, truncate printable tag output for readability (default: True)

100

- auto_seek: If True, automatically seek to start of file (default: True)

101

- extract_thumbnail: If True, extract JPEG thumbnail if present (default: True)

102

- builtin_types: If True, convert tags to standard Python types for serialization (default: False)

103

104

Returns:

105

Dictionary mapping tag names to values. Keys are formatted as "IFD_NAME TAG_NAME".

106

Values are IfdTag objects (builtin_types=False) or Python built-in types (builtin_types=True).

107

108

Raises:

109

- InvalidExif: When EXIF data is malformed (strict=True only)

110

- ExifNotFound: When no EXIF data is found (strict=True only)

111

"""

112

```

113

114

### GPS Coordinate Extraction

115

116

Extract latitude and longitude coordinates from EXIF GPS tags with automatic conversion to decimal degrees format.

117

118

```python { .api }

119

def get_gps_coords(tags: dict) -> tuple[float, float] | None:

120

"""

121

Extract GPS coordinates from EXIF tags.

122

123

Parameters:

124

- tags: Dictionary of EXIF tags from process_file()

125

126

Returns:

127

Tuple of (latitude, longitude) in decimal degrees, or None if GPS data not found.

128

Handles both IfdTag objects and serialized tags.

129

"""

130

```

131

132

### Data Serialization

133

134

Convert EXIF tag objects to standard Python types for easier programmatic use and JSON serialization.

135

136

```python { .api }

137

def convert_types(exif_tags: dict) -> dict:

138

"""

139

Convert Exif IfdTags to built-in Python types.

140

141

Parameters:

142

- exif_tags: Dictionary of IfdTag objects from process_file()

143

144

Returns:

145

Dictionary with same keys but values converted to int, float, str, bytes, list, or None.

146

Single-element lists are unpacked to individual values.

147

"""

148

```

149

150

### Command Line Interface

151

152

Process image files from the command line with various output and processing options.

153

154

```python { .api }

155

def main() -> None:

156

"""Main CLI entry point. Processes command line arguments and extracts EXIF data."""

157

158

def run_cli(args: argparse.Namespace) -> None:

159

"""

160

Execute CLI processing with parsed arguments.

161

162

Parameters:

163

- args: Parsed command line arguments from get_args()

164

"""

165

166

def get_args() -> argparse.Namespace:

167

"""

168

Parse command line arguments.

169

170

Returns:

171

argparse.Namespace with parsed CLI options

172

"""

173

```

174

175

**CLI Usage:**

176

```bash

177

# Basic usage

178

EXIF.py image.jpg

179

180

# Multiple files with quick processing and builtin types

181

EXIF.py -q -b image1.jpg image2.tiff

182

183

# Stop at specific tag with strict error handling

184

EXIF.py -t DateTimeOriginal -s image.jpg

185

186

# Debug mode with color output

187

EXIF.py -d -c image.jpg

188

189

# Module execution

190

python -m exifread image.jpg

191

```

192

193

**CLI Options:**

194

- `-v, --version`: Display version information

195

- `-q, --quick`: Skip MakerNotes and thumbnails for faster processing

196

- `-t TAG, --tag TAG`: Stop processing when specified tag is retrieved

197

- `-s, --strict`: Run in strict mode (raise exceptions on errors)

198

- `-b, --builtin`: Convert IfdTag values to built-in Python types

199

- `-d, --debug`: Run in debug mode with detailed information

200

- `-c, --color`: Enable colored output (POSIX systems only)

201

202

### Logging

203

204

Configure logging output with debug information and colored formatting for development and troubleshooting.

205

206

```python { .api }

207

def get_logger():

208

"""

209

Get the exifread logger instance.

210

211

Returns:

212

logging.Logger configured for exifread

213

"""

214

215

def setup_logger(debug: bool, color: bool) -> None:

216

"""

217

Configure logger with debug and color settings.

218

219

Parameters:

220

- debug: Enable debug level logging

221

- color: Enable colored console output

222

"""

223

```

224

225

## Data Types

226

227

### Tag Representation

228

229

```python { .api }

230

class IfdTag:

231

"""

232

Represents an IFD (Image File Directory) tag containing EXIF metadata.

233

234

Attributes:

235

- printable: Human-readable version of the tag data (str)

236

- tag: Numeric tag identifier (int)

237

- field_type: EXIF field type from FieldType enum

238

- values: Raw tag values (various types: str, bytes, list)

239

- field_offset: Byte offset of field start in IFD (int)

240

- field_length: Length of data field in bytes (int)

241

- prefer_printable: Whether to prefer printable over raw values for serialization (bool)

242

"""

243

244

def __init__(

245

self,

246

printable: str,

247

tag: int,

248

field_type: FieldType,

249

values,

250

field_offset: int,

251

field_length: int,

252

prefer_printable: bool = True

253

) -> None: ...

254

255

def __str__(self) -> str:

256

"""Return printable representation of tag."""

257

258

def __repr__(self) -> str:

259

"""Return detailed representation with field type and offset information."""

260

```

261

262

### Ratio Values

263

264

```python { .api }

265

class Ratio:

266

"""

267

Represents EXIF ratio values, extending fractions.Fraction with additional methods.

268

Used for GPS coordinates, exposure times, and other fractional EXIF data.

269

"""

270

271

def __new__(cls, numerator: int = 0, denominator: int | None = None): ...

272

273

@property

274

def num(self) -> int:

275

"""Get numerator value."""

276

277

@property

278

def den(self) -> int:

279

"""Get denominator value."""

280

281

def decimal(self) -> float:

282

"""Convert ratio to decimal floating point value."""

283

```

284

285

### Field Types

286

287

```python { .api }

288

class FieldType:

289

"""

290

EXIF field type enumeration defining data storage formats.

291

"""

292

PROPRIETARY = 0

293

BYTE = 1

294

ASCII = 2

295

SHORT = 3

296

LONG = 4

297

RATIO = 5

298

SIGNED_BYTE = 6

299

UNDEFINED = 7

300

SIGNED_SHORT = 8

301

SIGNED_LONG = 9

302

SIGNED_RATIO = 10

303

FLOAT_32 = 11

304

FLOAT_64 = 12

305

IFD = 13

306

```

307

308

### Exception Types

309

310

```python { .api }

311

class ExifError(Exception):

312

"""Base exception class for all ExifRead errors."""

313

314

class InvalidExif(ExifError):

315

"""Raised when EXIF data is malformed or corrupted."""

316

317

class ExifNotFound(ExifError):

318

"""Raised when no EXIF data is found in the image file."""

319

```

320

321

## Constants

322

323

```python { .api }

324

__version__: str = "3.5.1"

325

"""Package version string."""

326

327

DEFAULT_STOP_TAG: str = "UNDEF"

328

"""Default tag name to stop processing at."""

329

330

IGNORE_TAGS: list[int] = [0x02BC, 0x927C, 0x9286]

331

"""Tag IDs ignored during quick processing mode."""

332

333

FIELD_DEFINITIONS: dict

334

"""Maps FieldType values to (byte_length, description) tuples."""

335

```

336

337

## Usage Examples

338

339

### Processing Multiple Images

340

341

```python

342

import exifread

343

import os

344

from pathlib import Path

345

346

def process_directory(directory_path):

347

"""Process all JPEG images in a directory."""

348

image_extensions = {'.jpg', '.jpeg', '.tiff', '.tif'}

349

350

for file_path in Path(directory_path).iterdir():

351

if file_path.suffix.lower() in image_extensions:

352

try:

353

with open(file_path, 'rb') as f:

354

tags = exifread.process_file(f, builtin_types=True)

355

356

if tags:

357

print(f"\n{file_path.name}:")

358

# Print camera info

359

camera = tags.get('Image Make', 'Unknown')

360

model = tags.get('Image Model', 'Unknown')

361

print(f" Camera: {camera} {model}")

362

363

# Print capture date

364

date_taken = tags.get('EXIF DateTimeOriginal', 'Unknown')

365

print(f" Date: {date_taken}")

366

367

# Print GPS if available

368

from exifread.utils import get_gps_coords

369

coords = get_gps_coords(tags)

370

if coords:

371

lat, lng = coords

372

print(f" GPS: {lat:.6f}, {lng:.6f}")

373

374

except Exception as e:

375

print(f"Error processing {file_path.name}: {e}")

376

377

# Usage

378

process_directory("/path/to/photos")

379

```

380

381

### Custom Tag Processing

382

383

```python

384

import exifread

385

386

def extract_specific_tags(file_path, target_tags):

387

"""Extract only specific EXIF tags efficiently."""

388

389

with open(file_path, 'rb') as f:

390

# Use quick mode and stop early for efficiency

391

tags = exifread.process_file(

392

f,

393

details=False, # Skip MakerNotes

394

extract_thumbnail=False, # Skip thumbnails

395

builtin_types=True # Get Python types

396

)

397

398

result = {}

399

for tag_name in target_tags:

400

if tag_name in tags:

401

result[tag_name] = tags[tag_name]

402

403

return result

404

405

# Extract specific metadata

406

important_tags = [

407

'Image Make',

408

'Image Model',

409

'EXIF DateTimeOriginal',

410

'EXIF ExposureTime',

411

'EXIF FNumber',

412

'EXIF ISOSpeedRatings'

413

]

414

415

metadata = extract_specific_tags('photo.jpg', important_tags)

416

print(metadata)

417

```

418

419

### Image Orientation Correction

420

421

```python

422

import exifread

423

from PIL import Image

424

425

def correct_image_orientation(image_path):

426

"""Correct image orientation based on EXIF data."""

427

428

# Read EXIF orientation

429

with open(image_path, 'rb') as f:

430

tags = exifread.process_file(f, details=False)

431

432

# Open image

433

img = Image.open(image_path)

434

435

# Check for orientation tag

436

orientation = tags.get('Image Orientation')

437

if orientation:

438

orientation_value = orientation.values[0] if hasattr(orientation, 'values') else orientation

439

440

# Apply rotation based on orientation

441

if orientation_value == 3:

442

img = img.transpose(Image.ROTATE_180)

443

elif orientation_value == 6:

444

img = img.transpose(Image.ROTATE_270)

445

elif orientation_value == 8:

446

img = img.transpose(Image.ROTATE_90)

447

448

return img

449

450

# Usage

451

corrected_image = correct_image_orientation('photo.jpg')

452

corrected_image.save('corrected_photo.jpg')

453

```

454

455

## Error Handling

456

457

The library provides different error handling approaches based on the `strict` parameter:

458

459

**Lenient Mode (default):**

460

```python

461

# Warnings logged, empty dict returned on errors

462

with open('corrupted.jpg', 'rb') as f:

463

tags = exifread.process_file(f) # Returns {} if no EXIF found

464

```

465

466

**Strict Mode:**

467

```python

468

from exifread.core.exceptions import ExifNotFound, InvalidExif

469

470

try:

471

with open('image.jpg', 'rb') as f:

472

tags = exifread.process_file(f, strict=True)

473

except ExifNotFound:

474

print("No EXIF data found in image")

475

except InvalidExif:

476

print("EXIF data is corrupted or invalid")

477

except Exception as e:

478

print(f"Unexpected error: {e}")

479

```

480

481

## Supported Image Formats

482

483

- **TIFF**: Tagged Image File Format with full EXIF support

484

- **JPEG**: JPEG images with EXIF metadata blocks

485

- **JPEG XL**: Next-generation JPEG format

486

- **PNG**: Portable Network Graphics with EXIF chunks

487

- **WebP**: Web-optimized format with EXIF support

488

- **HEIC**: High Efficiency Image Container (Apple format)

489

- **RAW**: Various camera RAW formats with embedded EXIF

490

491

## Performance Considerations

492

493

**Quick Processing:** Use `details=False` and `extract_thumbnail=False` for faster processing:

494

```python

495

tags = exifread.process_file(f, details=False, extract_thumbnail=False)

496

```

497

498

**Early Termination:** Stop processing at specific tags:

499

```python

500

tags = exifread.process_file(f, stop_tag='DateTimeOriginal')

501

```

502

503

**Memory Efficiency:** Use built-in types to avoid keeping IfdTag objects:

504

```python

505

tags = exifread.process_file(f, builtin_types=True)

506

```