bonastreyair/node-red-contrib-teachable-machine

View on GitHub
teachable_machine.html

Summary

Maintainability
Test Coverage
<!-- Node Configuration Templates -->
<script type="text/html" data-template-name="teachable machine">
    <div class="form-row">
        <label for="node-input-mode"><i class="fa fa-file" style="margin-right: 6px"></i>Mode</label>
        <select style="width: 160px;" type="text" id="node-input-mode">
            <option value="online" selected> Online</option>
            <option value="local"> Local</option>
        </select>
    </div>

    <div class="node-row-msg-modelUri">
        <div class="form-row node-row-modelUri">
            <label for="node-input-modelUri"><i class="fa fa-link" style="margin-right: 6px;"></i>URI</label>
            <input type="text" id="node-input-modelUri" placeholder="https://teachablemachine.withgoogle.com/model/[...]">
        </div>
    </div>

    <div class="form-row">
        <label for="node-input-output"><i class="fa fa-sign-out" style="margin-right: 6px;"></i>Output</label>
        <select style="width: 160px;" type="text" id="node-input-output">
            <option value="best" selected>Best prediction</option>
            <option value="all">All predictions</option>
        </select>
    </div>

    <div class="node-row-msg-filters">
        <div class="form-row">
            <label for="node-input-filters"><i class="fa fa-filter" style="margin-right: 6px;"></i>Filters</label>
                <label for="node-input-threshold" style="width:70%;">
                    <input type="checkbox" id="node-input-activeThreshold" style="display:inline-block; width:22px; vertical-align:baseline;">
                    <label style="width:65px;">threshold</label>
                    <input type="text" id="node-input-threshold" style="width: 40px;" placeholder="80"> %
                </label>
        </div>
        <div class="form-row">
            <label for="node-input-filters"></label>
                <label for="node-input-threshold" style="width:70%;">
                    <input type="checkbox" id="node-input-activeMaxResults" style="display:inline-block; width:22px; vertical-align:baseline;">
                    <label style="width:65px;">maximum</label>
                    <input type="text" id="node-input-maxResults" style="width: 40px;" placeholder="3"> predictions
                </label>
        </div>
    </div>

    <div class="form-row">
        <label for="node-input-passThrough"><i class="fa fa-picture-o" style="margin-right: 6px;"></i>Image</label>
        <input type="checkbox" id="node-input-passThrough" style="display:inline-block; width:22px; vertical-align:baseline;">
        save original image in <code>msg.image</code>.
    </div>

    <div class="form-row">
        <label for="node-input-name"><i class="fa fa-tag" style="margin-right: 6px;"></i>Name</label>
        <input type="text" id="node-input-name" placeholder="Name">
    </div>
</script>

<!-- Information Help Menu -->
<script type="text/html" data-help-name="teachable machine">
    <p>A <a href="https://www.tensorflow.org/js" target = "_new">tensorflow.js</a> node to run custom trained <a href="https://teachablemachine.withgoogle.com" target = "_new">Teachable Machine</a> image classification online models.</p>
    <h3>Inputs</h3>
        <dl class="message-properties">
            <dt>payload<span class="property-type">image buffer</span></dt>
                <dd>A binary buffer of an image (jpeg or png)</dd>
            <dt>reload<span class="property-type">bool</span></dt>
                <dd>Set to true to reload the model. In the same time, ignore image in payload (if any) and do not perform image classification.</dd>
        </dl>
    <h3>Outputs</h3>
        <dl class="message-properties">
            <dt>payload<span class="property-type">array</span></dt>
                <dd>Objects in the array are ordered by descending probability and have the following properties:
                    <ul>
                        <li><code>nameClass</code> <i>string</i></li>
                        <li><code>probability</code> <i>float (0-1)</i></li>
                    </ul>
                </dd>
            <dt>classes<span class="property-type">array</span></dt>
                <dd>All the possible class names used in the model.</dd>
        </dl>

    <h3>Configuration</h3>
    <h4><i class="fa fa-file" style="margin-right: 6px"></i>Mode</h4>
        <p>You can configure how to load the model.</p>
        <ul>
            <li><code>Online</code> from the teachable machine URL</li>
            <li><code>Local</code> from the local URI (where the model.json, metadata.json and model.weights.bin exist)</li>
        </ul>
        <p>If you select the <code>Online</code> mode, you must set the URL obtained from the uploaded model in the <a href="https://teachablemachine.withgoogle.com" target = "_new">Teachable Machine</a> webpage.</p>

    <h4><i class="fa fa-sign-out" style="margin-right: 6px"></i>Output</h4>
        <p>You can configure how the <code>msg.payload</code> is generated.</p>
        <ul>
            <li><code>Best prediction</code> only the top-1 classification result</li>
            <li><code>All predictions</code> all predictions with enabled filters</li>
        </ul>
    <h4><i class="fa fa-filter" style="margin-right: 6px"></i>Filters</h4>
        <p>You can enable or disable any of the filters when <code>All predictions</code> Output mode is selected. Filters are all aplied if enabled.
        <ul>
            <li><code>Threshold</code> filters only the results that are above the threshold (0-100)</li>
            <li><code>Max. Predictions</code> limits the results to any number (1-5)</li>
        </ul>
</script>

<!-- Node JavaScript Registration -->
<script type="text/javascript">
  RED.nodes.registerType('teachable machine',
  {
    category: 'analysis-function',
    color: '#ED782F',
    defaults: {
        name: { value: '' },
        mode: { value: 'online' },
        modelUri: { value: '', required: true },
        localModel: { value: 'teachable_model' },
        output: { value: 'best', required: true },
        activeThreshold: { value: false },
        threshold: { value: 80, validate: function(v) { return RED.validators.number(v) && (v > 0) && (v < 100)}},
        activeMaxResults: { value: false },
        maxResults: { value: 3, validate: function(v) { return RED.validators.number(v) && (v > 0) && (v < 5)}},
        passThrough: { value: false }
    },
    inputs: 1,
    outputs: 1,
    icon: 'tensorflow_logo.png',
    label: function () {
      return this.name || 'teachable machine'
    },
    oneditprepare: function() {
        $("#node-input-threshold").spinner({ min: 0, max: 100})
        $("#node-input-maxResults").spinner({ min: 1, max: 5})
        $("#node-input-output").on("change", function(e) {
            var val = $(this).val()
            $(".node-row-msg-filters").toggle(val==="all")
        })
    }
  })
</script>