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

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

Built distributions (wheels)

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

Total release size: 18.0 MB

Release files / nkscan-0.16.0.tar.gz

Download URL nkscan-0.16.0.tar.gz
Size 2.9 MB
Tags Source
SHA-256 checksum
How to use checksums
9879c79301d7d0851f1f7c7a0eba6fe58b1aced38732dba75adc3245043ac737
BLAKE2b-256 checksum
How to use checksums
c07906765af3834eed4a3ab5548f6710b90f6dcf51599fe2f160ceb6a3cbd412
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 21, 2026.

Transparency log

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

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

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 21, 2026.

Transparency log

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

Download URL nkscan-0.16.0-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
470697558ca3557f05e8a8a3aaaa4bd331adc45b0325372937e56b0fbf846fb6
BLAKE2b-256 checksum
How to use checksums
888156a42279e874ce629e4df4ab3608bc02d34eb998a2778247265a755aa901
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 21, 2026.

Transparency log

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

Download URL nkscan-0.16.0-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
c04b51e10333bbeb929fa6b9ed86b2ce87d1143fc30fd6f5b0ae09568fcbd0a9
BLAKE2b-256 checksum
How to use checksums
fbed23aa3668461caefcbccda3d80e087400a8339734a3d09600bcc84c65793a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 21, 2026.

Transparency log

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

Download URL nkscan-0.16.0-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
360a7c1f49ea86bf41b7457d8b8ad0525cb2ead6966160a9bb560b50f6010a15
BLAKE2b-256 checksum
How to use checksums
b2a13ed0ff453f6b16dcaa8a6a29d18d5169c2d935a62260f008802965f846c7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 21, 2026.

Transparency log

Security for nkscan 0.16.0

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.16.0.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 21, 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