Skip to content

Instantly share code, notes, and snippets.



Created Feb 4, 2020
What would you like to do?
Dark frame subtraction script for removing digital camera sensor noise from long-exposure photos
#!/usr/bin/env node
Implements the dark frame subtraction pipeline using ImageMagick described here:
It's very slow.
This is really just a batch script; you'll need to have ImageMagick's
command-line utility `convert` installed (try `apt install imagemagick`
or `brew install imagemagick`).
Usage: $ darksub.js <darkframe.jpg> <pic1.jpg...picN.jpg>
The first argument should be the dark frame to be subtracted.
The remaining arguments are the photos from which to remove the dark frame noise.
Files are written to the directory named in OUTPUT_DIR.
const fs = require('fs');
const path = require('path');
const execSync = require('child_process').execSync;
const BLUR_RADIUS = 20;
const OUTPUT_DIR = 'darksub-out';
if (process.argv.length < 4) {
console.log(`Usage: ${path.basename(__filename)} <darkframe.jpg> <target0.jpg...targetN.jpg>`);
files = Array.from(process.argv.slice(2));
darkFrame = files.shift();
if (!fs.existsSync(OUTPUT_DIR))
for (file of files) {
// Don't de-noise the dark frame if it's among the targets.
if (file === darkFrame) continue;
outfile = path.join(OUTPUT_DIR, file);
darkProduct = path.join(OUTPUT_DIR, 'darkProduct.png');
oneMinusDarkProduct = path.join(OUTPUT_DIR, 'oneMinusDarkProduct.png');
corrections = path.join(OUTPUT_DIR, 'corrections.png');
blurFile = path.join(OUTPUT_DIR, 'blurFile.png');
process.stdout.write(`Processing ${file}....`);
// the product of the dark frame and the starting image is now in startbydark
execSync(`convert -compose multiply -composite ${darkFrame} ${file} ${darkProduct}`);
// I multiply by 2.54, as the math has some weird 255=100 thing, I'm sure it's documented, but...
execSync(`convert -evaluate Multiply 2.54 ${darkProduct} ${darkProduct}`);
//start - dark
// oneMinusDarkProduct = start - start*dark = start (1-dark)
execSync(`convert -compose minus -composite ${darkProduct} ${file} ${oneMinusDarkProduct}`);
// so I can easily get the local color
execSync(`convert -gaussian-blur 20x${BLUR_RADIUS} ${file} ${blurFile}`);
//local * dark
// corrections = dark * local_color
execSync(`convert -compose multiply -composite ${darkFrame} ${blurFile} ${corrections}`);
// fixing the math again, yes I realize this should be 2.55 and not 2.54
execSync(`convert -evaluate Multiply 2.54 ${corrections} ${corrections}`);
// final = start - start*dark + dark*local_color = (1-dark) * start + dark * local
execSync(`convert -compose plus -composite ${oneMinusDarkProduct} ${corrections} ${outfile}`);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.