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.3

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.3
File Size Uploaded Provenance Actions
nkscan-0.14.3.tar.gz 2.9 MB With provenance Details for nkscan-0.14.3.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for nkscan 0.14.3
File Actions
nkscan-0.14.3-cp313-abi3-win_amd64.whl CPython 3.13 abi3 Windows x86-64 Details for nkscan-0.14.3-cp313-abi3-win_amd64.whl
nkscan-0.14.3-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.3-cp313-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
nkscan-0.14.3-cp313-abi3-macosx_11_0_arm64.whl CPython 3.13 abi3 macOS 11.0+ ARM64 Details for nkscan-0.14.3-cp313-abi3-macosx_11_0_arm64.whl
nkscan-0.14.3-cp313-abi3-macosx_10_12_x86_64.whl CPython 3.13 abi3 macOS 10.12+ x86-64 Details for nkscan-0.14.3-cp313-abi3-macosx_10_12_x86_64.whl

Total release size: 18.0 MB

Release files / nkscan-0.14.3.tar.gz

Download URL nkscan-0.14.3.tar.gz
Size 2.9 MB
Tags Source
SHA-256 checksum
How to use checksums
c1a847afffb572050aa2a622229a1343ffc687b14275e94c39ac1d0c82056073
BLAKE2b-256 checksum
How to use checksums
a3edc901145ca096f85a40dba47be58e0511df7ecae9175b66f97e6b23ccef30
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 19, 2026.

Transparency log

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

Download URL nkscan-0.14.3-cp313-abi3-win_amd64.whl
Size 3.6 MB
Tags CPython 3.13 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
e97ae96175ab80bdf24967c9c1d6265162cdd15d96b01e4674067cf48ecca7cb
BLAKE2b-256 checksum
How to use checksums
64f06750693d5b0798024bda838f3106af4b517791afaf8eecbcaca4c0d4cdc7
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 19, 2026.

Transparency log

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

Download URL nkscan-0.14.3-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
ef7e3ccc595aa62b7c04029a022e70f6311f24196d46fa70a551be2b0812e296
BLAKE2b-256 checksum
How to use checksums
681b96237d44ffd94cd68c2c6ebd63d9be467a4c4d7a407c5278e9d7b969f7aa
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 19, 2026.

Transparency log

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

Download URL nkscan-0.14.3-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
7397f29377cbcb986be37e4a776328296675ffa4095fa84d106c318bd72971b7
BLAKE2b-256 checksum
How to use checksums
7c1834a201e02b91c8aa7edf430929d8f886d448f0b7cafadd21ce9c84813041
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 19, 2026.

Transparency log

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

Download URL nkscan-0.14.3-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
c5dd6e595f2f8647b3d448aac7cc1645e865281dd976a1160159121dc8a30dba
BLAKE2b-256 checksum
How to use checksums
5dda4025b70e32a576ff2a527a2d8105203d22830e5e75b0cfd74da30862a688
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 19, 2026.

Transparency log

Security for nkscan 0.14.3

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.3.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 19, 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