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.13.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.13.0
File Size Uploaded Provenance Actions
nkscan-0.13.0.tar.gz 2.8 MB With provenance Details for nkscan-0.13.0.tar.gz

Built distributions (wheels)

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

Total release size: 18.0 MB

Release files / nkscan-0.13.0.tar.gz

Download URL nkscan-0.13.0.tar.gz
Size 2.8 MB
Tags Source
SHA-256 checksum
How to use checksums
c76b6a6d2e5d4d29f5f83c45ea1c487854e54cc09d062cc80caa76a11aa2c5b4
BLAKE2b-256 checksum
How to use checksums
aba782e5e98a661d8444b92224b9bbf69bbe116e0000ca6040f87fba679cf3ed
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 13, 2026.

Transparency log

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

Download URL nkscan-0.13.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
ccb82d6f0e8b5ccddecaff04bb6a2496ccf2cf5c7f24c5c3019c01b389fa7ce3
BLAKE2b-256 checksum
How to use checksums
c22303b3e464e2dc1dc9b92d5aa47a2808eeddac42fe3fec90f546feea88f8d0
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 13, 2026.

Transparency log

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

Download URL nkscan-0.13.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
161121a2df3ef659d44a729d30659cba2aedf0c8700ea131a906ba3350672793
BLAKE2b-256 checksum
How to use checksums
9330cfd1b21a62ea9132eb50a5443b1f296c0209d70aa759fe58b70bcdc616c2
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 13, 2026.

Transparency log

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

Download URL nkscan-0.13.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
0dcda1f064e69f2685b67fc790248cc115e6f95b58c5ed55f59a4fddb58f74b3
BLAKE2b-256 checksum
How to use checksums
4c96ab3d87c5ec0b1c502f133d6812536662a2051de1c5970d119840b9872d4f
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 13, 2026.

Transparency log

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

Download URL nkscan-0.13.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
f47df183a2cc17a6705316369387355af411d8b3ad0b6752b2db14659c001343
BLAKE2b-256 checksum
How to use checksums
072710033495516b4fb35d28875da5ebf93154a750652bb45858e8faa92e8221
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 13, 2026.

Transparency log

Security for nkscan 0.13.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.13.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 13, 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