Search code examples
javascriptnode.jsimagemagickspawnexternal-process

ImageMagick compare: exit code 1 when spawned from Node.js, but exit code 0 when run from the command line


$ compare --version
Version: ImageMagick 6.9.1-1 Q16 x86_64 2015-04-15 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Features: DPC Modules
Delegates (built-in): bzlib freetype jng jpeg ltdl lzma png xml zlib


$ node --version
v0.10.33

I'm writing a Node.js script to automatically process some images for me.

When I try to run the script through child-process.exec or child-process.spawn it runs and completes correctly, but exits with code 1 instead of code 0 which throws errors in the script.

When I run the same script on the command line it exits with code 0.

The compare command does return things via stderr, it seems by default, but I don't need that output and if it needs to be, can be suppressed. I've tried adding -quiet with no difference.

Here's the Node script as child-process.spawn (again I tried using child-process.exec as well):

var spawn = require("child-process-promise").spawn,
    filenameA = "img0.png",
    filenameB = "img1.png",
    filenameO = "img.0-1.png";

    var p = spawn('compare', [filenameA, filenameB, '-fuzz', 20, '-highlight-color', "#ffffff", '-lowlight-color', "#000000", filenameO])
        .progress(function (childProcess) {
            console.log('[spawn] childProcess.pid: ', childProcess.pid);
            childProcess.stdout.on('data', function (data) {
                console.log('[spawn] stdout: ', data.toString());
            });
            childProcess.stderr.on('data', function (data) {
                console.log('[spawn] stderr: ', data.toString());
            });
        })
        .then(function(){
            console.log("completed", filenameO);
        })
        .fail(function (err) {
            console.error('[spawn] ERROR: ', err);
        });

Again, I get the generated image, and it looks correct, but the process exits with code 1 (an error occurred).

Output looks like:

[spawn] childProcess.pid:  55002
[spawn] ERROR:  { code: 1,
  message: '`compare img0.png img1.png -fuzz 20 -highlight-color #ffffff -lowlight-color #000000 img.0-1.png` failed with code 1' }

Result from the command line:

$ compare img0.png img1.png -fuzz 20 \
           -highlight-color #ffffff -lowlight-color #000000 img.0-1.png
$ echo $?
> 0

EDIT: The reason that exiting with a code other than 0 was a problem had to do with the child-process-promise node module assuming that any non-zero exit code means error. While that is somewhat standard, it is not formally standard, and Imagemagick can exit normally with non-zero codes.


Solution

  • I don't know much about running stuff in Node.js, but here is a way that may help you find a workaround:

    You can run compare without actually generating a "delta" image, but instead just return one of the supported metric results. A metric is a simple number indicating the differences between two images. To see a list of available metrics, see

    compare -list metric
    

    I'd recommend you to look that the AE metric first. This one essentially gives you the count of pixels which are different between the two input images.

    To suppress the generation of a "delta" image, just use the special name null: as the output file name.

    Example

    $ convert wizard: wizard.jpg              # Generate first image
    $ convert wizard: wizard.png              # Generate a similar, but different image
    $ compare wizard.{jpg,png} delta.pdf      # "Classical" run of `compare`
    $ compare wizard.{jpg,png} null:          # No output image, no `metric` either...
    

    Now let's introduce -metric into the picture:

    $ compare -metric AE wizard.{j,p}* delta.pdf      # "delta" image AND metric output
    $ compare -metric AE wizard.{j,p}* null:          # no image, only metric output
    

    In this case, the last command outputs this:

    $ compare -metric AE wizard.{j,p}* null: 
        122473
    
    $ echo $?
        1
    
    $ compare -fuzz 20% -metric AE w.{j,p}* null:
        0
    
    $ echo $?
        0
    

    So even in the terminal you get exit code 1 if your images do exhibit pixel differences. The advantage of looking for the value -metric AE returns is that you get a quantification of the differences.

    Your code could still branch for these cases where you want to generate a delta image, if the pixel differences are too large.


    Update

    The man page for compare has this to say:

    The compare program returns 2 on error otherwise 0 if the images are similar or 1 if they are dissimilar.