Trying to use Microsoft's Face API in Node.js but I am not able to load local images. What am I doing wrong? Thanks
I'm interfacing with a webcam and drawing the video out onto a canvas tag.
var canvas = document.getElementById("myCanvas"); // get the canvas from the page
var ctx = canvas.getContext("2d");
I have checked that I am getting an image using
var filename = new Date();
var imgData = canvas.toDataURL('image/jpeg');
var link = document.getElementById('saveImg');
link.href = imgData;
link.download = filename;
link.click();
and the image is saved fine...but I then try to do the following:
sendRequest(makeblob(imgData));
function sendRequest(imageURL) {
var returnData;
const request = require('request');
const subscriptionKey = '...';
const uriBase = 'https://eastus.api.cognitive.microsoft.com/face/v1.0/detect';
// Request parameters.
const params = {
'returnFaceId': 'true',
'returnFaceLandmarks': 'false',
'returnFaceAttributes': ''
};
const options = {
uri: uriBase,
qs: params,
body: '"' + imageURL + '"',
headers: {
'Content-Type': 'application/octet-stream',
'Ocp-Apim-Subscription-Key': subscriptionKey
}
};
request.post(options, (error, response, body) => {
if (error) {
console.log('Error: ', error);
return;
}
let jsonResponse = JSON.stringify(JSON.parse(body), null, ' ');
returnData = jsonResponse;
});
return returnData;
}
makeblob = function (dataURL) {
var BASE64_MARKER = ';base64,';
if (dataURL.indexOf(BASE64_MARKER) == -1) {
var parts = dataURL.split(',');
var contentType = parts[0].split(':')[1];
var raw = decodeURIComponent(parts[1]);
return new Blob([raw], { type: contentType });
}
var parts = dataURL.split(BASE64_MARKER);
var contentType = parts[0].split(':')[1];
var raw = window.atob(parts[1]);
var rawLength = raw.length;
var uInt8Array = new Uint8Array(rawLength);
for (var i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], { type: contentType });
}
And this simply returns
{
"error": {
"code": "InvalidImageSize",
"message": "Image size is too small."
}
}
How else am I supposed to de/encode the image?
“InvalidImageSize”, “message”: “Image size is too small.”
According to Face API - V1.0, we could know that Faces are detectable when its size is 36x36 to 4096x4096 pixels. If need to detect very small but clear faces, please try to enlarge the input image. If your image with clear faces, you could enlarge the local image with online tool.
Higher face image quality means better detection and recognition precision. Please consider high-quality faces: frontal, clear, and face size is 200x200 pixels (100 pixels between eyes) or bigger.
JPEG, PNG, GIF (the first frame), and BMP format are supported. The allowed image file size is from 1KB to 6MB.
Faces are detectable when its size is 36x36 to 4096x4096 pixels. If need to detect very small but clear faces, please try to enlarge the input image.
I know I am late here, but I have find something which surely helps you.
Sadly, the Emotion and Face APIs do not support chunked transfers, as noted here. The 'workaround' is to load the image bits synchronously prior to making the web request. The code snippet for that should be like this:
const request = require('request');
const fs = require('fs');
function sendRequest(imageData) {
const uriBase = 'https://eastus.api.cognitive.microsoft.com/face/v1.0/detect';
// Request parameters.
const params = {
'returnFaceId': 'true',
'returnFaceLandmarks': 'false',
'returnFaceAttributes': ''
};
const options = {
uri: uriBase,
qs: params,
body: fs.readFileSync(imgData),
headers: {
'Content-Type': 'application/octet-stream',
'Ocp-Apim-Subscription-Key': subscriptionKey
}
};
request.post(options, (error, response, body) => {
if (error) {
console.log('Error: ', error);
return;
}
let jsonResponse = JSON.stringify(JSON.parse(body), null, ' ');
returnData = jsonResponse;
});
return returnData;
}
Related
I was scouring through numerous other questions exactly asking the same. But couldnt get any of them to work in my case.
Basically what I need is to fetch a gif file from an API and display it. The image returned is a base64 encoded gif image built as shown below;
with io.BytesIO() as newfp:
....logic....
buf = base64.b64encode(newfp.getvalue()).decode()
return {
"statusCode": 200,
"headers": {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Content-Type",
"Content-Type": 'image/gif',
},
"body": buf,
"isBase64Encoded": True,
}
What I have come up is as shown below based on this link,
fetch('https://xxxxxxx.execute-api.us-west-2.amazonaws.com/v1/xxxx', {
method: 'POST',
body: ""
}).then((response) => {
response.arrayBuffer().then((buffer) => {
var base64Flag = 'data:image/gif;base64,';
var imageStr = arrayBufferToBase64(buffer);
var image = new Image();
image.src = base64Flag + imageStr;
document.body.appendChild(image);
});
});
function arrayBufferToBase64(buffer) {
console.log(buffer);
var binary = '';
var bytes = [].slice.call(new Uint8Array(buffer));
bytes.forEach((b) => binary += String.fromCharCode(b));
return window.btoa(binary);
};
Solved the problem with the below code.
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://xxxxx.execute-api.us-west-2.amazonaws.com/v1/xxxx");
xhr.responseType = "blob";
xhr.onload = response;
xhr.send();
function response(e) {
(this.response.text().then(imageStr => {
var base64Flag = 'data:image/gif;base64,';
var image = new Image();
image.src = base64Flag + imageStr;
document.body.appendChild(image);
}));
}
I'm trying to send my canvas image to controller. I tried using ajax but it doesn't seem to work. Hope you can help me. This is my code.
function takeSnapshot() {
var video = document.querySelector('video')
, canvas;
var img = document.querySelector('img') || document.createElement('img');
var context;
var width = video.offsetWidth
, height = video.offsetHeight;
canvas = canvas || document.createElement('canvas');
canvas.width = width;
canvas.height = height;
context = canvas.getContext('2d');
context.drawImage(video, 0, 0, width, height);
img.src = canvas.toDataURL('image/png');
document.body.appendChild(img);
var dataURL = canvas.toDataURL('image/png');
var blob = dataURItoBlob(dataURL);
var fd = new FormData(document.forms[0]);
fd.append("uploadfile", blob, "test");
$.ajax({
type: "POST",
enctype: 'multipart/form-data',
url: "/api/file/upload",
data: fd,
processData: false, //prevent jQuery from automatically transforming the data into a query string
contentType: false,
cache: false,
success: (data) => {
alert("shit");
},
error: (e) => {
alert("error");
}
});
}
function dataURItoBlob(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = unescape(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
And this is my controller that receives the submitted image.
#PostMapping("/api/file/upload")
public String uploadMultipartFile(#RequestParam("uploadfile") MultipartFile file) {
try {
fileStorage.store(file);
return "File uploaded successfully! -> filename = " + file.getOriginalFilename();
} catch (Exception e) {
return "Error -> message = " + e.getMessage();
}
}
I tried setting the value of input file type mannually but others said that it may lead to security problems. And I also I can't make it work. I want to do direct image capturing and save the image to server. I'm using spring and hibernate. Hoping you could help me. Thank you !!
This question already has answers here:
How to crop canvas.toDataURL
(2 answers)
Closed 5 years ago.
I'm trying to crop the image and send it to the server.
I have code as follows:
<div id="result">
<img id="photo" src="http://localhost:63342/frontend/assets/images/audi.png?_ijt=hj4q47j1ghucndpqm4t9j0p08r"/>
<canvas id="canvas"></canvas>
<canvas id="crop-area" class="hide"></canvas>
</div>
The important one in this case is the second canvas <canvas id="crop-area" class="hide"></canvas>.
When I'm done with selecting (cropping photo) I have the above canvas with a rect inside, as follows:
That with blue border is the cropped part which I want to upload to the server. I tried it with Html2Canvas but that library takes the screenshot of the div, thus in my case it uploads everything to the server, but I want only the blue part, thus only this:
The code for uploading is as follows:
function renderMessage(wrapper, message){
if(wrapper.classList.contains("fadeOut")) wrapper.classList.remove("fadeOut");
if(wrapper.classList.contains("fadeIn")) wrapper.classList.remove("fadeIn");
wrapper.classList.add("fadeIn");
setTimeout(function(){
wrapper.classList.add("fadeOut");
}, 4000);
wrapper.innerHTML = message;
save_button_label.classList.remove('hide');
save_button_spinner.classList.add('hide');
}
function upload_to_server(canvasData){
return fetch(api_url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({photo: canvasData})
}).then(function (value) {
if(value.ok){
return value.json().then(function (response) {
if(response.status === "success"){
drag = false;
buttons_shown = false;
selection_editing_buttons.classList.add("hide");
selection_editing_description.classList.remove("hide");
update = true;
rectangles = [];
return renderMessage(success, upload_success_msg);
}else{
return renderMessage(errors, upload_error_msg);
}
})
}
}).catch(function (reason) {
return renderMessage(errors, upload_error_msg);
})
}
function onSaveChanges(){
save_button_label.classList.add('hide');
save_button_spinner.classList.remove('hide');
console.log(crop_rect.startX); // I have start position x
console.log(crop_rect.startY); // I have start position x
console.log(crop_rect.w); // I have width
console.log(crop_rect.h); // I have height
html2canvas(document.getElementById("result")).then(function(canvas) {
var canvasData = canvas.toDataURL("image/" + image_type + "");
upload_to_server(canvasData)
});
}
Is there any possibility to get the cropped part and upload it to the server. I have start position x, start position y and width and height of the cropped part.
Any idea?
Canvas toBlob polyfill (better look for a better option):
if (!HTMLCanvasElement.prototype.toBlob) {
Object.defineProperty(HTMLCanvasElement.prototype, "toBlob", {
value: function(callback, type, quality) {
const binStr = atob(this.toDataURL(type, quality).split(",")[1]), len = binStr.length, arr = new Uint8Array(len);
for (let i = 0; i < len; i++) arr[i] = binStr.charCodeAt(i);
callback(new Blob([arr], { type: type || "image/png" }));
}
});
}
Uploading canvas to server:
croppedCanvas.toBlob((blob) => {
formData.append("file", blob);
fetch("/" /* api_url in your case */, {
method: "POST",
body: formData
}).then(r => r.json()).then(d => {
//Your logic, in my case I get JSON from server
});
}, "image/jpeg", 0.90);
I used a jquery plugin to crop images. The plugin will crop the image and give it to me as a base64 encoded string. In order to upload it to S3, I need to convert this into a file and pass the file into the upload function. How can I do this? I tried a lot of things including decoding the string using atob. None worked.
Here's the code of the cropping plugin ('croppie') which gives me the encoded string :
imageCropper.croppie('result', {
type: 'canvas',
size: 'viewport',
format: 'jpeg'
}).then(function (resp) {
updateAvatar(resp);
});
I pass it to a function called updateAvatar. Here's the updateAvatar function :
updateAvatar({Meteor, Slingshot}, avatar) {
const uploader = new Slingshot.Upload('userAvatarUpload');
uploader.send(avatar, function (error, downloadUrl) {
if (error) {
// Log service detailed response.
console.error('Error uploading', uploader.xhr.response);
console.log(error);
}
else {
console.log('uploaded', downloadUrl);
}
});
}
The uploader.send function expects a file or a url. It won't accept my encoded string.
The plugin which I use to upload files to S3 : https://github.com/CulturalMe/meteor-slingshot
It seems like the missing 'brick' in your code is a function that would take a base64-encoded image and convert it to a Blob.
So, I'm going to focus on that part exclusively with a short comment for each step.
The following function expects a string such as:
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICA...
function base64ImageToBlob(str) {
// extract content type and base64 payload from original string
var pos = str.indexOf(';base64,');
var type = str.substring(5, pos);
var b64 = str.substr(pos + 8);
// decode base64
var imageContent = atob(b64);
// create an ArrayBuffer and a view (as unsigned 8-bit)
var buffer = new ArrayBuffer(imageContent.length);
var view = new Uint8Array(buffer);
// fill the view, using the decoded base64
for(var n = 0; n < imageContent.length; n++) {
view[n] = imageContent.charCodeAt(n);
}
// convert ArrayBuffer to Blob
var blob = new Blob([buffer], { type: type });
return blob;
}
Convert the base64 string to blob, to be used in upload to S3. There are tidier ways of doing this of course! :)
Original SO Answer here: https://stackoverflow.com/a/16245768/1350913
imageCropper.croppie('result', {
type: 'canvas',
size: 'viewport',
format: 'jpeg'
}).then(function(resp) {
var contentType = 'image/png';
var s3Blob = b64toBlob(resp, contentType);
updateAvatar(s3Blob);
});
updateAvatar({
Meteor,
Slingshot
}, avatar) {
const uploader = new Slingshot.Upload('userAvatarUpload');
uploader.send(avatar, function(error, downloadUrl) {
if (error) {
// Log service detailed response.
console.error('Error uploading', uploader.xhr.response);
console.log(error);
} else {
console.log('uploaded', downloadUrl);
}
});
}
function b64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var b64DataString = b64Data.substr(b64Data.indexOf(',') + 1);
var byteCharacters = atob(b64DataString);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {
type: contentType
});
return blob;
}
The base64ToFile function (.ts) converts the base64 string into a File. The codeUnits and charCodes part make sure you can read Unicode text as ASCII by converting the string such that each 16-bit unit occupies only one byte.
Finally the download function (.ts) downloads the converted file from your browser to your local machine.
function base64ToFile(base64data: string, myFileNameWithdotExtention: string,
fileType: string): File {
let content = decodeURIComponent(escape(window.atob(base64data)));
let fileName = myFileNameWithdotExtention;
const codeUnits = Uint16Array.from(
{ length: content.length },
( _, index) => content.charCodeAt(index)
);
const charCodes = new Uint8Array(codeUnits.buffer);
const type = fileType; // 'text/csv' for instance
const blob = new Blob([charCodes], { type });
return new File([blob], fileName, { lastModified: new Date().getTime(), type });
}
download(){
let res: string = getMyDataFromSomewhere(); // base64 string
let data = base64ToFile(res);
let element = document.createElement('a');
window.URL = window.URL || window.webkitURL;
element.setAttribute('href', window.URL.createObjectURL(data));
element.setAttribute('download', data.name);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
I am posting the base64 clipboard image to server (node) and I am saving the base64 to disk. For some reason the image is corrupted.
Client side logic of posting:
function sendData($http, clipboardImage) {
// $http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";
// var imgData = JSON.stringify(clipboardImage);
//var data = {"imgdata" : clipboardImage};
var url = "http://localhost:3000/pad/img/";
$http({
method: 'POST',
url: url,
data: "data=" + clipboardImage
});
}
$("[ng-model='html']").delegate("p", "paste", function(event) {
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
console.log(JSON.stringify(items)); // will give you the mime types
// find pasted image among pasted items
var blob = null;
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") === 0) {
blob = items[i].getAsFile();
}
}
// load image if there is a pasted image
if (blob !== null) {
var reader = new FileReader();
reader.onload = function(e) {
sendData($http, e.target.result);
};
reader.readAsDataURL(blob);
}
});
Server logic:
app.post("/pad/img/", function(req, res) {
var imgB64Data = req.body.data;
var decodedImg = decodeBase64Image(imgB64Data);
var imageBuffer = decodedImg.data;
var type = decodedImg.type;
var extension = mime.extension(type);
var fileName = "image." + extension;
try {
fs.writeFile(fileName, imageBuffer, function(err) {
console.log(err);
});
} catch (err) {
console.error(err);
}
});
function decodeBase64Image(dataString) {
var matches = dataString.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/),
response = {};
if (matches.length !== 3) {
return new Error('Invalid input string');
}
response.type = matches[1];
response.data = new Buffer(matches[2], 'base64');
return response;
}
The image is being saved successfully but it seems to be corrupted. Can you please point out what may be missing?
Have you tried explicitly setting the file encoding when calling fs.writeFile?
try {
fs.writeFile(fileName, imageBuffer, {encoding:'utf8'}, function(err) {
console.log(err);
});
} catch (err) {
console.error(err);
}
NodeJS Docs: fs.writeFile(filename, data\[, options\], callback)