Created
February 4, 2020 02:18
-
-
Save mildmojo/f745f7eaee44ba8a4776a62a0c72a260 to your computer and use it in GitHub Desktop.
Dark frame subtraction script for removing digital camera sensor noise from long-exposure photos
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
/* | |
darksub.js | |
Implements the dark frame subtraction pipeline using ImageMagick described here: | |
https://www.imagemagick.org/discourse-server/viewtopic.php?p=62840&sid=c6a5c35cc2805a51e37ae5f18895b609#p62840 | |
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>`); | |
process.exit(1); | |
} | |
files = Array.from(process.argv.slice(2)); | |
darkFrame = files.shift(); | |
if (!fs.existsSync(OUTPUT_DIR)) | |
fs.mkdirSync(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}`); | |
//blurs | |
// 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}`); | |
[ | |
darkProduct, | |
oneMinusDarkProduct, | |
blurFile, | |
corrections, | |
].forEach(fs.unlinkSync); | |
process.stdout.write('done\n'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment