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();
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'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 ?
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 have an array list of image urls. I want to get an image width and height, to do so, I use:
const url = 'https://via.placeholder.com/350x150'
const img = new Image()
img.addEventListener('load', function () {
const isWide = img.width > img.height
const isTall = img.height > img.width
console.log(url, isWide, isTall, img.width, img.height) // <--- WORKS FINE
img.src = url
})
I have a list of images where I want to extract them all in some one Promise.all(arrayOfImageUrls).
How would I make it Promise so it awaits for all of the urls in the image array to complete ?
Here is my code which does not work, it just ignores the "await" and trigger the function before it was even finished:
const array = [
"https://via.placeholder.com/350x150",
"https://via.placeholder.com/650x250",
"https://via.placeholder.com/350x150",
"https://via.placeholder.com/650x250",
"https://via.placeholder.com/150x550",
"https://via.placeholder.com/510x450",
"https://via.placeholder.com/800x800"
]
function doSomethingAsync(url) {
return new Promise((resolve) => {
const img = new Image()
img.addEventListener('load', function() {
const isWide = img.width > img.height
const isTall = img.height > img.width
img.src = url
resolve({ url, isWide, isTall })
})
})
}
async function doAsync() {
const promises = []
array.forEach(url => {
promises.push(doSomethingAsync(url))
})
console.log('before promise all')
const results = await Promise.all(promises)
console.log('after promise all', results)
}
doAsync()
You have to put img.src = url before waiting for the images to load, not inside the onload handler. You are currently waiting for images with no src to load.
const array = [
"https://via.placeholder.com/350x150",
"https://via.placeholder.com/650x250",
"https://via.placeholder.com/150x550",
"https://via.placeholder.com/510x450",
"https://via.placeholder.com/800x800"
]
function doSomethingAsync(url) {
return new Promise((resolve) => {
const img = new Image();
img.src = url; // <--- Move this line here
img.addEventListener('load', function() {
const isWide = img.width > img.height
const isTall = img.height > img.width
resolve({ url, isWide, isTall })
})
})
}
async function doAsync() {
const promises = []
array.forEach(url => {
promises.push(doSomethingAsync(url))
})
console.log('before promise all')
const results = await Promise.all(promises)
console.log('after promise all', results)
}
doAsync()