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);
});
});
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="">
<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 ?
Help! I'm trying to combine images using canvas but the output always comes out as a blank box. I can't figure out what is going wrong, my code is below:
const Canvas = require('canvas');
const theLayers=['https://raw.githubusercontent.com/Iceee1000/SpaceVizsla/main/MediaAssets/pixelVizsla/testing_01.png',
'https://raw.githubusercontent.com/Iceee1000/SpaceVizsla/main/MediaAssets/pixelVizsla/B_02.png',
'https://raw.githubusercontent.com/Iceee1000/SpaceVizsla/main/MediaAssets/pixelVizsla/testing_02.png'];
//not-working function to combine multiple layers of images from the web.
const CombineLayers = async(layers) => {
console.log('combining layers')
let canvas=Canvas.createCanvas(250, 250);
let context=canvas.getContext('2d');
for (let layer of layers){
var img = new Image();
img.origin = 'anonymous';
img.src = layer;
img.onload = function(){
context.drawImage(img, 0, 0, 250, 250);
}
}
return canvas.toDataURL("image/png");
};
const dothings=async()=>{
const result= await CombineLayers(theLayers);
console.log(result);
}
dothings();
Solved my issue: Can not just image data directly from a URL, I needed to load/buffer it first. Solved below with axios:
const Canvas = require('canvas');
const axios = require('axios')
function getBase64(url) {
return axios
.get(url, {
responseType: 'arraybuffer'
})
.then(response => Buffer.from(response.data, 'binary').toString('base64'))
}
const theLayers=['https://imageURL.png',
'https://imageURL.png',
'https://imageURL.png'];
const CombineLayers = async(layers) => {
console.log('combining layers')
let canvas=Canvas.createCanvas(250, 250);
let context=canvas.getContext('2d');
for (let layer of layers){
const data = await getBase64(layer);
let datastring='data:image/png;base64,'+data
const img = await Canvas.loadImage(datastring);
context.drawImage(img, 0, 0, 250, 250);
}
return canvas.toDataURL("image/png");
};
const dothings=async()=>{
const result= await CombineLayers(theLayers);
console.log(result);
}
dothings();
I'm trying to create a JavaScript function that will return a dataURL from a JPEG. This function is intended to be called multiple times in the creation of a pdf document in a Vue.js application.
The following is code I've managed to cobble together from various web sites in seeking code examples for jsPDF use.
async loadImg (url) {
var dataURL = null
var toDataURL = async function (url) {
var img = new Image()
img.onError = function () {
alert('Cannot load image: "' + url + '"')
}
img.onload = async function () {
var canvas = document.createElement('canvas')
var context = canvas.getContext('2d')
canvas.height = this.naturalHeight
canvas.width = this.naturalWidth
context.drawImage(this, 0, 0)
dataURL = canvas.toDataURL('image/jpeg')
console.log('onload ' + dataURL)
}
img.src = url
}
await toDataURL(url)
console.log('end of function ' + dataURL)
return dataURL
}
I've tried using a callback approach, but no matter how what I've done I ultimately end up in the same state the console shows the 'end of function' as a null and then a few milliseconds later the onload remark shows up with a long string, which I assume is the dataURL of the graphic (jpg)
OK I thought async / await construct was the same as using promise.. but just to be on the safe side I rewrote my code using promise
toDataURL (url) {
return new Promise(function (resolve, reject) {
var img = new Image()
img.onError = function () {
reject(Error('Cannot load image: "' + url + '"'))
}
img.onload = async function () {
var canvas = document.createElement('canvas')
var context = canvas.getContext('2d')
canvas.height = this.naturalHeight
canvas.width = this.naturalWidth
context.drawImage(this, 0, 0)
resolve(canvas.toDataURL('image/jpeg'))
}
img.src = url
})
}
// in the function to create the pdf
imageData = toDataURL(url).then(function (response) {
console.log('Success!', response)
}, function (error) {
console.error('Failed!', error)
})
}
There are three jpgs that the main is trying to include in the pdf
In the console I see:
Report.vue?./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options:277 PromiseĀ {<pending>}
Report.vue?./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options:277 PromiseĀ {<pending>}
Report.vue?./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options:277 PromiseĀ {<pending>}
Report.vue?./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options:284 841.89 595.28
Report.vue?./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options:272 Success! data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCALaBEcDASIAAhEBAxEB/8QAHgAAAAcBAQEBAAAAAAAAAAAAAAIDBAUGBw ...
There are two additional Success! data:image/ ...
My interpretation is while the results are different in that I get a promise object, which is pending then I get the image data. I'm still no further ahead.
Google's "Report a Bug" or "Feedback Tool" lets you select an area of your browser window to create a screenshot that is submitted with your feedback about a bug.
Screenshot by Jason Small, posted in a duplicate question.
How are they doing this? Google's JavaScript feedback API is loaded from here and their overview of the feedback module will demonstrate the screenshot capability.
JavaScript can read the DOM and render a fairly accurate representation of that using canvas. I have been working on a script which converts HTML into a canvas image. Decided today to make an implementation of it into sending feedbacks like you described.
The script allows you to create feedback forms which include a screenshot, created on the client's browser, along with the form. The screenshot is based on the DOM and as such may not be 100% accurate to the real representation as it does not make an actual screenshot, but builds the screenshot based on the information available on the page.
It does not require any rendering from the server, as the whole image is created on the client's browser. The HTML2Canvas script itself is still in a very experimental state, as it does not parse nearly as much of the CSS3 attributes I would want it to, nor does it have any support to load CORS images even if a proxy was available.
Still quite limited browser compatibility (not because more couldn't be supported, just haven't had time to make it more cross browser supported).
For more information, have a look at the examples here:
http://hertzen.com/experiments/jsfeedback/
edit
The html2canvas script is now available separately here and some examples here.
edit 2
Another confirmation that Google uses a very similar method (in fact, based on the documentation, the only major difference is their async method of traversing/drawing) can be found in this presentation by Elliott Sprehn from the Google Feedback team:
http://www.elliottsprehn.com/preso/fluentconf/
Your web app can now take a 'native' screenshot of the client's entire desktop using getUserMedia():
Have a look at this example:
https://www.webrtc-experiment.com/Pluginfree-Screen-Sharing/
The client will have to be using chrome (for now) and will need to enable screen capture support under chrome://flags.
PoC
As Niklas mentioned you can use the html2canvas library to take a screenshot using JS in the browser. I will extend his answer in this point by providing an example of taking a screenshot using this library ("Proof of Concept"):
function report() {
let region = document.querySelector("body"); // whole screen
html2canvas(region, {
onrendered: function(canvas) {
let pngUrl = canvas.toDataURL(); // png in dataURL format
let img = document.querySelector(".screen");
img.src = pngUrl;
// here you can allow user to set bug-region
// and send it with 'pngUrl' to server
},
});
}
.container {
margin-top: 10px;
border: solid 1px black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<div>Screenshot tester</div>
<button onclick="report()">Take screenshot</button>
<div class="container">
<img width="75%" class="screen">
</div>
In report() function in onrendered after getting image as data URI you can show it to the user and allow him to draw "bug region" by mouse and then send a screenshot and region coordinates to the server.
In this example async/await version was made: with nice makeScreenshot() function.
UPDATE
Simple example which allows you to take screenshot, select region, describe bug and send POST request (here jsfiddle) (the main function is report()).
async function report() {
let screenshot = await makeScreenshot(); // png dataUrl
let img = q(".screen");
img.src = screenshot;
let c = q(".bug-container");
c.classList.remove('hide')
let box = await getBox();
c.classList.add('hide');
send(screenshot,box); // sed post request with bug image, region and description
alert('To see POST requset with image go to: chrome console > network tab');
}
// ----- Helper functions
let q = s => document.querySelector(s); // query selector helper
window.report = report; // bind report be visible in fiddle html
async function makeScreenshot(selector="body")
{
return new Promise((resolve, reject) => {
let node = document.querySelector(selector);
html2canvas(node, { onrendered: (canvas) => {
let pngUrl = canvas.toDataURL();
resolve(pngUrl);
}});
});
}
async function getBox(box) {
return new Promise((resolve, reject) => {
let b = q(".bug");
let r = q(".region");
let scr = q(".screen");
let send = q(".send");
let start=0;
let sx,sy,ex,ey=-1;
r.style.width=0;
r.style.height=0;
let drawBox= () => {
r.style.left = (ex > 0 ? sx : sx+ex ) +'px';
r.style.top = (ey > 0 ? sy : sy+ey) +'px';
r.style.width = Math.abs(ex) +'px';
r.style.height = Math.abs(ey) +'px';
}
//console.log({b,r, scr});
b.addEventListener("click", e=>{
if(start==0) {
sx=e.pageX;
sy=e.pageY;
ex=0;
ey=0;
drawBox();
}
start=(start+1)%3;
});
b.addEventListener("mousemove", e=>{
//console.log(e)
if(start==1) {
ex=e.pageX-sx;
ey=e.pageY-sy
drawBox();
}
});
send.addEventListener("click", e=>{
start=0;
let a=100/75 //zoom out img 75%
resolve({
x:Math.floor(((ex > 0 ? sx : sx+ex )-scr.offsetLeft)*a),
y:Math.floor(((ey > 0 ? sy : sy+ey )-b.offsetTop)*a),
width:Math.floor(Math.abs(ex)*a),
height:Math.floor(Math.abs(ex)*a),
desc: q('.bug-desc').value
});
});
});
}
function send(image,box) {
let formData = new FormData();
let req = new XMLHttpRequest();
formData.append("box", JSON.stringify(box));
formData.append("screenshot", image);
req.open("POST", '/upload/screenshot');
req.send(formData);
}
.bug-container { background: rgb(255,0,0,0.1); margin-top:20px; text-align: center; }
.send { border-radius:5px; padding:10px; background: green; cursor: pointer; }
.region { position: absolute; background: rgba(255,0,0,0.4); }
.example { height: 100px; background: yellow; }
.bug { margin-top: 10px; cursor: crosshair; }
.hide { display: none; }
.screen { pointer-events: none }
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<body>
<div>Screenshot tester</div>
<button onclick="report()">Report bug</button>
<div class="example">Lorem ipsum</div>
<div class="bug-container hide">
<div>Select bug region: click once - move mouse - click again</div>
<div class="bug">
<img width="75%" class="screen" >
<div class="region"></div>
</div>
<div>
<textarea class="bug-desc">Describe bug here...</textarea>
</div>
<div class="send">SEND BUG</div>
</div>
</body>
Get screenshot as Canvas or Jpeg Blob / ArrayBuffer using getDisplayMedia API:
FIX 1: Use the getUserMedia with chromeMediaSource only for Electron.js
FIX 2: Throw error instead return null object
FIX 3: Fix demo to prevent the error: getDisplayMedia must be called from a user gesture handler
// docs: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia
// see: https://www.webrtc-experiment.com/Pluginfree-Screen-Sharing/#20893521368186473
// see: https://github.com/muaz-khan/WebRTC-Experiment/blob/master/Pluginfree-Screen-Sharing/conference.js
function getDisplayMedia(options) {
if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
return navigator.mediaDevices.getDisplayMedia(options)
}
if (navigator.getDisplayMedia) {
return navigator.getDisplayMedia(options)
}
if (navigator.webkitGetDisplayMedia) {
return navigator.webkitGetDisplayMedia(options)
}
if (navigator.mozGetDisplayMedia) {
return navigator.mozGetDisplayMedia(options)
}
throw new Error('getDisplayMedia is not defined')
}
function getUserMedia(options) {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
return navigator.mediaDevices.getUserMedia(options)
}
if (navigator.getUserMedia) {
return navigator.getUserMedia(options)
}
if (navigator.webkitGetUserMedia) {
return navigator.webkitGetUserMedia(options)
}
if (navigator.mozGetUserMedia) {
return navigator.mozGetUserMedia(options)
}
throw new Error('getUserMedia is not defined')
}
async function takeScreenshotStream() {
// see: https://developer.mozilla.org/en-US/docs/Web/API/Window/screen
const width = screen.width * (window.devicePixelRatio || 1)
const height = screen.height * (window.devicePixelRatio || 1)
const errors = []
let stream
try {
stream = await getDisplayMedia({
audio: false,
// see: https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints/video
video: {
width,
height,
frameRate: 1,
},
})
} catch (ex) {
errors.push(ex)
}
// for electron js
if (navigator.userAgent.indexOf('Electron') >= 0) {
try {
stream = await getUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
// chromeMediaSourceId: source.id,
minWidth : width,
maxWidth : width,
minHeight : height,
maxHeight : height,
},
},
})
} catch (ex) {
errors.push(ex)
}
}
if (errors.length) {
console.debug(...errors)
if (!stream) {
throw errors[errors.length - 1]
}
}
return stream
}
async function takeScreenshotCanvas() {
const stream = await takeScreenshotStream()
// from: https://stackoverflow.com/a/57665309/5221762
const video = document.createElement('video')
const result = await new Promise((resolve, reject) => {
video.onloadedmetadata = () => {
video.play()
video.pause()
// from: https://github.com/kasprownik/electron-screencapture/blob/master/index.js
const canvas = document.createElement('canvas')
canvas.width = video.videoWidth
canvas.height = video.videoHeight
const context = canvas.getContext('2d')
// see: https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement
context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight)
resolve(canvas)
}
video.srcObject = stream
})
stream.getTracks().forEach(function (track) {
track.stop()
})
if (result == null) {
throw new Error('Cannot take canvas screenshot')
}
return result
}
// from: https://stackoverflow.com/a/46182044/5221762
function getJpegBlob(canvas) {
return new Promise((resolve, reject) => {
// docs: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob
canvas.toBlob(blob => resolve(blob), 'image/jpeg', 0.95)
})
}
async function getJpegBytes(canvas) {
const blob = await getJpegBlob(canvas)
return new Promise((resolve, reject) => {
const fileReader = new FileReader()
fileReader.addEventListener('loadend', function () {
if (this.error) {
reject(this.error)
return
}
resolve(this.result)
})
fileReader.readAsArrayBuffer(blob)
})
}
async function takeScreenshotJpegBlob() {
const canvas = await takeScreenshotCanvas()
return getJpegBlob(canvas)
}
async function takeScreenshotJpegBytes() {
const canvas = await takeScreenshotCanvas()
return getJpegBytes(canvas)
}
function blobToCanvas(blob, maxWidth, maxHeight) {
return new Promise((resolve, reject) => {
const img = new Image()
img.onload = function () {
const canvas = document.createElement('canvas')
const scale = Math.min(
1,
maxWidth ? maxWidth / img.width : 1,
maxHeight ? maxHeight / img.height : 1,
)
canvas.width = img.width * scale
canvas.height = img.height * scale
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height)
resolve(canvas)
}
img.onerror = () => {
reject(new Error('Error load blob to Image'))
}
img.src = URL.createObjectURL(blob)
})
}
DEMO:
document.body.onclick = async () => {
// take the screenshot
var screenshotJpegBlob = await takeScreenshotJpegBlob()
// show preview with max size 300 x 300 px
var previewCanvas = await blobToCanvas(screenshotJpegBlob, 300, 300)
previewCanvas.style.position = 'fixed'
document.body.appendChild(previewCanvas)
// send it to the server
var formdata = new FormData()
formdata.append("screenshot", screenshotJpegBlob)
await fetch('https://your-web-site.com/', {
method: 'POST',
body: formdata,
'Content-Type' : "multipart/form-data",
})
}
// and click on the page
Here is a complete screenshot example that works with chrome in 2021. The end result is a blob ready to be transmitted. Flow is: request media > grab frame > draw to canvas > transfer to blob. If you want to do it more memory efficient explore OffscreenCanvas or possibly ImageBitmapRenderingContext
https://jsfiddle.net/v24hyd3q/1/
// Request media
navigator.mediaDevices.getDisplayMedia().then(stream =>
{
// Grab frame from stream
let track = stream.getVideoTracks()[0];
let capture = new ImageCapture(track);
capture.grabFrame().then(bitmap =>
{
// Stop sharing
track.stop();
// Draw the bitmap to canvas
canvas.width = bitmap.width;
canvas.height = bitmap.height;
canvas.getContext('2d').drawImage(bitmap, 0, 0);
// Grab blob from canvas
canvas.toBlob(blob => {
// Do things with blob here
console.log('output blob:', blob);
});
});
})
.catch(e => console.log(e));
Heres an example using: getDisplayMedia
document.body.innerHTML = '<video style="width: 100%; height: 100%; border: 1px black solid;"/>';
navigator.mediaDevices.getDisplayMedia()
.then( mediaStream => {
const video = document.querySelector('video');
video.srcObject = mediaStream;
video.onloadedmetadata = e => {
video.play();
video.pause();
};
})
.catch( err => console.log(`${err.name}: ${err.message}`));
Also worth checking out is the Screen Capture API docs.
You can try my new JS library: screenshot.js.
It's enable to take real screenshot.
You load the script:
<script src="https://raw.githubusercontent.com/amiad/screenshot.js/master/screenshot.js"></script>
and take screenshot:
new Screenshot({success: img => {
// callback function
myimage = img;
}});
You can read more options in project page.
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');