npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

negpro

v2026.3.17-1

Published

Convert scanned film negatives to positives. Core inversion logic based on negfix8 by jaz99 (https://www.flickr.com/people/jaz99) with many new options and more robust output.

Downloads

1,218

Readme

negpro

Convert scanned 16-bit linear film negatives to positives. Core inversion logic based on negfix8 by jaz99, with many new options and more robust output.

Installation

npm install -g negpro

You can also test out a web based version of negpro

Usage

negpro [options] [inputs...]

See examples

Arguments

| Argument | Description | |----------|-------------| | inputs | Input file(s) and optional output file |

Input/Output

| Option | Description | Default | |--------|-------------|---------| | --dir <directory> | Process all TIFFs in directory | | | --output-dir <directory> | Write output files to this directory | |

Tuning

| Option | Description | Default | |--------|-------------|---------| | --gamma <value> | Set image gamma | 2.15 | | --clip-black <percent> | Clip darkest N% to black during contrast stretch | 0.1 | | --clip-white <percent> | Clip brightest N% to white during contrast stretch | 0.1 | | --clip <percent> | Clip both black and white ends by N% during contrast stretch | | | --no-stretch | Disable contrast stretch | on |

Image Analysis

| Option | Description | Default | |--------|-------------|---------| | --per-image | Compute a separate profile for each image instead of sharing | | | --border-exclude <percent> | Exclude outer N% of image from profiling and contrast stretch | 2 | | --pixel-rejection-percentage <percent> | Ignore brightest/darkest N% of pixels within a frame when profiling | 0.1 | | --no-frame-rejection | Disable automatic outlier frame rejection from shared profile | |

Config/Profiles

| Option | Description | Default | |--------|-------------|---------| | --save-config | Save current flags as global defaults | | | --save-profile <name> | Analyze input files, save profile, then exit | | | --use-profile <name> | Use a previously saved profile | |

Utility

| Option | Description | Default | |--------|-------------|---------| | --dry-run | Show what would be done without processing images | | | --concurrency <n> | Number of images to process in parallel | CPU threads - 1 | | --examples | Show usage examples | | | --explanation | Show how negpro works, profiles, and config details | |

How it works

By default, inversion values are calculated from an average of all input files (either named or within the directory when using --dir), which leads to more consistent image balancing throughout a roll of film. This can be disabled with --per-image, which will perform balancing on an image-by-image basis.

Output image data is contrast-stretched to fill the histogram by default. This can be disabled with --no-stretch.

Large dust spots that would normally skew balancing are excluded from calculations by the default --pixel-rejection-percentage value of 0.1% in conjunction with the default contrast stretch and clipping behaviour.

When computing a shared profile across multiple images, frames with unusual channel characteristics (e.g. backlit shots) are automatically detected and excluded from the profile to prevent color casts. Excluded frames are still processed, just not used for profiling. Frame rejection requires at least 6 images to have enough data for reliable detection, and will never reject more than 25% of frames — if too many would be rejected, all frames are kept. This can be disabled with --no-frame-rejection.

Output files or directories are never overwritten. If a file or directory already exists, a numeric suffix (_2, _3, etc.) is appended automatically and a warning is shown.

Slight unexposed/film holder areas around the edge of the frame (2% by default) are excluded from analysis and can be tuned via --border-exclude

If you use --dir to process a directory a new directory will be created with a "_processed" suffix and a negpro-log.txt file will be created in addition to the output files containing relevant options for future reference.

After processing, negpro checks whether the default clipping may have been too aggressive for any images. Negatives with a narrow density range (where the darkest and lightest tones are close together) are particularly sensitive to percentage-based clipping — a fixed 0.1% clip can eat into real tonal detail when the histogram is compressed or accentuate film grain. If this is detected, a warning is shown suggesting you reduce --clip-black and --clip-white to 0.01.

Profiles

Profiles store the inversion parameters computed from analyzing your negatives. Create one from a set of reference frames and reuse it across batches for consistency:

negpro --save-profile portra400 *.tif
negpro --use-profile portra400 --dir scans

Profiles are saved to:

  • macOS/Linux: ~/.negpro/<name>.json
  • Windows: %USERPROFILE%\.negpro\<name>.json

Each profile includes metadata (timestamp, source files, settings) for future reference only — this metadata is not used when applying the profile.

Every run also saves its profile to ~/.negpro/last_run_profile.json. If you like the results, copy it to reuse later:

cp ~/.negpro/last_run_profile.json ~/.negpro/portra400.json
negpro --use-profile portra400 --dir scans

Config file

Every run saves its effective settings to last_run_config.json. If you like the results, copy it to use as your global config:

# macOS/Linux
cp ~/.negpro/last_run_config.json ~/.negpro/config.json

# Windows
copy %USERPROFILE%\.negpro\last_run_config.json %USERPROFILE%\.negpro\config.json

Or use --save-config with flags to write config.json directly:

negpro --save-config --gamma 2.5 --clip-black 0.5

Supported config keys: gamma, stretch, clipBlack, clipWhite, clip, pixelRejectionPercentage, borderExclude, concurrency, perImage, noFrameRejection. CLI flags always override config file values.

Config and profile files are stored in:

  • macOS/Linux: ~/.negpro/
  • Windows: %USERPROFILE%\.negpro\

Logs

Every run writes a timestamped log to ~/.negpro/logs/. These logs include full settings, per-file analysis, profile values, and any warnings. The last 20 logs are kept; older ones are automatically pruned.

When using --dir, a negpro-log.txt is also written alongside the output files for convenience.

Examples

Convert a single negative (output: scan_processed.tif in same directory):

negpro scan.tif

Convert a single negative to a specific output file:

negpro scan.tif output.tif

Convert multiple negatives (shared profile computed automatically):

negpro frame01.tif frame02.tif frame03.tif

Process an entire directory of scans (output: scans_processed/):

negpro --dir scans

Process a directory, writing output to a specific folder:

negpro --dir scans --output-dir positives

Preview what would happen without processing:

negpro --dry-run --dir scans

Adjust gamma (higher = brighter midtones):

negpro --gamma 2.5 scan.tif

Increase shadow and highlight clipping to 1% for more contrast:

negpro --clip 1 --dir scans

Clip shadows aggressively but preserve highlights:

negpro --clip-black 2 --clip-white 0.1 scan.tif

Disable contrast stretch (output will be darker/flatter):

negpro --no-stretch scan.tif

Increase outlier rejection to handle dusty scans:

negpro --pixel-rejection-percentage 0.5 --dir scans

Exclude more of the frame border (useful if film holder edges are visible):

negpro --border-exclude 5 --dir scans

Force per-image profiling (each image balanced independently):

negpro --per-image --dir scans

Disable automatic frame rejection (include all frames in shared profile):

negpro --no-frame-rejection --dir scans

Create a saved profile from reference frames:

negpro --save-profile portra400 ref01.tif ref02.tif ref03.tif

Create a saved profile from all TIFFs in a directory:

negpro --save-profile portra400 --dir scans

Apply a saved profile to a batch:

negpro --use-profile portra400 --dir scans

Save your preferred settings as global defaults:

negpro --save-config --gamma 2.5 --clip 0.5 --pixel-rejection-percentage 0.3

Copy last run's settings as your global config:

# macOS/Linux
cp ~/.negpro/last_run_config.json ~/.negpro/config.json

# Windows
copy %USERPROFILE%\.negpro\last_run_config.json %USERPROFILE%\.negpro\config.json

Process with limited parallelism:

negpro --concurrency 2 --dir scans

Credits

Inspired by negfix8 by jaz99.

Carried over from negfix8:

  • Core inversion algorithm
  • Pattern of being able to save a "profile" based on analysis of images and reuse it against other images

New aspects:

  • No ImageMagick dependency — just npm install -g negpro
  • Parallel processing for faster batch conversion
  • Improved performance: typically 4x faster before the parallel processing improvements and 18-20x faster with them
  • Defaults to whole-roll calculations for the orange mask, leading to more accurate and consistent color inversion
  • Default slight clipping (0.1%) during contrast stretch to prevent dust specks and specular highlights from pulling the stretch endpoints and producing poor results
  • Automatic outlier frame rejection — backlit or unusual frames are detected and excluded from shared profiling to prevent color casts
  • Tunable outlier rejection (light/dark pixels) so large dust spots don't throw off color balance
  • Automatic detection of narrow-density negatives where default clipping may be too aggressive, with a warning and suggested alternative settings
  • Independent control of shadow and highlight clip percentages, rather than just contrast stretch on/off
  • Persistent global configuration — save your preferred settings once and they apply automatically to all future sessions
  • Web-based version for immediate inversion without any installation