code changs for multiple protobuf models 68/1168/1
authorEric Z <ezavesky@research.att.com>
Fri, 2 Mar 2018 15:49:48 +0000 (09:49 -0600)
committerEric Z <ezavesky@research.att.com>
Fri, 2 Mar 2018 15:49:48 +0000 (09:49 -0600)
- missed supplement to #1165, I24737d7b2bc6edcbb149d3d82e02dee20d1c61ac
- modify options to accomodate multiple proto destinations
- add both pixelate and detect as destination for face demo
- add multiple proto definitions for face demo

Change-Id: Ie0b6262c7a21bb1738fe8610f5d3d26c17880869
Signed-off-by: Eric Z <ezavesky@research.att.com>
docs/tutorials/lesson3.md
face_privacy_filter/filter_image.py
web_demo/face-privacy.html
web_demo/face-privacy.js

index 39390a1..519f3c7 100644 (file)
@@ -13,7 +13,8 @@ into the browser and accesing a locally hosted server API, as configured
 in [the previous tutorial](lesson2.md).
 
 If you want to run the test locally, you can use the built-in python
-webserver with the line below while working in the `web_demo` directory.
+webserver with the line below while working in the `web_demo` directory
+(assuming you're running python3).
 ```
 python -m http.server 5000
 ```
@@ -51,9 +52,12 @@ image that was sent to the remote service.  When available, the <strong>Download
 button will be enabled and a binary file will be generated in the browser.
 
 ```
-protoc --decode=sapLzHrujUMPBGCBEMWQFxEIMsxocFrG.FaceImage model.proto < protobuf.bin
+protoc --decode=sapLzHrujUMPBGCBEMWQFxEIMsxocFrG.FaceImage model.pixelate.proto < protobuf.bin
 ```
 
+**NOTE** The specific package name may have changed since the time of writing,
+so be sure to check the contents of the current `.proto` file.
+
 
 ## Example mood classification demo (HTTP parameters)
 To customize this demo, one should change either the included javascript
index e7d4c12..762c56e 100644 (file)
@@ -28,7 +28,7 @@ import numpy as np
 import pandas as pd
 
 
-def model_create_pipeline(transformer):
+def model_create_pipeline(transformer, funcName):
     from acumos.session import Requirements
     from acumos.modeling import Model, List, create_namedtuple
     import sklearn
@@ -55,7 +55,9 @@ def model_create_pipeline(transformer):
 
     # compute path of this package to add it as a dependency
     package_path = path.dirname(path.realpath(__file__))
-    return Model(transform=predict_class), Requirements(packages=[package_path], reqs=[pd, np, sklearn],
+    objModelDeclare = {}
+    objModelDeclare[funcName] = predict_class
+    return Model(**objModelDeclare), Requirements(packages=[package_path], reqs=[pd, np, sklearn],
                                                         req_map={cv2: 'opencv-python'})
 
 
@@ -86,7 +88,7 @@ def main(config={}):
         else:
             print("Error: Functional mode '{:}' unknown, aborting create".format(config['function']))
         inputDf = transform.generate_in_df()
-        pipeline, reqs = model_create_pipeline(transform)
+        pipeline, reqs = model_create_pipeline(transform, config['function'])
 
         # formulate the pipeline to be used
         model_name = MODEL_NAME + "_" + config['function']
index abe8e72..4d7f17d 100755 (executable)
@@ -67,9 +67,10 @@ Rewrite to utilize simple image-based processing steps.
 <form action="javascript:void(0);">
 <table class="tableConfig">
     <tr><td><label for="serverUrl">Transform URL:</label></td>
-        <td><input type="text" name="serverUrl" id="serverUrl" size="60" /></td></tr>
+        <td><input type="text" name="serverUrl" id="serverUrl" size="60" />
+        <br /><span class="tiny"><em>Note: The endpoint url may be modified when a new method below is utilized.</em></span></td></tr>
     <tr><td><label for="protoMethod">Protobuf Method:</label></td>
-        <td><select name="protoMethod" id="protoMethod" ><option value="">(disable, not loaded)</option></select></td></tr>
+        <td><select name="protoMethod" id="protoMethod" ><option value="">(disabled, not loaded)</option></select></td></tr>
     <tr><td><label for="protoButton">Protobuf Message:</label></td>
         <td><button type="button" name="protoInput" id="protoInput" >Download Encoded Input</button> -
         <button type="button" name="protoOutput" id="protoOutput" >Download Encoded Response</button></td></tr>
index a367ac7..e1a597a 100644 (file)
@@ -96,18 +96,26 @@ $(document).ready(function() {
     imageLoader.addEventListener('change', handleImage, false);
 
     //if protobuf is enabled, fire load event for it as well
-    protobuf.load("model.proto", function(err, root) {
+    $(document.body).data('hdparams').protoObj = {};  //clear from last load
+    protobuf_load("model.pixelate.proto", true);
+    protobuf_load("model.detect.proto");
+
+    //trigger first click
+    $("#sourceRibbon div")[0].click();
+});
+
+
+function protobuf_load(pathProto, forceSelect) {
+    protobuf.load(pathProto, function(err, root) {
         if (err) {
             console.log("[protobuf]: Error!: "+err);
             throw err;
         }
-        $(document.body).data('hdparams').protoObj = null;  //clear from last load
-        var numMethods = 0;
+        var domSelect = $("#protoMethod");
+        var numMethods = domSelect.children().length;
         $.each(root.nested, function(namePackage, objPackage) {    // walk all
             if ('Model' in objPackage && 'methods' in objPackage.Model) {    // walk to model and functions...
                 var typeSummary = {'root':root, 'methods':{} };
-                var domSelect = $("#protoMethod");
-                domSelect.empty();
                 $.each(objPackage.Model.methods, function(nameMethod, objMethod) {  // walk methods
                     typeSummary['methods'][nameMethod] = {};
                     typeSummary['methods'][nameMethod]['typeIn'] = namePackage+'.'+objMethod.requestType;
@@ -115,28 +123,26 @@ $(document).ready(function() {
                     typeSummary['methods'][nameMethod]['service'] = namePackage+'.'+nameMethod;
 
                     //create HTML object as well
-                    var domOpt = $("<option />").attr("value", nameMethod).text(
+                    var namePretty = namePackage+"."+nameMethod;
+                    var domOpt = $("<option />").attr("value", namePretty).text(
                         nameMethod+ " (input: "+objMethod.requestType
                         +", output: "+objMethod.responseType+")");
                     if (numMethods==0) {    // first method discovery
                         domSelect.append($("<option />").attr("value","").text("(disabled, not loaded)")); //add 'disabled'
+                    }
+                    if (forceSelect) {
                         domOpt.attr("selected", 1);
                     }
                     domSelect.append(domOpt);
                     numMethods++;
                 });
-                $(document.body).data('hdparams').protoObj = typeSummary;   //save new method set
+                $(document.body).data('hdparams').protoObj[namePackage] = typeSummary;   //save new method set
                 $("#protoContainer").show();
             }
         });
         console.log("[protobuf]: Load successful, found "+numMethods+" model methods.");
     });
-
-
-    //trigger first click
-    $("#sourceRibbon div")[0].click();
-});
-
+}
 
 /**
  * Called after a new video has loaded (at least the video metadata has loaded)
@@ -171,9 +177,14 @@ function nextFrame() {
     switchImage(hd.video, true);
 }
 
-function updateLink(domId) {
+function updateLink(domId, newServer) {
     var sPageURL = decodeURIComponent(window.location.search.split('?')[0]);
-    var newServer = $(document.body).data('hdparams')['classificationServer'];
+    if (newServer==undefined) {
+        newServer = $(document.body).data('hdparams')['classificationServer'];
+    }
+    else {
+        $("#serverUrl").val(newServer);
+    }
     var sNewUrl = sPageURL+"?url-image="+newServer;
     $("#"+domId).attr('href', sNewUrl);
 }
@@ -270,14 +281,24 @@ function getUrlParameter(sParam) {
 function doPostImage(srcCanvas, dstImg, dataPlaceholder) {
     var dataURL = srcCanvas.toDataURL('image/jpeg', 1.0);
     var hd = $(document.body).data('hdparams');
-    var serviceURL = hd.classificationServer;
     var sendPayload = null;
+
+    var nameProtoMethod = $("#protoMethod option:selected").attr('value');
+    var methodKeys = null;
+    if (nameProtoMethod && nameProtoMethod.length) {     //valid protobuf type?
+        var partsURL = hd.classificationServer.split("/");
+        methodKeys = nameProtoMethod.split(".", 2);       //modified for multiple detect/pixelate models
+        partsURL[partsURL.length-1] = methodKeys[1];
+        hd.classificationServer = partsURL.join("/");   //rejoin with new endpoint
+        updateLink("serverLink", hd.classificationServer);
+    }
+
+    var serviceURL = hd.classificationServer;
     var request = new XMLHttpRequest();     // create request to manipulate
     request.open("POST", serviceURL, true);
 
-    var nameProtoMethod = $("#protoMethod option:selected").attr('value');
     //console.log("[doPostImage]: Selected method ... '"+typeInput+"'");
-    if (nameProtoMethod.length) {     //valid protobuf type?
+    if (nameProtoMethod && nameProtoMethod.length) {     //valid protobuf type?
         var blob = dataURItoBlob(dataURL, true);
 
         // fields from .proto file at time of writing...
@@ -285,10 +306,12 @@ function doPostImage(srcCanvas, dstImg, dataPlaceholder) {
         //      repeated string mime_type = 1;   -> becomes "mimeType" (NOTE repeated type)
         //      repeated bytes image_binary = 2; -> becomes "imageBinary"
         //    }
+
+        //TODO: should we always assume this is input? answer: for now, YES, always image input!
         var inputPayload = { "mimeType": [blob.type], "imageBinary": [blob.bytes] };
 
         // ---- method for processing from a type ----
-        var msgInput = hd.protoObj['root'].lookupType(hd.protoObj['methods'][nameProtoMethod]['typeIn']);
+        var msgInput = hd.protoObj[methodKeys[0]]['root'].lookupType(hd.protoObj[methodKeys[0]]['methods'][methodKeys[1]]['typeIn']);
         // Verify the payload if necessary (i.e. when possibly incomplete or invalid)
         var errMsg = msgInput.verify(inputPayload);
         if (errMsg) {
@@ -350,7 +373,7 @@ function doPostImage(srcCanvas, dstImg, dataPlaceholder) {
     hd.imageIsWaiting = true;
     request.onreadystatechange=function() {
         if (request.readyState==4 && request.status>=200 && request.status<300) {
-            if (nameProtoMethod.length) {     //valid protobuf type?
+            if (methodKeys!=null) {     //valid protobuf type?
                 //console.log(request);
                 var bodyEncodedInString = new Uint8Array(request.response);
                 //console.log(bodyEncodedInString);
@@ -359,7 +382,7 @@ function doPostImage(srcCanvas, dstImg, dataPlaceholder) {
                 hd.protoPayloadOutput = bodyEncodedInString;
 
                 // ---- method for processing from a type ----
-                var msgOutput = hd.protoObj['root'].lookupType(hd.protoObj['methods'][nameProtoMethod]['typeOut']);
+                var msgOutput = hd.protoObj[methodKeys[0]]['root'].lookupType(hd.protoObj[methodKeys[0]]['methods'][methodKeys[1]]['typeOut']);
                 var objRecv = msgOutput.decode(hd.protoPayloadOutput);
                 //console.log(objRecv);
                 hd.protoRes = objRecv;
@@ -381,22 +404,28 @@ function doPostImage(srcCanvas, dstImg, dataPlaceholder) {
                     });
                     domTable = $("<table />").append(domTable);     // create embedded table
 
-                    domResult.empty().append($("<strong />").html("Results")).show();
                     var idxImg = -1;
-                    for (var i=0; i<objRecv.region.length; i++) {       //find the right region
-                        if (objRecv.region[i]==-1) {                    //special indicator for original image
-                            idxImg = i;
+                    if ('region' in msgOutput.fields) {             // did we get regions?
+                        for (var i=0; i<objRecv.region.length; i++) {       //find the right region
+                            if (objRecv.region[i]==-1) {                    //special indicator for original image
+                                idxImg = i;
+                            }
+                            var domRow = $("<tr />");
+                            var strDisplay = [];
+                            $.each(arrNames, function(idx, name) {      //collect data from each column
+                                domRow.append($("<td />").html(objRecv[name][i]));
+                            });
+                            domTable.append(domRow);
+                            //domResult.append($("div").html(objRecv.region));
                         }
-                        var domRow = $("<tr />");
-                        var strDisplay = [];
-                        $.each(arrNames, function(idx, name) {      //collect data from each column
-                            domRow.append($("<td />").html(objRecv[name][i]));
-                        });
-                        domTable.append(domRow);
-                        //domResult.append($("div").html(objRecv.region));
+                        domResult.empty().append($("<strong />").html("Results")).show();
+                        domResult.append(domTable);
+                    }
+                    else {                                  //got images, get that chunk directly
+                        idxImg = 0;
                     }
-                    domResult.append(domTable);
-                    if (idxImg != -1) {
+
+                    if (idxImg != -1) {                     //got any valid image? display it
                         //console.log(objRecv.mimeType[idxImg]);
                         //console.log(objRecv.imageBinary[idxImg]);
                         //var strImage = Uint8ToString(objRecv.imageBinary[idxImg]);