Last weekend I decided to push forward an idea which has wrapped around my mind for a very long time. My family (I guess like almost everyone’s) has a lot of photos, notes, documents, etc. A lot of memories, packed into albums, envelopes, frames, or hidden somewhere between books on the bookshelf. Pictures fade or lose quality over time. That’s why I decided to scan them, but as there are a lot of them, I wanted to automate the process as much as I can.

Manual Process

The process is simple and can be broken down into the following steps:

  1. Insert a few pictures into a scanner. I want to scan multiple pictures at once to make the whole process faster.
  2. Scan usingScan To Filebutton directly to the connected computer.
  3. Let the script make the rest, so go to step 1.

The Script

  1. Watch scanner output directory.
  2. If a new image, use ImageMagick to crop multiple images from it and then deskew them if needed. Move results to a different watched folder for further processing.
  3. Convert images to jpegs and move to the output directory.
  4. Preserve scan somewhere just in case.

Sounds easy, right? Ok, maybe except cropping images and deskewing part, but thanks tothe GoogleI found a glut of useful ImageMagick scripts. Those scripts are free, but only for non-commercial use, so mind your intentions. Of course, you have to have ImageMagick installed in the first place.

Gimmie Some Code

There isNode.jsmentioned in the title of this post, so Yes… I feel compelled to show some code ;). Here is the script code.

processArgs();
checkArgs();
checkDirs();
processImagesDir(imagesDir);

chokidar.watch(path.join(imagesDir, "*.pnm"), {depth: 0}).on("add", multicrop);
chokidar.watch(path.join(croppedDir, "*.pnm"), {depth: 0}).on("add", convertToJpg);

process.on("exit", exitHandler.bind(null, {cleanUp: true}));
process.on("SIGINT", exitHandler.bind(null, {exit: true}));
process.on("uncaughtException", exitHandler.bind(null, {exit: true}));

Default nodefs.watchmethod gave me a big headache, that’s why I decided to go for chokidar lib.

The most important part is the usage of multicrop script. You can always modify parameters if you are not pleased with results.

var outputFile = path.join(tempDir, path.basename(file));

exec("./multicrop -u 1 -d 50 -b white " + file + " " + outputFile, function (error) {
    if (error) {
        throw new Error("Error while multi cropping: " + error);
    } else {
        var processedFile = path.join(processedDir, path.basename(file));

        exec("mv " + file + " " + processedFile);
        exec("mv " + tempDir + "/* " + croppedDir);
    }
});

Conversion to.jpgis pretty straightforward.

var outputFile = path.join(outputDir, path.basename(file, ".pnm")) + ".jpg";

exec("convert -quality 90 " + file + " " + outputFile, function (error) {
    if (error) {
        throw new Error("Error while converting to jpeg: " + error);
    } else {
        exec("rm " + file);
    }
});

You can find full project on Github.

Results

I am pretty happy with results. Of course, I need to rotate pictures accordingly later.

results

Post Production

Sometimes I need to discard a few of the result pictures because of additional “subpictures” extracted from the original one. There is an option for the multi-crop script to discard images smaller than a given size. You have to be careful scanning small pictures, though.

I found Google Photos pretty robust for simple post-production, including applying automatic filters and deskewing if the script doesn’t work perfectly.

Things to do

Port to Windows. I have windows machine somewhere ;]. I will update this post afterwards when it’s done.

Conclusions

I find this approach pretty fast and good enough for my needs. Maybe You Guys know a better and also FREE way? Let me know in the comments.