- add auth arg
[face-privacy-filter.git] / web_demo / face-privacy.js
1 /**\r
2  face-privacy.js - send frames to an face privacy service\r
3 \r
4  Videos or camera are displayed locally and frames are periodically sent to GPU image-net classifier service (developed by Zhu Liu) via http post.\r
5  For webRTC, See: https://gist.github.com/greenido/6238800\r
6 \r
7  D. Gibbon 6/3/15\r
8  D. Gibbon 4/19/17 updated to new getUserMedia api, https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia\r
9  D. Gibbon 8/1/17 adapted for system\r
10  E. Zavesky 10/19/17 adapted for video+image\r
11  */\r
12 \r
13 "use strict";\r
14 \r
15 /**\r
16  * main entry point\r
17  */\r
18 $(document).ready(function() {\r
19     var urlDefault = getUrlParameter('url-image');\r
20     if (!urlDefault)\r
21         urlDefault = "http://localhost:8884/transform";\r
22 \r
23         $(document.body).data('hdparams', {     // store global vars in the body element\r
24                 classificationServer: urlDefault,\r
25                 frameCounter: 0,\r
26                 frameInterval: 500,             // Milliseconds to sleep between sending frames to reduce server load and reduce results updates\r
27                 frameTimer: -1,         // frame clock for processing\r
28                 imageIsWaiting: false,  // blocking to prevent too many queued frames\r
29                 // Objects from DOM elements\r
30                 srcImgCanvas: document.getElementById('srcImgCanvas'),  // we have a 'src' source image\r
31                 destImg: document.getElementById('destImg'),    // we have a 'src' source image\r
32                 video: document.getElementById('srcVideo'),\r
33         });\r
34     $(document.body).data('hdparams')['canvasMaxH'] = $(document.body).data('hdparams')['srcImgCanvas'].height;\r
35     $(document.body).data('hdparams')['canvasMaxW'] = $(document.body).data('hdparams')['srcImgCanvas'].width;\r
36 \r
37         //add text input tweak\r
38         $("#serverUrl").change(function() {\r
39             $(document.body).data('hdparams')['classificationServer'] = $(this).val();\r
40         updateLink("serverLink");\r
41         }).val($(document.body).data('hdparams')['classificationServer'])\r
42         //set launch link at first\r
43     updateLink("serverLink");\r
44 \r
45         // add buttons to change video\r
46         $("#sourceRibbon div").click(function() {\r
47             var $this = $(this);\r
48             $this.siblings().removeClass('selected'); //clear other selection\r
49             $this.addClass('selected');\r
50             var objImg = $this.children('img')[0];\r
51             var hd = $(document.body).data('hdparams');\r
52             if (objImg) {\r
53                 switchImage(objImg.src);\r
54             clearInterval(hd.frameTimer);       // stop the processing\r
55 \r
56             var movieAttr = $(objImg).attr('movie');\r
57             if (movieAttr) {\r
58                 // Set the video source based on URL specified in the 'videos' list, or select camera input\r
59                 $(hd.video).show();\r
60                 $(srcImgCanvas).hide();\r
61                 if (movieAttr == "Camera") {\r
62                     var constraints = {audio: false, video: true};\r
63                     navigator.mediaDevices.getUserMedia(constraints)\r
64                         .then(function(mediaStream) {\r
65                             hd.video.srcObject = mediaStream;\r
66                             hd.video.play();\r
67                         })\r
68                         .catch(function(err) {\r
69                             console.log(err.name + ": " + err.message);\r
70                         });\r
71                 } else {\r
72                     var mp4 = document.getElementById("mp4");\r
73                     mp4.setAttribute("src", movieAttr);\r
74                     hd.video.load();\r
75                     newVideo();\r
76                 }\r
77             }\r
78             else {\r
79                 hd.video.pause();\r
80                 $(hd.video).hide();\r
81                 $(srcImgCanvas).show();\r
82             }\r
83             }\r
84         });\r
85 \r
86         //allow user-uploaded images\r
87     var imageLoader = document.getElementById('imageLoader');\r
88     imageLoader.addEventListener('change', handleImage, false);\r
89 \r
90     //trigger first click\r
91     $("#sourceRibbon div")[0].click();\r
92 });\r
93 \r
94 \r
95 /**\r
96  * Called after a new video has loaded (at least the video metadata has loaded)\r
97  */\r
98 function newVideo() {\r
99         var hd = $(document.body).data('hdparams');\r
100         hd.frameCounter = 0;\r
101         hd.imageIsWaiting = false;\r
102         hd.video.play();\r
103 \r
104         // set processing canvas size based on source video\r
105         var pwidth = hd.video.videoWidth;\r
106         var pheight = hd.video.videoHeight;\r
107         if (pwidth > hd.maxSrcVideoWidth) {\r
108                 pwidth = hd.maxSrcVideoWidth;\r
109                 pheight = Math.floor((pwidth / hd.video.videoWidth) * pheight); // preserve aspect ratio\r
110         }\r
111         hd.srcImgCanvas.width = pwidth;\r
112         hd.srcImgCanvas.height = pheight;\r
113 \r
114         hd.frameTimer = setInterval(nextFrame, hd.frameInterval); // start the processing\r
115 }\r
116 \r
117 /**\r
118  * process the next video frame\r
119  */\r
120 function nextFrame() {\r
121         var hd = $(document.body).data('hdparams');\r
122         if (hd.video.ended || hd.video.paused) {\r
123                 return;\r
124         }\r
125     switchImage(hd.video, true);\r
126 }\r
127 \r
128 function updateLink(domId) {\r
129     var sPageURL = decodeURIComponent(window.location.search.split('?')[0]);\r
130     var newServer = $(document.body).data('hdparams')['classificationServer'];\r
131     var sNewUrl = sPageURL+"?url-image="+newServer;\r
132     $("#"+domId).attr('href', sNewUrl);\r
133 }\r
134 \r
135 function switchImage(imgSrc, isVideo) {\r
136     var canvas = $(document.body).data('hdparams')['srcImgCanvas'];\r
137     if (!isVideo) {\r
138         var img = new Image();\r
139         img.onload = function () {\r
140             var ctx = canvas.getContext('2d');\r
141             var canvasCopy = document.createElement("canvas");\r
142             var copyContext = canvasCopy.getContext("2d");\r
143 \r
144             var ratio = 1;\r
145 \r
146             //console.log( $(document.body).data('hdparams'));\r
147             //console.log( [ img.width, img.height]);\r
148             // https://stackoverflow.com/a/2412606\r
149             if(img.width > $(document.body).data('hdparams')['canvasMaxW'])\r
150                 ratio = $(document.body).data('hdparams')['canvasMaxW'] / img.width;\r
151             if(ratio*img.height > $(document.body).data('hdparams')['canvasMaxH'])\r
152                 ratio = $(document.body).data('hdparams')['canvasMaxH'] / img.height;\r
153 \r
154             canvasCopy.width = img.width;\r
155             canvasCopy.height = img.height;\r
156             copyContext.drawImage(img, 0, 0);\r
157 \r
158             canvas.width = img.width * ratio;\r
159             canvas.height = img.height * ratio;\r
160             ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);\r
161             //document.removeChild(canvasCopy);\r
162             doPostImage(canvas, '#destImg', canvas.toDataURL());\r
163         }\r
164         img.src = imgSrc;  //copy source, let image load\r
165     }\r
166     else if (!$(document.body).data('hdparams').imageIsWaiting) {\r
167         var ctx = canvas.getContext('2d');\r
168         var canvasCopy = document.createElement("canvas");\r
169         var copyContext = canvasCopy.getContext("2d");\r
170         var ratio = 1;\r
171 \r
172         if(imgSrc.videoWidth > $(document.body).data('hdparams')['canvasMaxW'])\r
173             ratio = $(document.body).data('hdparams')['canvasMaxW'] / imgSrc.videoWidth;\r
174         if(ratio*imgSrc.videoHeight > $(document.body).data('hdparams')['canvasMaxH'])\r
175             ratio = $(document.body).data('hdparams')['canvasMaxH'] / canvasCopy.height;\r
176 \r
177         //console.log("Canvas Copy:"+canvasCopy.width+"/"+canvasCopy.height);\r
178         //console.log("Canvas Ratio:"+ratio);\r
179         //console.log("Video: "+imgSrc.videoWidth+"x"+imgSrc.videoHeight);\r
180         canvasCopy.width = imgSrc.videoWidth;     //large as possible\r
181         canvasCopy.height = imgSrc.videoHeight;\r
182         copyContext.drawImage(imgSrc, 0, 0);\r
183 \r
184         canvas.width = canvasCopy.width * ratio;\r
185         canvas.height = canvasCopy.height * ratio;\r
186         ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);\r
187         //document.removeChild(canvasCopy);\r
188         doPostImage(canvas, '#destImg', canvas.toDataURL());\r
189     }\r
190 }\r
191 \r
192 \r
193 //load image that has been uploaded into a vancas\r
194 function handleImage(e){\r
195     var reader = new FileReader();\r
196     reader.onload = function(event){\r
197         switchImage(event.target.result);\r
198     }\r
199     reader.readAsDataURL(e.target.files[0]);\r
200 }\r
201 \r
202 \r
203 \r
204 // https://stackoverflow.com/questions/19491336/get-url-parameter-jquery-or-how-to-get-query-string-values-in-js\r
205 function getUrlParameter(sParam) {\r
206     var sPageURL = decodeURIComponent(window.location.search.substring(1)),\r
207         sURLVariables = sPageURL.split('&'),\r
208         sParameterName,\r
209         i;\r
210 \r
211     for (i = 0; i < sURLVariables.length; i++) {\r
212         sParameterName = sURLVariables[i].split('=');\r
213 \r
214         if (sParameterName[0] === sParam) {\r
215             return sParameterName[1] === undefined ? true : sParameterName[1];\r
216         }\r
217     }\r
218 };\r
219 \r
220 \r
221 /**\r
222  * post an image from the canvas to the service\r
223  */\r
224 function doPostImage(srcCanvas, dstImg, dataPlaceholder) {\r
225         var serviceURL = "";\r
226         var dataURL = srcCanvas.toDataURL('image/jpeg', 1.0);\r
227         var blob = dataURItoBlob(dataURL);\r
228         var hd = $(document.body).data('hdparams');\r
229         var fd = new FormData();\r
230 \r
231         $(document.body).data('hdparams').imageIsWaiting = true;\r
232     serviceURL = hd.classificationServer;\r
233     fd.append("image_binary", blob);\r
234     fd.append("mime_type", "image/jpeg");\r
235     var $dstImg = $(dstImg);\r
236     if ($dstImg.attr('src')=='') {\r
237         $dstImg.attr('src', dataPlaceholder);\r
238         //$(dstImg).addClass('workingImage').attr('src', dataPlaceholder);\r
239     }\r
240     //$(dstImg).addClaas('workingImage').siblings('.spinner').remove().after($("<span class='spinner'>&nbsp;</span>"));\r
241 \r
242         var request = new XMLHttpRequest();\r
243         hd.imageIsWaiting = true;\r
244         request.onreadystatechange=function() {\r
245                 if (request.readyState==4 && request.status==200) {\r
246                     var responseJson = $.parseJSON(request.responseText);\r
247                     var respImage = responseJson[0];\r
248                     // https://stackoverflow.com/questions/21227078/convert-base64-to-image-in-javascript-jquery\r
249             $dstImg.attr('src', "data:"+respImage['mime_type']+";base64,"+respImage['image_binary']).removeClass('workingImage');\r
250                         //genClassTable($.parseJSON(request.responseText), dstDiv);\r
251                         hd.imageIsWaiting = false;\r
252                 }\r
253         }\r
254         request.open("POST", serviceURL, true);\r
255         request.send(fd);\r
256         $(document.body).data('hdparams').imageIsWaiting = false;\r
257 }\r
258 \r
259 \r
260 /**\r
261  * convert base64/URLEncoded data component to raw binary data held in a string\r
262  *\r
263  * Stoive, http://stackoverflow.com/questions/4998908/convert-data-uri-to-file-then-append-to-formdata\r
264  */\r
265 function dataURItoBlob(dataURI) {\r
266     // convert base64/URLEncoded data component to raw binary data held in a string\r
267     var byteString;\r
268     if (dataURI.split(',')[0].indexOf('base64') >= 0)\r
269         byteString = atob(dataURI.split(',')[1]);\r
270     else\r
271         byteString = unescape(dataURI.split(',')[1]);\r
272 \r
273     // separate out the mime component\r
274     var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];\r
275 \r
276     // write the bytes of the string to a typed array\r
277     var ia = new Uint8Array(byteString.length);\r
278     for (var i = 0; i < byteString.length; i++) {\r
279         ia[i] = byteString.charCodeAt(i);\r
280     }\r
281 \r
282     return new Blob([ia], {type:mimeString});\r
283 }\r
284 \r