Skip to main content

nkscan

GitHub Actions Workflow Status Crates.io Version docs.rs PyPI Version

A cross-platform and performant driver for Nikon (Coolscan) film scanners.

Installation

There is no installer, just grab a binary for your system from the releases.

The MacOS binaries are not signed, so Gatekeeper will trigger and will prevent it from running. Clear that with xattr -d com.apple.quarantine nkscan-aarch64-apple-darwin, or build from source instead.

Building from Source

If you have a rust toolchain installed, you can install straight from crates.io via

cargo install nkscan --features cli --locked

which will put nkscan on your path.

Otherwise, clone the repo and cargo build --release --features cli.

If you have nix installed, you can build with nix build. We have the package defined in the flake, so you can add this to your personal config, if you wish.

Python

To use the library from Python, install it with pip install nkscan. Refer to PYTHON.md for the API and examples.

Example

Say I'm batch scanning 6x6 color negatives on my Coolscan 9000 (the only Nikon scanner attached to my computer). I usually do 2x multisampling at the full native resolution with an IR pass. Additionally, I'll "lock" the exposure from the first frame so every frame is exposed the same off the scanner so I can perform roll analysis when I invert. To do this and scan my whole roll (with the program prompting between strips), I'd run

nkscan scan --lock-ae --samples 2 --ir --format 66

demo gif

Options

nkscan scan --help
Perform a scan. Defaults to batch scanning with sensible defaults

Usage: nkscan scan [OPTIONS] [DEVICE]

Arguments:
  [DEVICE]
          The scanner to connect to. Optional, will default to the first found

Options:
      --basename <BASENAME>
          Where to write, as a path prefix. Each frame becomes <basename>_<n>.tiff, and its infrared mask <basename>_<n>_IR.tiff
          
          [default: scan]

      --unlock-wb
          Autoexpose per channel, taking the film's own cast off. The default for color negative, where that cast is the orange mask

      --lock-wb
          Autoexpose the channels as one, keeping the film's cast and the factory balance. The default for slide, Kodachrome and black and white

      --lock-ae
          Autoexpose the first frame and reuse that exposure across all frames

      --dpi <DPI>
          Resolution. Defaults to scanner maximum

      --log <LOG>
          Log verbosity: trace, debug, info, warn, error, or off
          
          [default: info]

      --samples <SAMPLES>
          Number of samples. Defaults to 1
          
          [default: 1]

      --superfine
          Singleline CCD mode. Only supported on multiline CCD scanners

      --frames <FRAMES>
          Which frame(s) to scan, comma separated. Defaults to all detected. Naming any stops after one holder rather than batching

      --ir
          Include the IR pass

      --clean
          Remove dust and scratches using the infrared channel

      --no-eject
          Don't eject at the end of the strip

      --thumbnail
          Keep the framing thumbnail as <basename>_<n>_thumbnail.tiff, on units that support this

      --format <FORMAT>
          Film format. One of: 135, half, IX240, 16, 645, 66, 67, 68, 69, or a custom frame length in mm (LS-8000/9000 only, up to 83 mm). Defaults to what the holder reports (if any)

      --film <FILM>
          Film type, which picks the color profile the scans are tagged with

          Possible values:
          - positive:   Slide film
          - negative:   Color negative
          - kodachrome: Kodachrome, whose dyes need their own profile
          - mono:       Black and white negative
          
          [default: negative]

  -h, --help
          Print help (see a summary with '-h')

Support

Our goal is to support all the scanners supported by Nikon Scan, which are enumerated here by testing status. This library doesn't have anything scanner or adapter-specific so theoretically it should work across devices.

If you test with a ⚠️-marked scanner/adapter combo and it works, please send a PR indicating support!

  • ✅ Supported, and run against real hardware
  • ⚠️ Untested but theoretically should work

Medium Format Scanners

Scanner \ Holder 835M 835S 869S 869G 869GR 869M 816 8G1
Super Coolscan 9000 (LS-9000 ED) ⚠️ ⚠️ ✅ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️
Super Coolscan 8000 (LS-8000 ED) ⚠️ ⚠️ ✅ ✅ ⚠️ ⚠️ ⚠️ ⚠️

35mm Scanners

Scanner \ Holder SA-21 IA-20/21 MA-20/21 SA-30 SF-210/200
Super Coolscan 5000 (LS-5000 ED) ⚠️ ⚠️ ✅ ⚠️ ⚠️
Super Coolscan 4000 (LS-4000 ED) ✅ ⚠️ ⚠️ ✅ ⚠️
Coolscan V (LS-50 ED) ✅ ✅ ✅ ✅ ✅
Coolscan IV (LS-40 ED) ✅ ⚠️ ⚠️ ⚠️ ⚠️

If you want to use a Firewire scanner on an old Mac that still has OS support for FireWire, let me know and I can scope it out. It is technically possible, but getting Rust to compile a binary for older MacOS is not something I have experience in. You could also just like, install Linux on it :)

USB Scanner Drivers

We use nusb, which is a pure-Rust alternative to libusb, but it carries the same invariants. On Windows, this means you need to associate your device with a WinUSB driver. The most popular way to do this is with Zadig.

On Linux, make sure you have the appropriate udev rules set up. Nusb has some help on this.

MacOS should just work.

FireWire Drivers

Things should just work on Linux (assuming you've got the SG module loaded) and Windows. MacOS dropped support for FireWire in Tahoe, but the open source ASFireWire project brings it back on Apple Silicon as a third-party dext. nkscan is tested and verified to work well with ASFireWire. If you have an older mac with FireWire on it, you could just install Linux and have an OS that respects your freedom.

Design Notes

This library is written from the ground up following the official Nikon spec of the wire protocol for the LS-5000 and LS-9000 ED scanners (located in docs/). Comparing the two, we find an identical protocol. Some types are absent in one but not the other, some lists capabilities the other doesn't have, but all of the bits and bytes are in the same position across all the data. This implies we don't need any model or holder specifics, we can just read what the scanner advertises as its capabilities and work from there (for the most part). This means (hopefully) we can support every scanner and every holder with a single codebase (although please test and let me know)!

The code is broken down into several layers of independent abstractions

  • Transport: Defines what moving SCSI bytes is for the different OSes and physical layer (USB/FireWire)
  • Protocol: An implementation of the Nikon spec via serialization and deserialization of bytes as they come off the wire. This module does no IO and is just byte-oriented.
  • Session: Combines a trait object of the Transport (type erasure) with the methods from Protocol. This wraps scanner state (like global units) and provides functions that essentially perform the spec's listed actions.
  • Scan: Combine the methods from Session to perform high-level scan operations. This asks the scanner what it can do and then orders session operations to do it.

RE: LLMs

I'd rather spend money on film than on tokens. While LLMs helped with the production of some of this crate, it was largely written by hand and not vibe-coded. If you contribute, please adhere to the contribution guide.

Why Rust

I'm impatient and don't have time for runtimes, garbage collection, and dumb compilers. I like types, memory safety, correctness, and speed. Rust's model fits this better than any language, plus it has great tooling and libraries for the low-level programming in this crate. For the CLI user, you get one ~5MB binary and that's it, no messing around. I'm not super interested in a GUI right now, but that's the library part of this code base. Please go make one (hopefully also in Rust)!

License

Dual licensed under either of

at your option.

Except for the ICC profiles in profiles/, which are derived from Nikon's and are not ours to license.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Metadata

Release files for nkscan 0.14.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nkscan 0.14.2
File Size Uploaded Provenance Actions
nkscan-0.14.2.tar.gz 2.9 MB With provenance Details for nkscan-0.14.2.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for nkscan 0.14.2
File Actions
nkscan-0.14.2-cp313-abi3-win_amd64.whl CPython 3.13 abi3 Windows x86-64 Details for nkscan-0.14.2-cp313-abi3-win_amd64.whl
nkscan-0.14.2-cp313-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.13 abi3 Linux glibc 2.17+ x86-64 Details for nkscan-0.14.2-cp313-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
nkscan-0.14.2-cp313-abi3-macosx_11_0_arm64.whl CPython 3.13 abi3 macOS 11.0+ ARM64 Details for nkscan-0.14.2-cp313-abi3-macosx_11_0_arm64.whl
nkscan-0.14.2-cp313-abi3-macosx_10_12_x86_64.whl CPython 3.13 abi3 macOS 10.12+ x86-64 Details for nkscan-0.14.2-cp313-abi3-macosx_10_12_x86_64.whl

Total release size: 18.0 MB

Release files / nkscan-0.14.2.tar.gz

Download URL nkscan-0.14.2.tar.gz
Size 2.9 MB
Tags Source
SHA-256 checksum
How to use checksums
2f4a8c3de9f73e076c1364af92fc566b126c1eec2d44482e779c86cade8b9d46
BLAKE2b-256 checksum
How to use checksums
0f23a99115465fcfac857742345bba7038192b53118b3bab4bbe1c0bfdbc3ed9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release files / nkscan-0.14.2-cp313-abi3-win_amd64.whl

Download URL nkscan-0.14.2-cp313-abi3-win_amd64.whl
Size 3.6 MB
Tags CPython 3.13 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
734d1049d4203cfb4cedc1d69d0f53bbfa031fd3689c3a83d0452f1a43306e89
BLAKE2b-256 checksum
How to use checksums
e5b32de4e2afe0df0c4ee1fb1b010d6d088c1f9886f7da14705ba484ac7a7cb3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release files / nkscan-0.14.2-cp313-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL nkscan-0.14.2-cp313-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 3.9 MB
Tags CPython 3.13 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
94717316d492379177c516146945308c56e6915acdc7848e0222edbd4f6661d5
BLAKE2b-256 checksum
How to use checksums
791b4e4d22b6ec16bf905e8b3ca16f16603563cce2d7874bf1c4da2b0863bb1c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release files / nkscan-0.14.2-cp313-abi3-macosx_11_0_arm64.whl

Download URL nkscan-0.14.2-cp313-abi3-macosx_11_0_arm64.whl
Size 3.8 MB
Tags CPython 3.13 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
7c8fbf8754cfe795b8c13910edf579931f3cfac4a39034712dde352baf2d2be1
BLAKE2b-256 checksum
How to use checksums
db0c359acf66ffb761cd9168b505273c800c9f17b647a9e7265f1d6397152ce1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release files / nkscan-0.14.2-cp313-abi3-macosx_10_12_x86_64.whl

Download URL nkscan-0.14.2-cp313-abi3-macosx_10_12_x86_64.whl
Size 3.8 MB
Tags CPython 3.13 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
d46fa90c320cee5629eb5bd2d1e8ad7bbe6b13276ee41f886b8e07beaabd4439
BLAKE2b-256 checksum
How to use checksums
e30f918c06c216b302f06a51072ba88af8e8967e70dde5726a7a8a15ec08bdf3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Security for nkscan 0.14.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 5 files in this release have been uploaded with provenance metadata.

Select from 5 files

Attestation for nkscan-0.14.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page