I'm using face-api.js Javascript API to develop a web app that user uploads her/his picture and we want to detect faces in the picture.
this is my HTML codes:
<input type="file" id="user_pic" accept="image/x-png,image/gif,image/jpeg">
<img src="images/250x250.webp" id="preview" alt="">
<canvas id="canvas"></canvas>
<script src="scripts/tfjs.js"></script>
<script src="scripts/face-api.min.js"></script>
<script src="scripts/index.js"></script>
and following code are what I wrote to face detection:
const model = tf.loadLayersModel('./web_model/vgg_model.json')
const user_pic = document.getElementById('user_pic')
const preview = document.getElementById('preview')
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
window.onload = function() {
canvas.width = preview.width;
canvas.height = preview.height;
ctx.drawImage(preview, 0, 0);
};
preview.onclick = () => user_pic.click()
const MODEL_URL = '../faceapi_models'
Promise.all([
faceapi.nets.ssdMobilenetv1.loadFromUri(MODEL_URL),
faceapi.nets.faceRecognitionNet.loadFromUri(MODEL_URL),
faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL),])
.then((val) => {
console.log('val')
})
.catch((err) => {
console.log('err')
})
user_pic.addEventListener('change', () => {
const reader = new FileReader()
reader.onload = (e) => {
const img = new Image();
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
};
img.src = e.target.result;
}
reader.readAsDataURL(user_pic.files[0]);
detectFaces(user_pic.files[0])
})
async function detectFaces(input) {
let imgURL = URL.createObjectURL(input)
const imgElement = new Image()
imgElement.src = imgURL
const results = await faceapi.detectAllFaces(imgElement)
// .withFaceLandmarks()
// .withFaceExpressions()
.then(results => {
if (Array.isArray(results) && results.forEach) {
results.forEach(result => {
console.log(result)
const { x, y, width, height } = result.box;
ctx.lineWidth = 3;
ctx.strokeRect(x, y, width, height);
});
} else {
console.error('Results is not an array or does not have a forEach function.');
}
});
}
So far I have only used face-api.min.js file and all things work fine and after selecting image File , face is recognized. But as soon as I added tfjs.js file to use const model = tf.loadLayersModel('./web_model/vgg_model.json') method I got new following Errors:
Uncaught (in promise) Error: SsdMobilenetv1 - load model before inference
And after a few seonds this error shown in console :
Uncaught (in promise) TypeError: Og(...).platform.isTypedArray is not a function
What is problem ?
Related
I'm using face-api.js Javascript API to develop a web app that user uploads her/his picture and we want to detect faces in the picture.
this is my HTML codes:
<input type="file" id="user_pic" accept="image/x-png,image/gif,image/jpeg">
<img src="images/250x250.webp" id="preview" alt="">
and following code are what I wrote to face detection:
document.addEventListener('DOMContentLoaded', function() {
run()
});
async function run() {
// load the models
await faceapi.loadMtcnnModel('../faceapi_models')
await faceapi.loadFaceRecognitionModel('../faceapi_models')
}
const user_pic = document.getElementById('user_pic')
const preview = document.getElementById('preview')
user_pic.addEventListener('change', () => {
const reader = new FileReader()
reader.onload = (e) => {
preview.src = e.target.result
}
reader.readAsDataURL(user_pic.files[0]);
detectFaces(user_pic.files[0])
})
preview.onclick = () => user_pic.click()
async function detectFaces(input) {
let imgURL = URL.createObjectURL(input)
const imgElement = new Image()
imgElement.src = imgURL
const results = faceapi.detectAllFaces(imgElement)
.withFaceLandmarks()
.withFaceExpressions()
console.log(results)
results.forEach(result => {
const { x, y, width, height } = result.detection.box;
ctx.strokeRect(x, y, width, height);
});
}
Now whenever I select an image results variable is empty and this error occured:
Uncaught (in promise) TypeError: results.forEach is not a function
The withFaceExpressions() method is async. You should use await or then().
Documentation for the reference
Using await
const results = await faceapi.detectAllFaces(imgElement)
.withFaceLandmarks()
.withFaceExpressions();
Using then()
faceapi.detectAllFaces(imgElement)
.withFaceLandmarks()
.withFaceExpressions()
.then( results => {
results.forEach(result => {
const { x, y, width, height } = result.detection.box;
ctx.strokeRect(x, y, width, height);
});
});
I want to check the image alpha channel to see if it has a background, and reject it if true, allow it if false but when I upload an image using the const changefile, the hasAlpha function doesn't serve an 'error' alert if the image has a background.
Function for checking if the image has a transparent background:
export function hasAlpha(file) {
return new Promise((resolve, reject) => {
let hasAlpha = false;
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const img = new Image();
img.crossOrigin = "Anonymous";
img.onerror = reject;
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
for (let j = 0; j < imgData.length; j += 4) {
if (imgData[j + 3] < 255) {
hasAlpha = true;
break;
}
}
resolve(hasAlpha);
};
img.src = URL.createObjectURL(file);
});
}
Where the image is uploaded:
const changefile = async (e) => {
if (e.target.id === "mainImg") {
1;
let file = e.target.files[0] ? e.target.files[0] : "";
if (file) {
let extension = file.name.substr(file.name.lastIndexOf(".") + 1);
if (validExtensions.includes(extension)) {
setTempImg(URL.createObjectURL(file));
setstate({
...state,
image: file
});
if (hasAlpha(URL.createObjectURL(file))) {
alert(hasAlpha(URL.createObjectURL(file)));
} else {
alert("error");
}
} else {
setstate({
...state,
image: ""
});
}
} else {
setstate({
...state,
image: ""
});
setTempImg("");
}
}
};
I am here from your Google Docs bug. I have already sent a proposal to you. I tested this code. If it doesn't work in your project, it means another bug exists in your React project. To solve those bugs I need to see your whole react component code.
function hasAlpha(file) {
return new Promise((resolve, reject) => {
const img = new Image()
// create image from file
img.src = URL.createObjectURL(file)
img.onerror = reject
img.onload = () => {
// create canvas
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = img.width
canvas.height = img.height
ctx.drawImage(img, 0, 0)
// get image data
const data = ctx.getImageData(0, 0, canvas.width, canvas.height)
// check if image has any transparent background
const hasTransparent = [...data.data].some((value, index) => {
return index % 4 === 3 && value < 255
})
return hasTransparent ? resolve(true) : resolve(false)
}
})}
You have to wait before hasAlpha() resolve or reject. So, you should call await hasAlpha(file) and wrap entire call with try catch. If promise rejected you can access it in catch block.
try {
if(await hasAlpha(file)) {
// promise resloved, image is transparent
} else {
// promise resloved, image is not transparent
}
} catch (e) {
// promise rejected
}
At first I thought it should be as easy as:
const img = document.createElement('img');
img.onload = () => { ... }
img.onerror = () => { ... }
img.src = url;
But then it turned out I need to draw it on a canvas, then toBlob(). And don't forget to add CORS and crossorigin="anonymous" to the img tag. Isn't it a bit too involved? Is there a better way?
To show you the final solution (you need CORS headers):
function getFileFromURL(url) {
return new Promise((resolve, reject) => {
const fileName = url.split('/').pop();
const img = document.createElement('img');
img.setAttribute('crossorigin', 'anonymous');
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
canvas.toBlob(blob => {
resolve(new File([blob], fileName));
});
};
img.onerror = () => {
reject('something went wrong');
};
img.src = url;
})
}
The solution suggested by CBroe is arguably better:
function getFileFromURL(url) {
const fileName = url.split('/').pop();
return fetch(url)
.then(response => {
return new File([response.data], fileName);
});
}
Make sure you add CORS headers to the request(s).
I'm using Mozilla PDF.js library and the demo works properly but the rendered PDF doesn't show forms / Acroforms.
Please help me out, how could I switch-on the forms and let the users to fulfill them.
I use a canvas at HTML:
<body>
<canvas id="pdf-render"></canvas>
<script src="https://mozilla.github.io/pdf.js/build/pdf.js"></script>
<script src="js/main.js"></script>
</body>
And my main.js:
const url = 'pdf.pdf';
let pdfDoc = null,
pageNum = 1,
pageIsRendering = false,
pageNumIsPending = null;
const scale = 1,
canvas = document.querySelector('#pdf-render'),
ctx = canvas.getContext('2d');
// Render the page
const renderPage = num => {
pageIsRendering = true;
// Get page
pdfDoc.getPage(num).then(page => {
// Set scale
const viewport = page.getViewport({ scale });
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderCtx = {
canvasContext: ctx,
viewport
};
page.render(renderCtx).promise.then(() => {
pageIsRendering = false;
if (pageNumIsPending !== null) {
renderPage(pageNumIsPending);
pageNumIsPending = null;
}
});
});
};
// Check for pages rendering
const queueRenderPage = num => {
if (pageIsRendering) {
pageNumIsPending = num;
} else {
renderPage(num);
}
};
// Get Document
pdfjsLib
.getDocument(url)
.promise.then(pdfDoc_ => {
pdfDoc = pdfDoc_;
renderPage(pageNum);
})
.catch(err => {
// Display error
});
The PDF looks nice and appear but my main usage would be the forms. I'm sure it can be done, but I'm quite newbie at PDF.js lib.
So the alert gives undefined values for the width and height. I think the w and h values of the image from the img.onload calculation is not being passed to the values to return, or it may be returning w and h before the onload calculates them:
function getMeta(url){
var w; var h;
var img=new Image;
img.src=url;
img.onload=function(){w=this.width; h=this.height;};
return {w:w,h:h}
}
// "http://snook.ca/files/mootools_83_snookca.png" //1024x678
// "http://shijitht.files.wordpress.com/2010/08/github.png" //128x128
var end = getMeta("http://shijitht.files.wordpress.com/2010/08/github.png");
var w = end.w;
var h = end.h;
alert(w+'width'+h+'height');
How can I have the alert show the correct width and height?
http://jsfiddle.net/YtqXk/
Get image size with JavaScript
In order to read the data from an image you'll need to make sure it's first loaded. Here's a callback-based approach and two promise-based solutions:
Callback
const getMeta = (url, cb) => {
const img = new Image();
img.onload = () => cb(null, img);
img.onerror = (err) => cb(err);
img.src = url;
};
// Use like:
getMeta("https://i.stack.imgur.com/qCWYU.jpg", (err, img) => {
console.log(img.naturalWidth, img.naturalHeight);
});
Using the load Event listener (Promise):
const getMeta = (url) =>
new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = (err) => reject(err);
img.src = url;
});
// Usage example:
;(async() => {
const img = await getMeta('https://i.stack.imgur.com/qCWYU.jpg');
console.dir(img.naturalHeight + ' ' + img.naturalWidth);
})();
Using HTMLImageElement.decode() (Promise)
const getMeta = async (url) => {
const img = new Image();
img.src = url;
await img.decode();
return img
};
// Usage example:
getMeta('https://i.stack.imgur.com/qCWYU.jpg').then(img => {
console.dir(img.naturalHeight +' '+ img.naturalWidth);
});
MDN Docs: HTMLImageElement
Just pass a callback as argument like this:
function getMeta(url, callback) {
const img = new Image();
img.src = url;
img.onload = function() { callback(this.width, this.height); }
}
getMeta(
"http://snook.ca/files/mootools_83_snookca.png",
(width, height) => { alert(width + 'px ' + height + 'px') }
);
ES6
Using async/await you can do below getMeta function in sequence-like way and you can use it as follows (which is almost identical to code in your question (I add await keyword and change variable end to img, and change var to let keyword). You need to run getMeta by await only from async function (run).
function getMeta(url) {
return new Promise((resolve, reject) => {
let img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject();
img.src = url;
});
}
async function run() {
let img = await getMeta("http://shijitht.files.wordpress.com/2010/08/github.png");
let w = img.width;
let h = img.height;
size.innerText = `width=${w}px, height=${h}px`;
size.appendChild(img);
}
run();
<div id="size" />
Rxjs
const { race, fromEvent, map, mergeMap, of } = rxjs;
function getMeta(url) {
return of(url).pipe(
mergeMap((path) => {
const img = new Image();
let load = fromEvent(img, 'load').pipe(map(_=> img))
let error = fromEvent(img, 'error').pipe(mergeMap((err) => throwError(() => err)));
img.src = path;
return race(load, error);
})
);
}
let url = "http://shijitht.files.wordpress.com/2010/08/github.png";
getMeta(url).subscribe(img=> {
let w = img.width;
let h = img.height;
size.innerText = `width=${w}px, height=${h}px`;
size.appendChild(img);
}, e=> console.log('Load error'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.5.2/rxjs.umd.min.js" integrity="sha512-wBEi/LQM8Pi08xK2jwHJNCiHchHnfcJao0XVQvkTGc91Q/yvC/6q0xPf+qQr54SlG8yRbRCA8QDYrA98+0H+hg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="size" />
The w and h variables in img.onload function are not in the same scope with those in the getMeta() function. One way to do it, is as follows:
Fiddle: http://jsfiddle.net/ppanagi/28UES/2/
function getMeta(varA, varB) {
if (typeof varB !== 'undefined') {
alert(varA + ' width ' + varB + ' height');
} else {
var img = new Image();
img.src = varA;
img.onload = getMeta(this.width, this.height);
}
}
getMeta("http://snook.ca/files/mootools_83_snookca.png");
Get image size with jQuery
(depending on which formatting method is more suitable for your preferences):
function getMeta(url){
$('<img/>',{
src: url,
on: {
load: (e) => {
console.log('image size:', $(e.target).width(), $(e.target).height());
},
}
});
}
or
function getMeta(url){
$('<img/>',{
src: url,
}).on({
load: (e) => {
console.log('image size:', $(e.target).width(), $(e.target).height());
},
});
}
You will can try package.
import getImageSize from 'image-size-from-url';
const {width, height} = await getImageSize('URL');