Source: org/terraswarm/accessor/accessors/web/image/AprilTags.js

// Copyright (c) 2015-2016 The Regents of the University of California.
// All rights reserved.
//
// Permission is hereby granted, without written agreement and without
// license or royalty fees, to use, copy, modify, and distribute this
// software and its documentation for any purpose, provided that the above
// copyright notice and the following two paragraphs appear in all copies
// of this software.
//
// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
// ENHANCEMENTS, OR MODIFICATIONS.
//

/**
 * Accessor to detect AprilTags in an image or stream of images.
 * An AprilTag is a pattern of dark and light squares similar to a QR code but easier for
 * cameras to detect robustly and at a distance.
 * AprilTags were created by Associate Professor Edwin Olson (ebolson@umich.edu),
 * EECS, University of Michigan. See [https://april.eecs.umich.edu/](https://april.eecs.umich.edu/#in_browser).
 * 
 * The implementation of this accessor on the Ptolemy II/Nashorn accessor host
 * uses an older Java implementation of the AprilTags detector written by Edwin Olson
 * and more recently supplanted by a C version that performs much better. But this Java
 * version is more easily included in Ptolemy II in a portable way. If you need better
 * performance, consider replacing this with the C implementation and using JNI to interface
 * to Ptolemy II.
 * 
 * https://april.eecs.umich.edu/software/apriltag.html contains a set of
 * pregenerated tags as png and PostScript files.  However, these are of
 * low resolution.  To scale them, use linear interpolation to avoid blurring.
 * For example, with ImageMagik, use:
 *
 *  mogrify -scale 1000x1000 *.png; convert *.png tag36h11.pdf
 *
 * Or, search the web for "tag 36H11".  
 *
 * In the Ptolemy tree, a sample file may be found at
 * $PTII/ptolemy/actor/lib/jjs/modules/aprilTags//demo/AprilTags/tag36_11_00586.pdf
 *
 * The input to this accessor is an image or a stream of images, e.g. from the Camera
 * accessor.  There are two outputs. The one named _output_ is a modified version
 * of the input image that outlines any detected AprilTags in the image
 * and indicates their center and ID.  The _tags_ output is an array of
 * objects representing the detected tags. Each object includes the following fields:
 * 
 * + _id_: The ID of the detected tag.
 * + _center_: An array with two doubles giving the center of the tag in pixel coordinates.
 * + _perimeter_: An array with four arrays, each of which gives the x and y coordinates of
 *   a corner of the AprilTag.
 * 
 * The AprilTags detector has a large number of parameters that can be tuned via
 * the _options_ input. To set an option, provide a JSON object with a field matching
 * the option name.  The options are described below using descriptions provided by
 * by Edwin Olson in his Java implementation of an AprilTag detector:
 * 
 * + _MagThresh_: When growing components, the intra component variation is
 *   allowed to grow when the component is small in size. This
 *   threshold affects how much. The default is 1200.
 * + _MaxEdgeCost_: Set the maximum angle range allowed for the gradient directions
 *   when connecting edges, in radians. This defaults to the radian
 *   equivalent of 30 degrees.
 * + _MinMag_: Set the gradient magnitude threshold for ignoring pixels.
 *   Do not consider pixels whose gradient magnitude is less than
 *   minMag. Small values make the detector more sensitive, but also
 *   force us to consider many more edges resulting in slower
 *   computation time. A value of 0.001 is very sensitive. A value
 *   of 0.01 is quite fast. The default is 0.004.
 * + _SegDecimate_: Set whether decimating before segmenting is enabled.
 *   Instead of blurring the input image before segmentation, we
 *   can achieve similar effects by decimating the image by a factor
 *   of two. When enabled, this option applies a block LPF filter of
 *   width 2, then decimates the image. With this option, not only
 *   can we safely set segSigma = 0, but the slowest part of the
 *   algorithm (the segmentation) runs about 4 times faster. The
 *   downside is that the position of the targets is determined
 *   based on the segmentation: lower resolution will result in more
 *   localization error. However, the effect on quality is quite
 *   modest, and this optimization is generally recommended (along
 *   with segSigma = 0). If segSigma is non-zero, the filtering by
 *   segSigma occurs first, followed by the block LPF, and the
 *   decimation. This defaults to false, indicating that the option
 *   is not enabled.
 * + _SegSigma_: Set the Gaussian smoothing kernel applied to image (0 == no filter)
 *   used when detecting the outline of the box. It is almost always
 *   useful to have some filtering, since the loss of small details
 *   won't hurt. Recommended value = 0.8 (the default). The case where sigma ==
 *   segsigma has been optimized to avoid a redundant filter
 *   operation.
 * + _Sigma_: Set the Gaussian smoothing kernel applied to image (0 == no filter, the default)
 *   used when sampling bits. Filtering is a good idea in cases
 *   where A) a cheap camera is introducing artifical sharpening, B)
 *   the bayer pattern is creating artifcats, C) the sensor is very
 *   noisy and/or has hot/cold pixels. However, filtering makes it
 *   harder to decode very small tags. Reasonable values are 0, or
 *   [0.8, 1.5].
 * + _TagFamily_: Set the name of the tag family being detected.
 *   This defaults to "Tag36h11".
 *   The supported families are "Tag16h5", "Tag25h7", "Tag25h9", "Tag36h10", and "Tag36h11".
 *   The default family seems least susceptible to false positives.
 * + _ThetaThresh_: When growing components, the intra component variation is
 *   allowed to grow when the component is small in size. This
 *   threshold affects how much. The default is 100.
 * 
 *   @accessor image/AprilTags
 *   @author Edward A. Lee (eal@eecs.berkeley.edu)
 *   @input input An input image.
 *   @output output An output image, with detected AprilTags outlined in green and identified.
 *   @output tags An array of objects, one object for each tag detected in the image.
 *   @parameter options The options for the detector. This is a JSON object with fields defined above.
 *   It defaults to an empty object, meaning to use default values for all the otpions.
 *   @version $$Id$$
 */

// Stop extra messages from jslint.  Note that there should be no
// space between the / and the * and global.
/*globals addInputHandler, exports, get, input, output, removeInputHandler, require, send,  */
/*jshint globalstrict: true */
"use strict";

var aprilTags = require('aprilTags');

exports.setup = function () {
    this.input('input');
    this.output('output');
    this.output('tags');
    this.input('options', {
        'type': 'JSON',
        'value': ''
    });
};

var handle;
exports.initialize = function () {
    var self = this;
    handle = self.addInputHandler('input', function () {
        var options = self.get('options');
        var image = self.get('input');
        var result = aprilTags.filter(image, options);
        self.send('output', result);
        var tags = aprilTags.tags();
        if (tags) {
            self.send('tags', tags);
        }
    });
};

exports.wrapup = function () {
    if (handle) {
        this.removeInputHandler(handle);
    }
};