Skip to content

About

A Python script for fast, concurrent extraction of JPEG data from RAW files, preserving original quality. Simplify your RAW file workflow effortlessly.

Resources

Stars

5 stars

Watchers

1 watching

Forks

Latest commit

 

History

20 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

JPEG Extractor Tool

A cross-platform desktop application for fast, concurrent extraction of embedded JPEG data from RAW camera files and other binary media containers — preserving original quality.

Python PyQt6 License


Overview

JPEG Extractor Tool is a PyQt6 GUI application that scans RAW image files (and other binary media) for embedded JPEG streams, carves them out byte-for-byte, validates their integrity, and saves them as standalone .jpg files. It is designed for photographers and recovery engineers who need to pull usable JPEG previews or thumbnails out of RAW containers without decoding the full RAW data.

It works as a companion to the RAW Repair Tool and the JPEG Repair Tool.


Screenshots

Recovery dashboard Extraction in progress
Dashboard Progress

Replace the image paths above with the actual screenshot filenames in the repository.


Features

  • Broad RAW support — recognises .cr3, .cr2, .nef, .arw, .raf, .dng, .raw, .bin, .img files.
  • Drag & drop — drop a folder (or a file) straight onto the window to set the source.
  • Subfolder scanning — optional recursive walk of the source tree.
  • Discovered media table — lists every matching file with a thumbnail preview, file name, size, and live recovery status.
  • Live recovery view — the most recently carved JPEG is displayed in a large preview pane with source/target filenames.
  • Recovery statistics — running counters for files recovered, files failed, and total MB extracted.
  • Threaded worker — extraction runs on a QThread so the UI stays responsive.
  • Cancel support — abort a running batch at any time.
  • JPEG validation — every carved file is verified by magic-byte check (FF D8 header, FF D9 footer) and minimum size before being marked as recovered.
  • Export recovery report — save a session log as a .txt report.
  • Dark dashboard theme — modern #0f172a / #1e293b slate palette with blue accents.

Requirements

  • Python 3.6 or newer
  • PyQt6

Installation

1. Clone the repository

git clone https://github.com/DRCRecoveryData/JPEG-Extractor-Tool.git
cd JPEG-Extractor-Tool

2. Create a virtual environment (recommended)

Modern Linux distributions (Debian 12+, Ubuntu 23.04+, Fedora 38+) block system-wide pip install to protect the OS Python. You will see:

error: externally-managed-environment

The correct fix is a virtual environment, not --break-system-packages.

Linux / macOS

python3 -m venv .venv
source .venv/bin/activate

Windows (PowerShell)

python -m venv .venv
.venv\Scripts\Activate.ps1

Your shell prompt should now show (.venv) at the start.

Ubuntu note: if python3 -m venv fails, install the venv support package first:

sudo apt update && sudo apt install -y python3-full python3-venv

3. Install dependencies

pip install PyQt6

4. Run the application

python3 jpegextractor-gui.py

Usage

  1. Select the RAW source folder — click Browse next to RAW Source Folder, or drag & drop a folder onto the window. The discovered files populate the table immediately.
  2. Toggle subfolder scanning (optional) — tick Include Subfolders to scan recursively.
  3. Select the output folder — click Browse next to Extracted JPEGs Folder to choose where carved .jpg files will be saved.
  4. Click Start JPEG Extraction — the worker thread scans each file, carves the embedded JPEG, validates it, and writes it out.
  5. Monitor progress — the progress bar tracks completion, the table updates each row's status (Extracting… → Recovered / Corrupt / Failed), and the log console prints timestamped messages.
  6. Preview results — click any row to preview the carved JPEG; double-click to open it in the system viewer.
  7. Export the report — click Export Log Report to save a session summary as a text file.

To abort a running job, click Cancel. The current file finishes, then the worker stops.

Status meanings

Status Colour Meaning
Extracting… blue File is currently being scanned
Recovered green JPEG carved and passed validation
Corrupt amber JPEG carved but failed integrity check
Failed red No JPEG signature found in the file
(blank) grey Not yet processed

How It Works

JPEG carving

The extractor reads the entire RAW file into memory as a byte string and uses regular expressions to locate JPEG start markers:

pattern_1_exif = rb"\xFF\xD8\xFF\xE1.{2}Exif"   # EXIF-bearing JPEG
pattern_1_jfif = rb"\xFF\xD8\xFF\xE0.{2}JFIF"   # JFIF JPEG
pattern_2      = rb"\xFF\xD8\xFF\xDB.{4,8}"     # raw DQT-start JPEG

All matches are collected and sorted by offset. End markers (FF D9) are also located. The algorithm then iterates the start positions in reverse and picks the first one that has a matching end marker after it — effectively extracting the last, largest, most complete JPEG embedded in the file (typically the full-size preview rather than a thumbnail).

Integrity validation

Every carved file is checked before it is reported as recovered:

def validate_jpeg(file_path):
    size = os.path.getsize(file_path)
    if size < 1024:
        return False
    with open(file_path, 'rb') as f:
        header = f.read(2)
        f.seek(-2, 2)
        footer = f.read(2)
    return header == b'\xFF\xD8' and footer == b'\xFF\xD9'

A file must be at least 1 KB, start with FF D8, and end with FF D9 to be marked Recovered.

Threading model

Class Responsibility
JPEGExtractionWorker QThread subclass; loops over the file list, emits per-file signals
JPEGRepairApp QWidget dashboard holding the layout, stylesheet, table, and callbacks
extract_jpeg_from_raw() Standalone carving function (regex + reverse scan)
validate_jpeg() Binary integrity check

Signals emitted by the worker:

Signal Payload Purpose
progress_updated int Overall completion 0–100
log_updated str Console message
file_status_updated int, str Row index + new status text
jpeg_carved int, str, str Row index, source RAW path, carved JPEG path
extraction_finished str Final summary message

Project Structure

JPEG-Extractor-Tool/
├── jpegextractor-gui.py    # Entire application (GUI + worker + carving logic)
├── screenshot.png          # UI screenshot referenced by README
├── LICENSE                 # MIT License
└── README.md

The whole application lives in a single file, jpegextractor-gui.py, organised into these sections:

Section Responsibility
Constants RAW_EXTENSIONS tuple of supported extensions
validate_jpeg() Binary JPEG integrity check
JPEGRepairApp Main QWidget: splitter layout, config frame, files table, preview card, stats, log, actions
JPEGExtractionWorker QThread subclass running the extraction loop
extract_jpeg_from_raw() Regex-based JPEG carver

Technologies Used

  • Python 3 — application language
  • PyQt6 — GUI framework (widgets, signals/slots, QThread, drag & drop, QTableWidget, QPixmap)
  • re — binary regex pattern matching for JPEG marker detection
  • threading.Event — cooperative cancellation flag shared with the worker

Known Limitations

  • The carver extracts only one JPEG per source file — the last valid start/end pair found. Multi-image RAW files (e.g. RAW + embedded thumbnail) yield the largest/last JPEG only.
  • Only JPEGs beginning with FF D8 FF E1 (EXIF), FF D8 FF E0 (JFIF), or FF D8 FF DB (DQT) are detected. Unusual JPEG variants with different APP markers may be missed.
  • Extraction depends on the RAW container actually holding an embedded JPEG preview. Some RAW formats store only compressed sensor data with no JPEG stream — those will report Failed.
  • The entire source file is read into RAM; very large files (multi-GB) may be slow or memory-intensive.
  • No EXIF metadata is preserved or re-attached to the carved JPEG.
  • Report export writes plain text only (.txt), not CSV or JSON.

Contributing

Contributions, bug reports, and feature requests are welcome.

  1. Fork the repository.
  2. Create a feature branch (git checkout -b feature/my-improvement).
  3. Commit your changes with a clear message.
  4. Open a pull request describing what you changed and why.

If a specific camera model fails to yield any JPEG, please open an issue and include:

  • The camera make and model
  • The RAW file extension
  • The log output from the app
  • (If possible) a small sample of the RAW file

License

This project is licensed under the MIT License — see the LICENSE file for details.


Acknowledgements

  • The PyQt6 team — GUI framework
  • Special thanks to OpenAI for providing guidance and assistance during development

Related Tools

Part of the DRCRecoveryData toolkit — free recovery utilities for ransomware-affected data:

About

A Python script for fast, concurrent extraction of JPEG data from RAW files, preserving original quality. Simplify your RAW file workflow effortlessly.

Resources

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages