Comparing version 1.0.6 to 1.0.7
@@ -95,3 +95,4 @@ 'use strict'; | ||
DeepAI.prototype.renderResultIntoElement = resultRendering.renderResultIntoElement; | ||
DeepAI.prototype.renderAnnotatedResultIntoElement = resultRendering.renderAnnotatedResultIntoElement; | ||
module.exports = DeepAI; |
'use strict'; | ||
const apiBaseUrl = require('./apiBaseUrl').baseUrl; | ||
var WAD_COLORS = [ | ||
"rgb(173, 35, 35)", // Red | ||
"rgb(42, 75, 215)", // Blue | ||
"rgb(87, 87, 87)", // Dark Gray | ||
"rgb(29, 105, 20)", // Green | ||
"rgb(129, 74, 25)", // Brown | ||
"rgb(129, 38, 192)", // Purple | ||
"rgb(160, 160, 160)", // Lt Gray | ||
"rgb(129, 197, 122)", // Lt green | ||
"rgb(157, 175, 255)", // Lt blue | ||
"rgb(41, 208, 208)", // Cyan | ||
"rgb(255, 146, 51)", // Orange | ||
"rgb(199, 183, 0)", // Yellow | ||
"rgb(233, 222, 187)", // Tan | ||
"rgb(255, 205, 243)", // Pink | ||
// "rgb(255, 255, 255)", // White | ||
//"rgb(0, 0, 0)", // Black | ||
"rgb(173, 35, 35)", // Red | ||
"rgb(42, 75, 215)", // Blue | ||
"rgb(87, 87, 87)", // Dark Gray | ||
"rgb(29, 105, 20)", // Green | ||
"rgb(129, 74, 25)", // Brown | ||
"rgb(129, 38, 192)", // Purple | ||
"rgb(160, 160, 160)", // Lt Gray | ||
"rgb(129, 197, 122)", // Lt green | ||
"rgb(157, 175, 255)", // Lt blue | ||
"rgb(41, 208, 208)", // Cyan | ||
"rgb(255, 146, 51)", // Orange | ||
"rgb(199, 183, 0)", // Yellow | ||
"rgb(233, 222, 187)", // Tan | ||
"rgb(255, 205, 243)", // Pink | ||
// "rgb(255, 255, 255)", // White | ||
//"rgb(0, 0, 0)", // Black | ||
]; | ||
var isAbsolute = new RegExp('^([a-z]+://|//)', 'i'); | ||
var isDataOrBlob = new RegExp('^(data|blob):', 'i'); | ||
async function renderResultIntoElement(result, element){ | ||
function prependApiBaseIfNeeded(url) { | ||
if(isAbsolute.test(url) || isDataOrBlob.test(url)) { | ||
return url; // already absolute | ||
} else { | ||
return apiBaseUrl + url; // turn relative into absolute | ||
} | ||
} | ||
/* | ||
Data structures basic info... | ||
result | ||
{ | ||
output_url: | ||
output: | ||
id: | ||
err: | ||
} | ||
resultPageData | ||
{ | ||
result_data: { | ||
inputs:[ | ||
{ | ||
is_img: true, | ||
url: (relative or absolute) | ||
} | ||
], | ||
visualizer_data: { | ||
list_key: 'Objects' | ||
label_key: 'Object' | ||
}, | ||
scale_applied: 1.333 | ||
} | ||
} | ||
annotatedResult - this is basically the merging of the 2 above | ||
{ err: | ||
output_url: | ||
output: | ||
id: | ||
inputs:[ | ||
{ | ||
is_img: true, | ||
url: (relative or absolute) | ||
} | ||
], | ||
visualizer_data: { | ||
list_key: 'Objects' | ||
label_key: 'Object' | ||
}, | ||
scale_applied: 1.333 | ||
} | ||
*/ | ||
// Take a result object from API call, and fetch additional data, and return the additional data merged in. | ||
async function getAnnotatedResultFromResult(result) { | ||
if(result.err) { | ||
console.log('cannot get result page data for error result'); | ||
return result; | ||
} | ||
var resultPageData = await fetch(apiBaseUrl + '/get_standard_api_result_data/' + result.id, { | ||
credentials: 'include' | ||
}); | ||
resultPageData = await resultPageData.json(); | ||
var result_data = resultPageData.result_data; | ||
// make merging of all the properties manually... | ||
return { | ||
err: result.err, | ||
output: result.output, | ||
output_url: result.output_url, | ||
id: result.id, | ||
inputs: result_data.inputs, | ||
visualizer_data: result_data.visualizer_data, | ||
scale_applied: result_data.scale_applied | ||
}; | ||
} | ||
async function renderResultIntoElement(result, element) { | ||
console.log('getting result page data'); | ||
var annotatedResult = await getAnnotatedResultFromResult(result); | ||
console.log('got result page data'); | ||
return renderAnnotatedResultIntoElement(annotatedResult, element); | ||
} | ||
function renderAnnotatedResultIntoElement(annotatedResult, element) { | ||
element.innerHTML = ''; // remove everything to start | ||
if(result.err){ | ||
if(annotatedResult.err) { | ||
element.innerHTML = err; | ||
return false; | ||
} | ||
if(result.output){ | ||
if(annotatedResult.output) { | ||
// JSON or text output. | ||
console.log('got json or text output'); | ||
console.log('getting result page data'); | ||
var resultPageData = await fetch(apiBaseUrl + '/get_standard_api_result_data/'+result.id, { | ||
credentials: 'include' | ||
}); | ||
resultPageData = await resultPageData.json(); | ||
console.log('got result page data'); | ||
if(typeof result.output === 'string'){ | ||
if(typeof annotatedResult.output === 'string') { | ||
var scroller = document.createElement("div"); | ||
@@ -51,15 +131,12 @@ scroller.style.width = '100%'; | ||
element.appendChild(scroller); | ||
var pre = document.createElement("pre"); | ||
pre.textContent = result.output; | ||
pre.textContent = annotatedResult.output; | ||
pre.style.whiteSpace = "pre-wrap"; | ||
pre.style.margin = '0px'; | ||
scroller.appendChild(pre); | ||
// Append inputs | ||
for(var input of resultPageData.result_data.inputs){ | ||
if(input.is_img){ | ||
for(var input of annotatedResult.inputs) { | ||
if(input.is_img) { | ||
var img_tag = document.createElement('img'); | ||
img_tag.src = apiBaseUrl + input.url; | ||
img_tag.src = prependApiBaseIfNeeded(input.url); | ||
img_tag.style.position = 'relative'; | ||
@@ -72,25 +149,15 @@ img_tag.style.width = '100%'; | ||
} | ||
return true; | ||
}else if(typeof result.output === 'object'){ | ||
} else if(typeof annotatedResult.output === 'object') { | ||
// If we uploaded an image, then we may be able to render the image with boxes on top | ||
if( resultPageData.result_data.inputs.length == 1 | ||
&& resultPageData.result_data.inputs[0].is_img | ||
&& resultPageData.result_data.visualizer_data | ||
){ | ||
if(annotatedResult.inputs.length == 1 && | ||
annotatedResult.inputs[0].is_img && | ||
annotatedResult.visualizer_data | ||
) { | ||
// single image input and we know how to visualize it. | ||
console.log('have visualizer for result JSON'); | ||
var resultscaler = document.createElement('iframe'); | ||
// Set up a handler for when the frame loads - we need to handle this event | ||
resultscaler.onload = function() { | ||
// Firefox doesnt allow inner iframe manip until the iframe is loaded... | ||
var innerDoc = resultscaler.contentDocument.body; | ||
@@ -102,75 +169,55 @@ innerDoc.style.margin = '0px'; | ||
bbox_container.style.opacity = '0.001'; // the result are hidden until the iframe reflows - which is first when the img loads | ||
innerDoc.appendChild(bbox_container); | ||
var img_tag = document.createElement('img'); | ||
img_tag.src = apiBaseUrl + resultPageData.result_data.inputs[0].url; | ||
img_tag.src = prependApiBaseIfNeeded(annotatedResult.inputs[0].url); | ||
img_tag.style.position = 'absolute'; | ||
bbox_container.appendChild(img_tag); | ||
var iframe_reflow = function(){ | ||
var iframe_reflow = function() { | ||
console.log('iframe resize'); | ||
resultscaler.contentDocument.body.style.transform = null; | ||
var bodyWidth = resultscaler.contentDocument.body.scrollWidth; | ||
var bodyHeight = resultscaler.contentDocument.body.scrollHeight; | ||
var imgWidth = img_tag.offsetWidth; | ||
var imgHeight = img_tag.offsetHeight; | ||
var containerWidth = resultscaler.offsetWidth; | ||
var containerHeight = resultscaler.offsetHeight; | ||
var wExcess=0; | ||
var hExcess=0; | ||
if(imgWidth < bodyWidth && imgHeight < bodyHeight){ | ||
var wExcess = 0; | ||
var hExcess = 0; | ||
if(imgWidth < bodyWidth && imgHeight < bodyHeight) { | ||
var wScale = containerWidth / imgWidth; | ||
var hScale = containerHeight / imgHeight; | ||
var minScale = Math.min(wScale, hScale); | ||
wExcess = containerWidth - imgWidth*minScale; | ||
hExcess = containerHeight - imgHeight*minScale; | ||
}else{ | ||
wExcess = containerWidth - imgWidth * minScale; | ||
hExcess = containerHeight - imgHeight * minScale; | ||
} else { | ||
var wScale = containerWidth / bodyWidth; | ||
var hScale = containerHeight / bodyHeight; | ||
var minScale = Math.min(wScale, hScale); | ||
wExcess = containerWidth - bodyWidth*minScale; | ||
hExcess = containerHeight - bodyHeight*minScale; | ||
wExcess = containerWidth - bodyWidth * minScale; | ||
hExcess = containerHeight - bodyHeight * minScale; | ||
} | ||
wExcess = wExcess/minScale; | ||
hExcess = hExcess/minScale; | ||
wExcess = wExcess / minScale; | ||
hExcess = hExcess / minScale; | ||
resultscaler.contentDocument.body.style.transformOrigin = 'top left'; | ||
resultscaler.contentDocument.body.style.transform = 'scale('+minScale+')'; | ||
bbox_container.style.setProperty('--fontscale', (100/minScale)+ "%"); | ||
bbox_container.style.left = (wExcess/2)+"px"; | ||
bbox_container.style.top = (hExcess/2)+"px"; | ||
resultscaler.contentDocument.body.style.transform = 'scale(' + minScale + ')'; | ||
bbox_container.style.setProperty('--fontscale', (100 / minScale) + "%"); | ||
bbox_container.style.left = (wExcess / 2) + "px"; | ||
bbox_container.style.top = (hExcess / 2) + "px"; | ||
bbox_container.style.opacity = '1'; | ||
}; | ||
resultscaler.contentWindow.onresize = iframe_reflow; | ||
img_tag.onload = iframe_reflow; | ||
var processed_annotations = process_annotations(result.output, resultPageData.result_data.visualizer_data, resultPageData.result_data.scale_applied); | ||
var processed_annotations = process_annotations(annotatedResult.output, annotatedResult.visualizer_data, annotatedResult.scale_applied); | ||
console.log('processed annotations', processed_annotations); | ||
var i = 0; | ||
for(var annotation of processed_annotations){ | ||
for(var annotation of processed_annotations) { | ||
var bbox = document.createElement('boundingbox'); | ||
bbox.style.position = 'absolute'; | ||
bbox.style.left = annotation.bounding_box[0]+'px'; | ||
bbox.style.top = annotation.bounding_box[1]+'px'; | ||
bbox.style.width = annotation.bounding_box[2]+'px'; | ||
bbox.style.height = annotation.bounding_box[3]+'px'; | ||
bbox.style.left = annotation.bounding_box[0] + 'px'; | ||
bbox.style.top = annotation.bounding_box[1] + 'px'; | ||
bbox.style.width = annotation.bounding_box[2] + 'px'; | ||
bbox.style.height = annotation.bounding_box[3] + 'px'; | ||
var color = WAD_COLORS[i++ % WAD_COLORS.length]; | ||
bbox.style.border = '2px solid '+color; | ||
bbox.style.border = '2px solid ' + color; | ||
bbox_container.appendChild(bbox); | ||
var bbox_label = document.createElement('boundingboxlabel'); | ||
@@ -184,7 +231,5 @@ bbox_label.textContent = annotation.caption; | ||
} | ||
} | ||
// Set the src which will end up triggering the onload event in all browsers. | ||
resultscaler.src='about:blank'; | ||
resultscaler.src = 'about:blank'; | ||
resultscaler.style.border = 'none'; | ||
@@ -195,5 +240,3 @@ resultscaler.style.width = '100%'; | ||
return true; | ||
}else{ | ||
} else { | ||
// not single image - perhaps multi image or text input. | ||
@@ -209,15 +252,12 @@ // or no visualizer | ||
scroller.style.flexDirection = 'column'; | ||
element.appendChild(scroller); | ||
var pre = document.createElement("pre"); | ||
pre.style.margin = '0px'; | ||
pre.textContent = JSON.stringify(result.output, null, 4); | ||
pre.textContent = JSON.stringify(annotatedResult.output, null, 4); | ||
scroller.appendChild(pre); | ||
// Append inputs | ||
for(var input of resultPageData.result_data.inputs){ | ||
if(input.is_img){ | ||
for(var input of annotatedResult.inputs) { | ||
if(input.is_img) { | ||
var img_tag = document.createElement('img'); | ||
img_tag.src = apiBaseUrl + input.url; | ||
img_tag.src = prependApiBaseIfNeeded(input.url); | ||
img_tag.style.width = '100%'; | ||
@@ -229,22 +269,15 @@ img_tag.style.height = '79%'; | ||
} | ||
return true; | ||
// We got JSON output for a multi image or text input ... don't bother showing the input right now | ||
} | ||
}else{ | ||
} else { | ||
element.innerHTML = "Model returned an unknown data type."; | ||
return false; | ||
} | ||
}else if(result.output_url){ | ||
} else if(annotatedResult.output_url) { | ||
// Image output. | ||
console.log('got image output'); | ||
// Just show the image. | ||
var img_tag = document.createElement('img'); | ||
img_tag.src = result.output_url; | ||
img_tag.src = annotatedResult.output_url; | ||
img_tag.style.position = 'relative'; | ||
@@ -256,4 +289,3 @@ img_tag.style.width = '100%'; | ||
return true; | ||
}else{ | ||
} else { | ||
element.innerHTML = "Model did not return an output or an error."; | ||
@@ -264,3 +296,2 @@ return false; | ||
function capitalizeFirstLetter(string) { | ||
@@ -271,11 +302,11 @@ return string.charAt(0).toUpperCase() + string.slice(1); | ||
function toTitleCase(str) { | ||
return str.replace( | ||
/\w\S*/g, | ||
function(txt) { | ||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); | ||
} | ||
); | ||
return str.replace( | ||
/\w\S*/g, | ||
function(txt) { | ||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); | ||
} | ||
); | ||
} | ||
function process_annotations(input_struct, visualizer_data, scale_applied){ | ||
function process_annotations(input_struct, visualizer_data, scale_applied) { | ||
input_struct = JSON.parse(JSON.stringify(input_struct)); // cheap deep clone | ||
@@ -286,37 +317,34 @@ var detections = input_struct[visualizer_data.list_key]; | ||
}); | ||
var count = Math.min(15, detections.length); | ||
var processed_annotations = []; | ||
for (var i = 0; i < count; i++) { | ||
for(var i = 0; i < count; i++) { | ||
var detection = detections[i]; | ||
var caption; | ||
if(visualizer_data.label_key=='demographic'){ | ||
if(detection[visualizer_data.label_key]){ | ||
caption = detection[visualizer_data.label_key]; // backwards compatible demog format | ||
}else{ | ||
//"White Male, 30-40" | ||
caption = detection['cultural_appearance']+' '+detection['gender']+', '+detection['age_range'][0]+'-'+detection['age_range'][1] | ||
} | ||
}else if(visualizer_data.label_key=='people'){ | ||
//produces "Sad, White Male, 30-40, Ted Cruz" | ||
var parts = []; | ||
if(detection['facial-expression-recognition'] && detection['facial-expression-recognition']['emotion'] != null){ | ||
parts.push( capitalizeFirstLetter(detection['facial-expression-recognition']['emotion']) ); | ||
} | ||
if(detection['demographic-recognition'] && detection['demographic-recognition']['cultural_appearance'] != null){ | ||
parts.push( detection['demographic-recognition']['cultural_appearance']+' '+detection['demographic-recognition']['gender']+', '+detection['demographic-recognition']['age_range'][0]+'-'+detection['demographic-recognition']['age_range'][1]); | ||
} | ||
if(detection['celebrity-recognition'] && detection['celebrity-recognition']['name'] != null && detection['celebrity-recognition']['name'] != 'unknown'){ | ||
parts.push( toTitleCase(detection['celebrity-recognition']['name']) ); | ||
} | ||
if(parts.length > 0){ | ||
caption = parts.join(', '); | ||
}else{ | ||
caption = "Face"; | ||
} | ||
}else{ | ||
caption = detection[visualizer_data.label_key]; // non demographic mode | ||
if(visualizer_data.label_key == 'demographic') { | ||
if(detection[visualizer_data.label_key]) { | ||
caption = detection[visualizer_data.label_key]; // backwards compatible demog format | ||
} else { | ||
//"White Male, 30-40" | ||
caption = detection['cultural_appearance'] + ' ' + detection['gender'] + ', ' + detection['age_range'][0] + '-' + detection['age_range'][1] | ||
} | ||
} else if(visualizer_data.label_key == 'people') { | ||
//produces "Sad, White Male, 30-40, Ted Cruz" | ||
var parts = []; | ||
if(detection['facial-expression-recognition'] && detection['facial-expression-recognition']['emotion'] != null) { | ||
parts.push(capitalizeFirstLetter(detection['facial-expression-recognition']['emotion'])); | ||
} | ||
if(detection['demographic-recognition'] && detection['demographic-recognition']['cultural_appearance'] != null) { | ||
parts.push(detection['demographic-recognition']['cultural_appearance'] + ' ' + detection['demographic-recognition']['gender'] + ', ' + detection['demographic-recognition']['age_range'][0] + '-' + detection['demographic-recognition']['age_range'][1]); | ||
} | ||
if(detection['celebrity-recognition'] && detection['celebrity-recognition']['name'] != null && detection['celebrity-recognition']['name'] != 'unknown') { | ||
parts.push(toTitleCase(detection['celebrity-recognition']['name'])); | ||
} | ||
if(parts.length > 0) { | ||
caption = parts.join(', '); | ||
} else { | ||
caption = "Face"; | ||
} | ||
} else { | ||
caption = detection[visualizer_data.label_key]; // non demographic mode | ||
} | ||
detection.bounding_box[0] *= scale_applied; | ||
@@ -326,3 +354,2 @@ detection.bounding_box[1] *= scale_applied; | ||
detection.bounding_box[3] *= scale_applied; | ||
processed_annotations.push({ | ||
@@ -337,3 +364,4 @@ bounding_box: detection.bounding_box, | ||
module.exports = { | ||
renderResultIntoElement: renderResultIntoElement | ||
renderResultIntoElement: renderResultIntoElement, | ||
renderAnnotatedResultIntoElement: renderAnnotatedResultIntoElement | ||
}; |
{ | ||
"name": "deepai", | ||
"version": "1.0.6", | ||
"version": "1.0.7", | ||
"license": "BSD-2-Clause", | ||
@@ -11,22 +11,23 @@ "repository": { | ||
"dependencies": { | ||
"axios": "0.18.0", | ||
"form-data": "2.3.3" | ||
"axios": "0.19.0", | ||
"form-data": "2.5.0" | ||
}, | ||
"devDependencies": { | ||
"babel-core": "^6.26.3", | ||
"babel-loader": "^6.2.2", | ||
"babel-plugin-transform-async-to-generator": "^6.5.0", | ||
"babel-polyfill": "^6.5.0", | ||
"babel-preset-es2015": "^6.5.0", | ||
"es6-promise": "^4.2.4", | ||
"grunt": "^1.0.2", | ||
"@babel/polyfill": "^7.4.4", | ||
"@babel/core": "^7.5.0", | ||
"@babel/plugin-transform-async-to-generator": "^7.5.0", | ||
"@babel/preset-env": "^7.5.2", | ||
"babel-loader": "^8.0.6", | ||
"es6-promise": "4.2.8", | ||
"grunt": "1.0.4", | ||
"grunt-banner": "^0.6.0", | ||
"grunt-cli": "^1.2.0", | ||
"grunt-contrib-clean": "^1.1.0", | ||
"grunt-contrib-clean": "2.0.0", | ||
"grunt-contrib-watch": "^1.0.0", | ||
"grunt-eslint": "^20.1.0", | ||
"grunt-webpack": "^1.0.18", | ||
"load-grunt-tasks": "^3.5.2", | ||
"webpack": "^1.13.1", | ||
"webpack-dev-server": "^1.14.1" | ||
"grunt-eslint": "22.0.0", | ||
"grunt-webpack": "3.1.3", | ||
"load-grunt-tasks": "5.0.0", | ||
"uglifyjs-webpack-plugin": "^2.1.3", | ||
"webpack": "4.35.3", | ||
"webpack-dev-server": "3.7.2" | ||
}, | ||
@@ -33,0 +34,0 @@ "scripts": { |
@@ -80,2 +80,30 @@ # DeepAI JS Client | ||
##### Rendering a result without an extra network request: | ||
The function renderAnnotatedResultIntoElement is for advanced users only. | ||
```js | ||
var resultAnnotated = { | ||
output_url: <Pass URL of the model output> | ||
output: <Pass the model output directly in case of JSON or text output> | ||
id: "fa616aa1-c762-4c98-b44e-75781627974a" <pass your job ID> | ||
inputs:[ | ||
{ | ||
is_img: true, | ||
url: (relative or absolute img url, annotations will be rendered on top of this result url.) | ||
} | ||
], | ||
visualizer_data: { | ||
list_key: 'Objects', (Name of the list property containing annotations) | ||
label_key: 'Object' (Name of the value property to label annotations with) | ||
}, | ||
scale_applied: 1.333 (Scale to multiply all detection x y coordinates by before rendering) | ||
}; | ||
deepai.renderAnnotatedResultIntoElement(resultAnnotated, document.getElementById('yourResultContainerId')); | ||
``` | ||
#### Node.js | ||
@@ -82,0 +110,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
148
1904516
17
12711
+ Addedaxios@0.19.0(transitive)
+ Addedfollow-redirects@1.5.10(transitive)
+ Addedform-data@2.5.0(transitive)
+ Addedis-buffer@2.0.5(transitive)
- Removedaxios@0.18.0(transitive)
- Removedfollow-redirects@1.15.9(transitive)
- Removedform-data@2.3.3(transitive)
- Removedis-buffer@1.1.6(transitive)
Updatedaxios@0.19.0
Updatedform-data@2.5.0