update demo page and add object drawing capability
[face-privacy-filter.git] / web_demo / face-privacy.js
1 /*
2   ===============LICENSE_START=======================================================
3   Acumos Apache-2.0
4   ===================================================================================
5   Copyright (C) 2017-2018 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
6   ===================================================================================
7   This Acumos software file is distributed by AT&T and Tech Mahindra
8   under the Apache License, Version 2.0 (the "License");
9   you may not use this file except in compliance with the License.
10   You may obtain a copy of the License at
11
12   http://www.apache.org/licenses/LICENSE-2.0
13
14   This file is distributed on an "AS IS" BASIS,
15   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   See the License for the specific language governing permissions and
17   limitations under the License.
18   ===============LICENSE_END=========================================================
19 */
20 /**
21  face-privacy.js - send frames to an face privacy service; clone from image-classes.js
22
23  Videos or camera are displayed locally and frames are periodically sent to GPU image-net classifier service (developed by Zhu Liu) via http post.
24  For webRTC, See: https://gist.github.com/greenido/6238800
25
26  D. Gibbon 6/3/15
27  D. Gibbon 4/19/17 updated to new getUserMedia api, https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
28  D. Gibbon 8/1/17 adapted for system
29  E. Zavesky 10/19/17 adapted for video+image
30  E. Zavesky 05/05/18 adapted for row-based image and other results
31  E. Zavesky 05/30/18 forked model generic code to `demo-framework.js`, switch to flat image
32  */
33
34 "use strict";
35
36 /**
37  * main entry point
38  */
39
40 // called one time when document is ready
41 $(document).ready(function() {
42     var urlDefault = getUrlParameter('url-image');
43     if (!urlDefault)
44         urlDefault = "http://localhost:8884/classify";
45     demo_init({
46         classificationServer: urlDefault,
47         protoList: [["model.pixelate.proto", true], ["model.detect.proto", false], ["model.recognize.proto", false] ],
48         mediaList: [
49             {
50                 'img': 'images/face_reunion.jpg',
51                 'source': 'https://flic.kr/p/bEgYbs',
52                 'name': 'reuninon (flickr)'
53             },
54             {
55                 'img': 'images/face_family.jpg',
56                 'source': 'https://www.pexels.com/photo/adult-affection-beautiful-beauty-265764',
57                 'name': 'family (pexels)'
58             },
59             {
60                 'img': 'images/commercial.jpg',
61                 'movie': "images/commercial.mp4",
62                 'source': 'https://www.youtube.com/watch?v=34KfCNapnUg',
63                 'name': 'family (pexels)'
64             },
65             {
66                 'img': 'images/face_Schwarzenegger.jpg',
67                 'source': 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/0f/A._Schwarzenegger.jpg/220px-A._Schwarzenegger.jpg',
68                 'name': 'Schwarzenegger (wikipedia)'
69             },            {
70                 'img': 'images/face_DeGeneres.jpg',
71                 'source': 'https://en.wikipedia.org/wiki/Ellen_DeGeneres#/media/File:Ellen_DeGeneres-2009.jpg',
72                 'name': 'DeGeneres (wikipedia)'
73             },
74         ]
75     });
76 });
77
78
79
80 // what do we do with a good processing result?
81 //
82 //  data: the raw body from the response
83 //  dstImg: the dom element of a destination image
84 //  methodKeys: which protomethod was selected
85 //  dstImg: the dom element of a destination image (if available)
86 //  imgPlaceholder: the exported canvas image from last source
87 //
88 function processResult(data, dstDiv, methodKeys, dstImg, imgPlaceholder) {
89     var hd = $(document.body).data('hdparams');
90     if (methodKeys) {
91         //console.log(request);
92         var bodyEncodedInString = new Uint8Array(data);
93         //console.log(bodyEncodedInString);
94         //console.log(bodyEncodedInString.length);
95         $("#protoOutput").prop("disabled",false);
96         hd.protoPayloadOutput = bodyEncodedInString;
97
98         // ---- method for processing from a type ----
99         var msgOutput = hd.protoObj[methodKeys[0]]['root'].lookupType(hd.protoObj[methodKeys[0]]['methods'][methodKeys[1]]['typeOut']);
100         var objOutput = null;
101         try {
102             objOutput = msgOutput.decode(hd.protoPayloadOutput);
103         }
104         catch(err) {
105             var errStr = "Error: Failed to parse protobuf response, was the right method chosen? (err: "+err.message+")";
106             console.log(errStr);
107             dstDiv.html(errStr);
108             return false;
109         }
110         var nameRepeated = null;
111
112         // NOTE: this code expects one top-level item to be an array of nested results
113         //  e.g.   Image{mime_type, image_binary}
114         //  e.g.   DetectionFrameSet [ DetectionFrame{x, y, ...., mime_type, image_binary}, .... ]
115
116         //try to crawl the fields in the protobuf....
117         var numFields = 0;
118         $.each(msgOutput.fields, function(name, val) {           //collect field names
119             if (val.repeated) {     //indicates it's a repeated field (likely an array)
120                 nameRepeated = name;      //save this as last repeated field (ideally there is just one)
121             }
122             numFields += 1;
123         });
124
125         var typeNested = methodKeys[0]+"."+msgOutput.name;
126         if (nameRepeated) {
127             objOutput = objOutput[nameRepeated];  // dereference neseted object
128             typeNested = methodKeys[0]+"."+msgOutput.fields[nameRepeated].type;
129         }
130         else {
131             objOutput = [objOutput];    // simple singleton wrapper for uniform code below
132         }
133
134         //grab the nested array type and print out the fields of interest
135         var msgOutputNested = hd.protoObj[methodKeys[0]]['root'].lookupType(typeNested);
136         //console.log(msgOutputNested);
137         var domTable = $("<tr />");
138         var arrNames = [];
139         $.each(msgOutputNested.fields, function(name, val) {           //collect field names
140             var nameClean = val.name;
141             if (nameClean != 'imageBinary') {
142                 domTable.append($("<th />").html(nameClean));
143                 arrNames.push([nameClean, val.repeated]);
144             }
145         });
146         domTable = $("<table />").append(domTable);     // create embedded table
147
148         // loop through all members of array to do two things:
149         //  (1) find the biggest/best image
150         //  (2) print out the textual fields
151         var objBest = null;
152         $.each(objOutput, function(idx, val) {
153             if ('imageBinary' in val) {
154                 // at this time, we only support ONE output image, so we will loop through
155                 //  to grab the largest image (old code could grab the one with region == -1)
156                 if (objBest==null || val.imageBinary.length>objBest.imageBinary.length) {
157                     objBest = val;
158                 }
159             }
160
161             var domRow = $("<tr />");
162             $.each(arrNames, function(idx, field_data) {      //collect data from each column
163                 //add safety to avoid printing repeated rows!
164                 domRow.append($("<td />").html(!field_data[1] ?
165                     val[field_data[0]] : val[field_data[0]].length + " items"));
166             });
167             if (val.x && val.y) {  //valid bounding box examples?
168                 canvas_rect(false, val.x, val.y, val.w, val.h, hd.colorSet[idx % hd.colorSet.length]);
169                 domRow.children(":nth-child(2)").append($("<div class='colorblock'/>").css(
170                     "background-color", hd.colorSet[idx % hd.colorSet.length]));
171             }
172             domTable.append(domRow);
173         });
174         dstDiv.empty().append($("<strong />").html("Results")).show();
175         dstDiv.append(domTable);
176
177         //did we find an image? show it now!
178         if (objBest != null) {
179             //some images are too big for direct btoa/array processing...
180             //dstImg.attr('src', "data:"+objBest.mimeType+";base64,"+strImage).removeClass('workingImage');
181             dstImg.attr('src', BlobToDataURI(objBest.imageBinary, objBest.mimeType)).removeClass('workingImage');
182         }
183         else if (imgPlaceholder) {
184             dstImg.attr('src', imgPlaceholder).removeClass('workingImage');
185         }
186         else {
187             var errStr = "Error: No valid image data was found and no placeholder, aborting display.";
188             console.log(errStr);
189             dstDiv.html(errStr);
190             return false;
191         }
192     }
193     else {       //legacy code where response was in base64 encoded image...
194         var responseJson = $.parseJSON(data);
195         var respImage = responseJson[0];
196         // https://stackoverflow.com/questions/21227078/convert-base64-to-image-in-javascript-jquery
197         dstImg.attr('src', "data:"+respImage['mime_type']+";base64,"+respImage['image_binary']).removeClass('workingImage');
198     }
199 }
200
201