// work out new width for canvas. depending on whether width > height of the // image, then use the width of the image (with the specified gap) otherwise // the height > width, so scale the new width based on height ratio of // image to window function NewWidth() { w_r=im.width/(window.innerWidth*gap) h_r=im.height/(window.innerHeight*gap) if( w_r > h_r ) return window.innerWidth*gap else return im.width*gap / (im.height/window.innerHeight) } // work out new height for canvas. depending on whether height > width of the // image, then use the height of the image (with the specified gap) otherwise // the width > height, so scale the new height based on width ratio of // image to window function NewHeight() { w_r=im.width/(window.innerWidth*gap) h_r=im.height/(window.innerHeight*gap) if( h_r > w_r ) return window.innerHeight*gap else return im.height*gap / (im.width/window.innerWidth) } function DrawLabelOnFace(str) { // finish face box, need to clear out new settings for // transparent backed-name tag context.stroke(); context.beginPath() context.lineWidth = 0.1 context.font = "30px Arial" context.globalAlpha = 0.6 bbox = context.measureText(str); f_h=bbox.fontBoundingBoxAscent if( bbox.fontBoundingBoxDescent ) f_h += bbox.fontBoundingBoxDescent f_h -= 8 context.rect( x+w/2-bbox.width/2, y-f_h, bbox.width, f_h ) context.fillStyle="white" context.fill() context.stroke(); context.beginPath() context.globalAlpha = 1.0 context.font = "30px Arial" context.textAlign = "center" context.fillStyle = context.strokeStyle context.fillText(str, x+w/2, y-2) } // This draws the image, it can be called on resize events, img.src finishing // loading or explicitly on page load. Will also deal with all state/toggles // for items like name, grayscale, etc. function DrawImg() { // another call to this func will occur on load, so skip this one if( im.width == 0 ) return canvas.width=NewWidth(im) canvas.height=NewHeight(im) // actually draw the pixel images to the canvas at the right size if( grayscale ) context.filter='grayscale(1)' context.drawImage(im, 0, 0, canvas.width, canvas.height ) // -50 is a straight up hack, no idea why this works, but its good enough for me if( throbber ) $('#throbber').attr('style', 'display:show; position:absolute; left:'+canvas.width/2+'px; top:'+(canvas.height/2-50)+'px' ) else $('#throbber').hide(); // show (or not) the whole figcaption with fname in it - based on state of fname_toggle if( $('#fname_toggle').prop('checked' ) ) { // reset fname for new image (if navigated left/right to get here) $('#fname').html(PrettyFname(objs[current].url)) $('.figcaption').show() } else $('.figcaption').hide() // if we have faces, the enable the toggles, otherwise disable them // and reset model select too if( objs[current].faces ) { $('#faces').attr('disabled', false) $('#distance').attr('disabled', false) $('#model').val( Number(objs[current].face_model) ) } else { $('#faces').attr('disabled', true) $('#distance').attr('disabled', true) // if no faces, then model is N/A (always 1st element - or 0 in select) $('#model').val(0) } // okay, we want faces drawn so lets do it if( $('#faces').prop('checked') && objs[current].faces ) { // draw rect on each face for( i=0; i= fx && x <= fx+fw && y >= fy && y <= fy+fh ) { if( objs[current].faces[i].override ) { item_list['remove_override']={ 'name': 'Remove override for this face', 'which_face': i, 'id': objs[current].faces[i].id } } else if( objs[current].faces[i].who ) { item_list['match']={ 'name': objs[current].faces[i].who, 'which_face': i, 'id': objs[current].faces[i].id } item_list['wrong_person']={ 'name': 'wrong person', 'which_face': i, 'id': objs[current].faces[i].id } } else { item_list['no_match_new_person']={ 'name': 'Add as reference image to NEW person', 'which_face': i, 'id': objs[current].faces[i].id } item_list['no_match_new_refimg']={ 'name': 'Add as reference image to EXISTING person', 'which_face': i, 'id': objs[current].faces[i].id } item_list['no_match_override_match']={ 'name': 'Manually match to existing person', 'which_face': i, 'id': objs[current].faces[i].id } item_list['no_match_no_face']={ 'name': 'Mark as not a face', 'which_face': i, 'id': objs[current].faces[i].id } item_list['no_match_too_young']={ 'name': 'Mark as face too young', 'which_face': i, 'id': objs[current].faces[i].id } item_list['no_match_ignore']={ 'name': 'Ignore this face', 'which_face': i, 'id': objs[current].faces[i].id } } delete item_list['not_a_face'] $('#canvas').prop('menu_item', item_list ) break } } return { callback: function( key, options) { if( key == 'not_a_face' ) { return true } item=$('#canvas').prop( 'menu_item' ); FaceDBox( key, item ) }, items: item_list }; } } ) } ); // quick wrapper function to make calling this ajax code simpler in SearchForPerson function OverrideForceMatch( person_id, face_id, face_pos ) { ofm='&person_id='+person_id+'&face_id='+face_id $.ajax({ type: 'POST', data: ofm, url: '/override_force_match', success: function(data) { // on success, remember the original data, apply override values, close the dbox, force face drawing boxes on and redraw the face with the new override if( objs[current].faces[face_pos].who ) objs[current].faces[face_pos].old_who=objs[current].faces[face_pos].who objs[current].faces[face_pos].old_distance=objs[current].faces[face_pos].distance objs[current].faces[face_pos].who=data.person_tag objs[current].faces[face_pos].distance="N/A" objs[current].faces[face_pos].override=1 $('#dbox').modal('hide') $('#faces').prop('checked',true) DrawImg() } } ) } // function to facilitate adding a face match override to this "found" person // uses Ajax to the f/e to get any person matching #stext's content (via any name/tag) // and displays results in #search_person_results function SearchForPerson(face_id, face_pos) { // make URI safe who = encodeURIComponent( $('#stext').val() ) // call ajax to find ppl $.ajax({ type: 'POST', data: null, url: '/find_persons/'+ who, success: function(data) { content='Click one of the link(s) below to manually connect this face as once-off connection to the person:

' for( var key in data ) { var person = data[key]; /* content+= ''+person.firstname+' '+person.surname+' ('+person.tag+')
' */ content+= ''+person.firstname+' '+person.surname+' ('+person.tag+')
' } $('#search_person_results').html( content ) } } ) return false } function RemoveOverride() { d='&face_id='+objs[current].faces[face_pos].id+'&person_tag='+objs[current].faces[face_pos].who+ '&file_eid='+current $.ajax({ type: 'POST', data: d, url: '/remove_override', success: function(data) { if( objs[current].faces[face_pos].old_who ) objs[current].faces[face_pos].who=objs[current].faces[face_pos].old_who else delete objs[current].faces[face_pos].who delete objs[current].faces[face_pos].override $('#dbox').modal('hide') DrawImg() return false } } ) return false } // function that is called when we click on a face in the viewer and we want to // potentially override the non-match / match... it shows the face, and then // based on which menu item got us here, shows appropriate text to do next action function FaceDBox(key, item) { face_pos=item[key]['which_face'] div ='

' div+='Face position #' + face_pos div+='

' $.ajax({ type: 'POST', data: null, url: '/get_face_from_image/'+item[key]['id'], success: function(img_data) { $('#face_img').html( '' ) } } ) div+='

' if ( key == 'remove_override' ) { div+='
remove this override (force match to: ' + objs[current].faces[face_pos].who + ')' div+='
' div+='' div+='' div+='
' } if ( key == 'no_match_new_person' ) { div+='
create new person' } if ( key == 'no_match_new_refimg' || key == 'no_match_override_match' ) { div+='
search for existing person:
' div+= `
` } if ( key == 'wrong_person' ) { div+='
wrong person, so mark this as the wrong person/refimg connection, for face#' + item[key]['which_face'] div+='
face db id: ' + item[key]['id'] } if ( key == 'no_match_no_face' || key == 'no_match_too_young' || key == 'no_match_ignore' ) { div+='
just track this against face#' + item[key]['which_face'] div+='
face db id: ' + item[key]['id'] } $('#dbox-title').html(item[key]['name']) $('#dbox-content').html(div) $('#dbox').modal('show') } // func called to show logs relating to this filename from viewer // pops results up in a dbox function JoblogSearch() { data="eid="+current $.ajax({ type: 'POST', data: data, url: '/joblog_search', success: function(res) { data = JSON.parse(res) div ='
' div+='' for( i=0; i' } div+='
LogWhenJob
' + data[i].log_date + '' div+='' + data[i].name + ' #:'+data[i].id+'
' $('#dbox-title').html("Logs relating to this filename") $('#dbox-content').html(div) $('#dbox').modal('show') } }) }