2 image-classes.js - send frames to an image classification service
\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
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 Cognita
\r
17 $(document).ready(function() {
\r
18 var urlDefault = getUrlParameter('url-image');
\r
20 urlDefault = "http://localhost:8884/transform";
\r
22 $(document.body).data('hdparams', { // store global vars in the body element
\r
23 classificationServer: urlDefault,
\r
25 frameInterval: 500, // Milliseconds to sleep between sending frames to reduce server load and reduce results updates
\r
26 frameTimer: -1, // frame clock for processing
\r
27 imageIsWaiting: false, // blocking to prevent too many queued frames
\r
28 // Objects from DOM elements
\r
29 srcImgCanvas: document.getElementById('srcImgCanvas'), // we have a 'src' source image
\r
30 destImg: document.getElementById('destImg'), // we have a 'src' source image
\r
31 video: document.getElementById('srcVideo'),
\r
33 $(document.body).data('hdparams')['canvasMaxH'] = $(document.body).data('hdparams')['srcImgCanvas'].height;
\r
34 $(document.body).data('hdparams')['canvasMaxW'] = $(document.body).data('hdparams')['srcImgCanvas'].width;
\r
36 //add text input tweak
\r
37 $("#serverUrl").change(function() {
\r
38 $(document.body).data('hdparams')['classificationServer'] = $(this).val();
\r
39 updateLink("serverLink");
\r
40 }).val($(document.body).data('hdparams')['classificationServer'])
\r
41 //set launch link at first
\r
42 updateLink("serverLink");
\r
44 // add buttons to change video
\r
45 $("#sourceRibbon div").click(function() {
\r
46 var $this = $(this);
\r
47 $this.siblings().removeClass('selected'); //clear other selection
\r
48 $this.addClass('selected');
\r
49 var objImg = $this.children('img')[0];
\r
50 var hd = $(document.body).data('hdparams');
\r
52 switchImage(objImg.src);
\r
53 clearInterval(hd.frameTimer); // stop the processing
\r
55 var movieAttr = $(objImg).attr('movie');
\r
57 // Set the video source based on URL specified in the 'videos' list, or select camera input
\r
59 $(srcImgCanvas).hide();
\r
60 if (movieAttr == "Camera") {
\r
61 var constraints = {audio: false, video: true};
\r
62 navigator.mediaDevices.getUserMedia(constraints)
\r
63 .then(function(mediaStream) {
\r
64 hd.video.srcObject = mediaStream;
\r
67 .catch(function(err) {
\r
68 console.log(err.name + ": " + err.message);
\r
71 var mp4 = document.getElementById("mp4");
\r
72 mp4.setAttribute("src", movieAttr);
\r
80 $(srcImgCanvas).show();
\r
85 //allow user-uploaded images
\r
86 var imageLoader = document.getElementById('imageLoader');
\r
87 imageLoader.addEventListener('change', handleImage, false);
\r
89 //trigger first click
\r
90 $("#sourceRibbon div")[0].click();
\r
95 * Called after a new video has loaded (at least the video metadata has loaded)
\r
97 function newVideo() {
\r
98 var hd = $(document.body).data('hdparams');
\r
99 hd.frameCounter = 0;
\r
100 hd.imageIsWaiting = false;
\r
103 // set processing canvas size based on source video
\r
104 var pwidth = hd.video.videoWidth;
\r
105 var pheight = hd.video.videoHeight;
\r
106 if (pwidth > hd.maxSrcVideoWidth) {
\r
107 pwidth = hd.maxSrcVideoWidth;
\r
108 pheight = Math.floor((pwidth / hd.video.videoWidth) * pheight); // preserve aspect ratio
\r
110 hd.srcImgCanvas.width = pwidth;
\r
111 hd.srcImgCanvas.height = pheight;
\r
113 hd.frameTimer = setInterval(nextFrame, hd.frameInterval); // start the processing
\r
117 * process the next video frame
\r
119 function nextFrame() {
\r
120 var hd = $(document.body).data('hdparams');
\r
121 if (hd.video.ended || hd.video.paused) {
\r
124 switchImage(hd.video, true);
\r
127 function updateLink(domId) {
\r
128 var sPageURL = decodeURIComponent(window.location.search.split('?')[0]);
\r
129 var newServer = $(document.body).data('hdparams')['classificationServer'];
\r
130 var sNewUrl = sPageURL+"?url-image="+newServer;
\r
131 $("#"+domId).attr('href', sNewUrl);
\r
134 function switchImage(imgSrc, isVideo) {
\r
135 var canvas = $(document.body).data('hdparams')['srcImgCanvas'];
\r
137 var img = new Image();
\r
138 img.onload = function () {
\r
139 var ctx = canvas.getContext('2d');
\r
140 var canvasCopy = document.createElement("canvas");
\r
141 var copyContext = canvasCopy.getContext("2d");
\r
145 //console.log( $(document.body).data('hdparams'));
\r
146 //console.log( [ img.width, img.height]);
\r
147 // https://stackoverflow.com/a/2412606
\r
148 if(img.width > $(document.body).data('hdparams')['canvasMaxW'])
\r
149 ratio = $(document.body).data('hdparams')['canvasMaxW'] / img.width;
\r
150 if(ratio*img.height > $(document.body).data('hdparams')['canvasMaxH'])
\r
151 ratio = $(document.body).data('hdparams')['canvasMaxH'] / img.height;
\r
153 canvasCopy.width = img.width;
\r
154 canvasCopy.height = img.height;
\r
155 copyContext.drawImage(img, 0, 0);
\r
157 canvas.width = img.width * ratio;
\r
158 canvas.height = img.height * ratio;
\r
159 ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
\r
160 //document.removeChild(canvasCopy);
\r
161 doPostImage(canvas, '#destImg', canvas.toDataURL());
\r
163 img.src = imgSrc; //copy source, let image load
\r
165 else if (!$(document.body).data('hdparams').imageIsWaiting) {
\r
166 var ctx = canvas.getContext('2d');
\r
167 var canvasCopy = document.createElement("canvas");
\r
168 var copyContext = canvasCopy.getContext("2d");
\r
171 if(imgSrc.videoWidth > $(document.body).data('hdparams')['canvasMaxW'])
\r
172 ratio = $(document.body).data('hdparams')['canvasMaxW'] / imgSrc.videoWidth;
\r
173 if(ratio*imgSrc.videoHeight > $(document.body).data('hdparams')['canvasMaxH'])
\r
174 ratio = $(document.body).data('hdparams')['canvasMaxH'] / canvasCopy.height;
\r
176 //console.log("Canvas Copy:"+canvasCopy.width+"/"+canvasCopy.height);
\r
177 //console.log("Canvas Ratio:"+ratio);
\r
178 //console.log("Video: "+imgSrc.videoWidth+"x"+imgSrc.videoHeight);
\r
179 canvasCopy.width = imgSrc.videoWidth; //large as possible
\r
180 canvasCopy.height = imgSrc.videoHeight;
\r
181 copyContext.drawImage(imgSrc, 0, 0);
\r
183 canvas.width = canvasCopy.width * ratio;
\r
184 canvas.height = canvasCopy.height * ratio;
\r
185 ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
\r
186 //document.removeChild(canvasCopy);
\r
187 doPostImage(canvas, '#destImg', canvas.toDataURL());
\r
192 //load image that has been uploaded into a vancas
\r
193 function handleImage(e){
\r
194 var reader = new FileReader();
\r
195 reader.onload = function(event){
\r
196 switchImage(event.target.result);
\r
198 reader.readAsDataURL(e.target.files[0]);
\r
203 // https://stackoverflow.com/questions/19491336/get-url-parameter-jquery-or-how-to-get-query-string-values-in-js
\r
204 function getUrlParameter(sParam) {
\r
205 var sPageURL = decodeURIComponent(window.location.search.substring(1)),
\r
206 sURLVariables = sPageURL.split('&'),
\r
210 for (i = 0; i < sURLVariables.length; i++) {
\r
211 sParameterName = sURLVariables[i].split('=');
\r
213 if (sParameterName[0] === sParam) {
\r
214 return sParameterName[1] === undefined ? true : sParameterName[1];
\r
221 * post an image from the canvas to the service
\r
223 function doPostImage(srcCanvas, dstImg, dataPlaceholder) {
\r
224 var serviceURL = "";
\r
225 var dataURL = srcCanvas.toDataURL('image/jpeg', 1.0);
\r
226 var blob = dataURItoBlob(dataURL);
\r
227 var hd = $(document.body).data('hdparams');
\r
228 var fd = new FormData();
\r
230 $(document.body).data('hdparams').imageIsWaiting = true;
\r
231 serviceURL = hd.classificationServer;
\r
232 fd.append("base64_data", blob);
\r
233 fd.append("mime_type", "image/jpeg");
\r
234 var $dstImg = $(dstImg);
\r
235 if ($dstImg.attr('src')=='') {
\r
236 $dstImg.attr('src', dataPlaceholder);
\r
237 //$(dstImg).addClass('workingImage').attr('src', dataPlaceholder);
\r
239 //$(dstImg).addClaas('workingImage').siblings('.spinner').remove().after($("<span class='spinner'> </span>"));
\r
241 var request = new XMLHttpRequest();
\r
242 hd.imageIsWaiting = true;
\r
243 request.onreadystatechange=function() {
\r
244 if (request.readyState==4 && request.status==200) {
\r
245 var responseJson = $.parseJSON(request.responseText);
\r
246 var respImage = responseJson[0];
\r
247 // https://stackoverflow.com/questions/21227078/convert-base64-to-image-in-javascript-jquery
\r
248 $dstImg.attr('src', "data:"+respImage['mime_type']+";base64,"+respImage['base64_data']).removeClass('workingImage');
\r
249 //genClassTable($.parseJSON(request.responseText), dstDiv);
\r
250 hd.imageIsWaiting = false;
\r
253 request.open("POST", serviceURL, true);
\r
255 $(document.body).data('hdparams').imageIsWaiting = false;
\r
260 * convert base64/URLEncoded data component to raw binary data held in a string
\r
262 * Stoive, http://stackoverflow.com/questions/4998908/convert-data-uri-to-file-then-append-to-formdata
\r
264 function dataURItoBlob(dataURI) {
\r
265 // convert base64/URLEncoded data component to raw binary data held in a string
\r
267 if (dataURI.split(',')[0].indexOf('base64') >= 0)
\r
268 byteString = atob(dataURI.split(',')[1]);
\r
270 byteString = unescape(dataURI.split(',')[1]);
\r
272 // separate out the mime component
\r
273 var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
\r
275 // write the bytes of the string to a typed array
\r
276 var ia = new Uint8Array(byteString.length);
\r
277 for (var i = 0; i < byteString.length; i++) {
\r
278 ia[i] = byteString.charCodeAt(i);
\r
281 return new Blob([ia], {type:mimeString});
\r