Skip to content

Repository files navigation

FirstEdit

A card of RAW files in. A folder of keepers with a starting edit out.

Culling and a per-frame starting edit for DxO PhotoLab, on your Mac.

Latest release macOS 15+ Python 3.12 MIT licence

Download · Guide · Workflow · How the edit is decided · Changelog

The cull's report: it put forward 150 of 1,558 frames, with what was set aside and why

What it does

FirstEdit takes a shoot from the card to PhotoLab. It culls the shoot, lets you pick keepers from the keyboard a burst at a time, and writes a DxO PhotoLab sidecar for each keeper. You open the folder in PhotoLab and finish the edit there.

  • Culls for you. Every face is judged at full resolution. Frames with blinks, blur or a mouth caught mid-word are set aside, and each one says why. Near-identical frames are grouped, never hidden.
  • Keyboard-first choosing. One burst on screen at a time, with a side-by-side compare for frames that differ by a blink.
  • A starting edit per frame. Each keeper gets its own exposure, tone curve, white balance and color, measured from its RAW. There are no presets for a whole shoot.
  • Learns your taste. Once a model beats the built-in rules on shoots it has never seen, the brightness, face and contrast targets are learned from your finished exports.
  • Everything is explained. Each frame's note says what was decided and why. Nothing is written that PhotoLab can't open.
  • Packed RAW backups. Backing up to iCloud packs each burst into one lossless .roll file, 57–88% of the size of its RAWs on real a6500 shoots (79% overall), and every byte comes back on the way down.

Noise reduction stays in PhotoLab (DeepPRIME). FirstEdit never renders your photos itself.

Screenshots

The light table: one frame at comparison size, the whole burst underneath Compare: four near-identical frames of one exchange, each with its focus score
Light table: one frame at comparison size, with the whole burst underneath Compare: four near-identical frames, each with its focus score

Install

  1. Download the .dmg from the latest release.
  2. Drag FirstEdit into Applications.
  3. On first launch, the app fetches one picture model (1.7 GB). Everything else ships inside the app.

Requires a Mac with Apple silicon on macOS 15 or newer, plus DxO PhotoLab to finish the edit.

How a shoot goes

Step What happens
1. Copy the card Every byte is read back after copying to check it.
2. Cull Broken frames are set aside with a reason. The rest are ranked a burst at a time.
3. Choose keepers E keep · D drop · S/F previous/next frame · R/W next/previous burst · C compare · Q undo
4. Presets A sidecar is written beside each keeper's RAW.
5. Edit in PhotoLab Your keepers open in one folder, with their sidecars.
6. Instagram & Finish Crops for Instagram. What you exported is recorded so the next shoot learns from it.

Packed RAW backups

Finish ▸ Back Up to iCloud… sends a shoot's RAWs up packed by default. Choose RAW files on the same sheet to send the ARWs as they are.

How it saves space. A burst is many near-identical frames. Each burst becomes one file, packed/burst-<n>.roll, holding every frame of that burst, not just one of them. The frame you kept is stored on its own, compressed. Each of the others is stored as its difference from the frame next to it, after allowing for the camera moving a few pixels between shots. A frame that isn't in any burst gets a file of its own (frame-<name>.roll), compressed the same way. The codec understands Sony's compressed-ARW sensor blocks (a6500 "Compressed" RAW), and coding those more tightly is where nearly all of the saving comes from. On real a6500 bursts the frame-to-frame differences add little: a frame stored from its neighbour packs to about the size of one stored alone (73% against 75% outdoors, 88% both indoors), since what is left once the picture is predicted is sensor noise, which differs in every frame. So a long burst saves no more per frame than a short one.

It's lossless. Unpacking gives back the original ARW, byte for byte, with its modified time. Each file is unpacked and checked against its RAWs' checksums before it is copied up, and read back after. A frame that doesn't come back exactly is sent as its ARW instead.

What it saves. Measured on three real a6500 shoots, every burst packed and checked (an a6500 compressed ARW is about 24 MB):

Shoot Frames RAWs Packed Saved
A short shoot 29 0.68 GB 0.39 GB (57%) 0.29 GB
Outdoors, overcast 850 19.8 GB 14.5 GB (73%) 5.3 GB
Indoors, a dim gym 649 15.1 GB 13.3 GB (88%) 1.8 GB
All three 1,528 35.6 GB 28.1 GB (79%) 7.4 GB

How much a shoot saves depends on its light. Sensor noise can't be predicted, so frames shot in good light at low ISO pack small, and dim, high-ISO frames, which are mostly noise, barely pack at all. On the bursts the codec was first measured on (Google's HDR+ set, bright hand-held bursts) the packed files came to 40–66% of their RAWs, where xz -9 managed 74%. ./pl burstpack bench <shoot> measures a shoot of yours without writing anything.

Getting them back. You never have to unpack by hand. Culling again, writing the presets or building the PhotoLab folder first brings back any RAW that is only in iCloud, from its ARW or its .roll. The viewer shows packed frames as it shows RAWs, unpacking a burst when one of its frames is opened. Bring Back from iCloud… puts every RAW of a shoot back on the Mac. In Finder, each .roll shows the frame you kept as its icon, the space bar shows it large, and a double-click opens it in Preview (the kept frame's RAW, unpacked and checked) without opening FirstEdit. A .roll that is only in iCloud still shows its picture, which it carries with it, and nothing is downloaded to show it. Packed bursts from before v0.1.9 get their picture with ./pl rollicon <folder>. Free Up Space ▸ Pack in iCloud packs RAWs that are already up there as ARWs, several bursts at once, then removes each ARW copy once its packed copy is proven.

How the codec works, with the measurements: docs/BURSTPACK.md.

The starting edit

Each keeper is measured on the camera's JPEG and on the RAW, and gets values of its own:

How it is decided
Exposure The midtones, or the main face, move toward a target brightness. The lift never goes past the highlights the RAW can hold, the noise its ISO allows, or 1.5 stops. Frames with clipped highlights use PhotoLab's own highlight recovery. A face that is still too dark gets a gentle mask.
Tone curve A gentle S-curve, sized to the frame's light.
White balance As shot, or PhotoLab's fluorescent preset where your own edits show you would switch.
Color More Vibrancy on flat frames and none on vivid ones. Skies and foliage are nudged toward their preferred colors, and skin stays under a published limit.

Published rules decide until a model has learned your exports. A learned model is used only if it beats the rules on held-out shoots, and even then it can't move a safety limit. Every frame's note in cull/presets.md says which one decided. docs/COLOR.md has the sources for every target, and docs/ML.md has the checks a model has to pass.

From a checkout

The engine in pipeline/ runs on its own. app/ builds it into the Mac app.

./pl setup                                          # venv and models (Python 3.12, exiftool)
./pl ingest /Volumes/CARD 2026-10-04-lake           # copy a card into ~/photos/shoots/
./pl cull ~/photos/shoots/2026-10-04-lake           # cull it
./pl presets ~/photos/shoots/2026-10-04-lake --dop  # write the sidecars
./pl gather ~/photos/shoots/2026-10-04-lake         # one folder of keepers for PhotoLab
./pl learned run                                    # learn from what you have exported

Every command takes the shoot folder, its raw/, or its cull/. Each shoot lives in ~/photos/shoots/<date-name>/:

raw/         the RAWs, with a sidecar beside each keeper
decisions/   your keeps, stars and drop reasons
cull/        rebuildable work: reports, previews, presets.md
edit/        the keepers, gathered for PhotoLab

Documentation

Guide The long version, with every number and the reason for it
Workflow Step by step, from card to last photo
Color How exposure, tone and color are decided, with sources
ML What is learned, and the checks it has to pass
Design The app, screen by screen
Burstpack Packed RAW backups: the format, the codec and what it saves
Contributing Running the checks and building the app
Releasing Signing, notarizing and publishing a release

Contributing

FirstEdit has been measured on four of my own shoots so far. If it gets your camera, editor or kind of shoot wrong, please open an issue with the frame and the numbers from its note. That is the most useful thing you can send. For security reports, see SECURITY.md.

Licence

MIT. The images in tests/fixtures/pets come from the Oxford-IIIT Pet dataset (CC BY-SA 4.0) and are not covered by that licence. NOTICES.md is generated at every build and ships inside the app and beside the DMG. It lists every third-party component and its licence.

About

Card to finished photos: every face judged at full resolution, a DxO preset per setup, one window from the card to the export. Early work in progress.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages