Skip to main content

nkscan

GitHub Actions Workflow Status

A cross-platform and performant library and command line application for Nikon film scanners

Support

I only own a Coolscan 9000, but through reverse-engineering of the Nikon binaries and the work of others, we are slowly adding support for more scanners. Please reach out if you have a scanner we can test! Even just dumps of USB/SCSI payloads of a normal scan would be incredibly useful.

Our goal is to support all the scanners supported by Nikon Scan, which are enumerated here

  • ✅ Supported, and run against the hardware
  • ⚠️ Untested but theoretically should work
  • ❌ Not started

Every scanner Nikon Scan supported is recognized by name, including the ones with no driver: nkscan --list will report an attached 8000, 4000 or IV and say it has no driver rather than staying silent about it.

Medium Format Scanners

Scanner \ Holder 835M 835S 869S 869G 869GR 869M 816 8G1
9000 ⚠️ ⚠️
8000

35mm Scanners

Scanner \ Holder SA-21 IA-20/21 MA-20/21 SA-30 SF-210/200
5000 ⚠️ ⚠️
4000
V ⚠️
IV

MacOS Firewire support is planned, but just stubbed out for the moment. It should work for USB scanners, like the Coolscan V.

Caveat Emptor

While the 9000 has the best support right now, there are still some gaps in capability based on what I have.

  • The Coolscan 9000 workflow assumes a medium format strip film holder. The frame detection algorithm will not work for others, but you can manually place the frames for the time being. If someone has the 35 holder or others, please reach out and we can add the missing logic.
  • The Coolscan V workflow assumes a strip holder, not a full roll holder. I need more dumps of payloads/RE work to understand the typical flow for those (or the other inserts like the bulk slide loader)
  • The Coolscan 5000 has now scanned with an SA-21, on Windows. The roll transport, the multi-sample readout and the metering path are all still inference, so treat anything past a strip scan as unproven.
  • Which film holder a medium format body has loaded cannot yet be identified exactly. The scanner reports a holder class rather than a part number, and a class does not say whether it is an FH-869S, an FH-869G or a 35 mm carrier, so capabilities fall back to what every holder in the family can do. See docs/HOLDERS.md.

If you would like to contribute support for new scanners, please take a look at scsi_proxy/README.md

Usage

Usage: nkscan [OPTIONS]

Options:
      --device <DEVICE>            Which scanner to use, as `--list` reports it. Only needed when more than one is attached
      --list                       List the scanners attached and exit, without touching any of them
      --frames <FRAMES>            Frames on the loaded strip
      --frame <FRAME>              Which frames to actually scan, zero-indexed and comma separated. All of them by default
      --dpi <DPI>                  Resolution in DPI. One of the firmware's divisions of the sensor's native pitch
      --gain <R,G,B[,IR]>          Fixed per-channel analog gain as `red,green,blue[,ir]`, which turns autoexposure off
      --focus <FOCUS>              Focus: `auto` to let the scanner find it on each frame, or a fixed setpoint [default: auto]
      --ir                         Capture the infrared plane for dust removal
      --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]
      --offset <OFFSET>            Where each frame starts along the feed, in mm, comma separated, last value repeating
      --pitch <PITCH>              Frame pitch in mm, overriding what the scanner would use
      --lock-wb                    Hold the white balance during autoexposure, so the film keeps its cast. Not on the LS-50
      --multisample <MULTISAMPLE>  Multisampling, trading scan time for noise. One of 1,2,4,8,16. LS-9000 only [default: 1]
      --singleline                 Single-line CCD mode. Slow, but may improve banding. LS-9000 only
      --eject                      Send the film back out when everything is done
      --batch                      Scan every strip of the roll, ejecting and pausing to reload in between
  -h, --help                       Print help (see more with '--help')
  -V, --version                    Print version

Example Workflow

Raw Negatives for External Inversion

Say I have a LS-9000 and I invert with either NegPy or Negative Lab Pro. For this to work well, I want essentially the raw scans out of the scanner, but with equal exposure across all frames so I can perform "roll analysis".

So, I load up the film holder with the first strip (in my case 6x6 negatives, with three frames), and scan in "batch mode". For this, I tell the program how many frames per strip, include the IR pass, lock the whitebalance during autoexposure of the first frame (which will be applied to every farme), and enable 2x multisampling.

nkscan --frames 3 --ir --lock-wb --multisample 2 --batch

Personally, I find this much faster than anything I could do in NikonScan or Vuescan.

Python

We also ship the driver as a Python extension, which simplifies use in from Python-based scanning/inversion programs. The image data comes back as zero-copy numpy arrays in linear 16-bit ADC counts.

pip install nkscan
import nkscan

device = nkscan.list_devices()[0]
print(device.id, device.model, device.capabilities.dpi)

with nkscan.Session(device.id) as session:
    # Three 6x6 frames at a 56 mm pitch, holding one exposure across all of them
    session.prepare(frames=3, pitch_mm=56.0, gain=(283048, 202864, 166589))

    for frame in range(3):
        result = session.scan(frame, dpi=2000, ir=True,
                              progress=lambda read, total: print(f"\r{read}/{total}", end=""))
        result.rgb          # (height, width, 3) uint16
        result.ir           # (height, width) uint16, or None
    session.eject()

session.overview() takes the low-resolution pass Nikon Scan calls a thumbnail, returning the whole strip in one image alongside the resolution it ran at, which is what maps a point on the thumbnail back to a position on the film.

session.capabilities answers for the model and the loaded adapter, since most of what a scanner will do depends on what is in it: ejecting returns a holder on one adapter and rewinds a cartridge on another. Anything the scanner will not do raises nkscan.UnsupportedError, which carries feature and reason so a caller can tell "this scanner cannot" (not_present) from "this library does not yet" (not_implemented) without reading the message.

The scan may take minutes, so a progress callback can drive a UI and returning False from it stops the scan. Failures worth retrying share a nkscan.TransientError base, so one except covers a link glitch or a short read without swallowing a real problem like nkscan.MediaError.

Building it from source needs maturin; the dev shell in flake.nix has it, along with a Python carrying numpy.

maturin develop

The type stub is generated rather than written, so it cannot fall out of step with the bindings:

cargo run --features python --bin stub_gen

License

Dual licensed under either of

at your option.

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.

Related Projects and References

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nkscan-0.2.0.tar.gz (253.1 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

nkscan-0.2.0-cp313-abi3-win_amd64.whl (417.0 kB view details)

Uploaded CPython 3.13+Windows x86-64

nkscan-0.2.0-cp313-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (679.5 kB view details)

Uploaded CPython 3.13+manylinux: glibc 2.17+ x86-64

nkscan-0.2.0-cp313-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (634.0 kB view details)

Uploaded CPython 3.13+manylinux: glibc 2.17+ ARM64

nkscan-0.2.0-cp313-abi3-macosx_11_0_arm64.whl (554.7 kB view details)

Uploaded CPython 3.13+macOS 11.0+ ARM64

nkscan-0.2.0-cp313-abi3-macosx_10_12_x86_64.whl (559.8 kB view details)

Uploaded CPython 3.13+macOS 10.12+ x86-64

File details

Details for the file nkscan-0.2.0.tar.gz.

File metadata

  • Download URL: nkscan-0.2.0.tar.gz
  • Upload date:
  • Size: 253.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for nkscan-0.2.0.tar.gz
Algorithm Hash digest
SHA256 0c545fcc2b81d19f8db192d9d17befc394cf6c0cf8ac1550e6b072896faf8037
MD5 4248b852dfd8470a1c8fcdf2a84f1b91
BLAKE2b-256 05d3615036d9fa3bd799bceeed7220c683608a0969207bb9ec73b4000807ebd6

See more details on using hashes here.

Provenance

The following attestation bundles were made for nkscan-0.2.0.tar.gz:

Publisher: release.yml on activexray/nkscan

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nkscan-0.2.0-cp313-abi3-win_amd64.whl.

File metadata

  • Download URL: nkscan-0.2.0-cp313-abi3-win_amd64.whl
  • Upload date:
  • Size: 417.0 kB
  • Tags: CPython 3.13+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for nkscan-0.2.0-cp313-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 68e147308f47abfffb5009c5c49c0813aea2209fb5963282bf1ecd483369df29
MD5 dbf193d7792c81e8a29b2a1ecac255ff
BLAKE2b-256 b14f853a347095615381bc7c4c5bf902cab7173baac23c45fe4cbdd9885f5c90

See more details on using hashes here.

Provenance

The following attestation bundles were made for nkscan-0.2.0-cp313-abi3-win_amd64.whl:

Publisher: release.yml on activexray/nkscan

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nkscan-0.2.0-cp313-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for nkscan-0.2.0-cp313-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 05481a417bdb03e4f22e9c829043196e47ab9359fd33c5a44073630da01eddee
MD5 3b4adbb8a604a8a33114eab3b4583441
BLAKE2b-256 a432543747701c76fbe094f0f8a722fa7103b50713a1cf7c83f09a610e07f8fe

See more details on using hashes here.

Provenance

The following attestation bundles were made for nkscan-0.2.0-cp313-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on activexray/nkscan

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nkscan-0.2.0-cp313-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for nkscan-0.2.0-cp313-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 60bfd950b556198e95b3f688220f710eaa1419ce94e243cb206cbf6770d36e15
MD5 94218b887a7a0694cd851a203a93105b
BLAKE2b-256 72abae9dd681f1137dac262c60cc58ab6df9bd18d9731c4bf21b2c78c2b26d39

See more details on using hashes here.

Provenance

The following attestation bundles were made for nkscan-0.2.0-cp313-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on activexray/nkscan

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nkscan-0.2.0-cp313-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for nkscan-0.2.0-cp313-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 f47c0fe3d5c41f596a00a9d055c4e809a3b6ecc3871fdeec65b6cc0f1eaa828b
MD5 b0967f10df56299acaa048e47a3f2759
BLAKE2b-256 223b23fdae6bb5a4527e2131fcb5474b2e255ee74d37c31290bc3b939ecad5db

See more details on using hashes here.

Provenance

The following attestation bundles were made for nkscan-0.2.0-cp313-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on activexray/nkscan

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nkscan-0.2.0-cp313-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for nkscan-0.2.0-cp313-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 0240c6dd08cb62c9352e5129c98dc017feba9dfacf558866d48058d376e025af
MD5 a78bf8226ecb448bca362d1895fe5de4
BLAKE2b-256 de9f607e4b6236a4ab850661d654d2058aa5b236231b6ef6091f3486f81c9382

See more details on using hashes here.

Provenance

The following attestation bundles were made for nkscan-0.2.0-cp313-abi3-macosx_10_12_x86_64.whl:

Publisher: release.yml on activexray/nkscan

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.11.0

5 files

0.10.0

5 files

0.9.0

5 files

0.8.0

4 files

0.7.0

4 files

This release

0.2.0 This release

6 files

0.1.0

6 files

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