How do I load pixels from uploaded image file in p5js? - javascript

The image is created from a file input. With this input it is then loaded to make sure the image exists before loading pixels. Either way, both using the successful callback in loadimage and without a callback, the error says TypeError: img.loadPixels is not a function. I assumed this meant the image is not loaded in time for the pixels to be loaded.
let imgInput, img;
let reds = [], greens = [], blues = [], alphas = [], newColor;
let tileSize = 20;
function setup() {
createCanvas(400, 400);
//creates input box for image
imgInput = createFileInput(handleFile);
imgInput.position(20, 20);
imgInput.hide();
//creates the button to start the game
startBtn = createButton('Start Game');
startBtn.position(width/2-startBtn.width/2, height/2 - startBtn.height/2);
startBtn.mousePressed(start);
}
function draw() {
//if image exists, change canvas to fit and create image
if (img) {
resizeCanvas(img.width, img.height);
image(img, 0, 0, width, height);
} else {
background(255);
}
}
//determines what to do with file
function handleFile(file) {
print(file);
if (file.type === 'image') {
img = createImg(file.data, '');
img.hide();
let imgSrc = img.attribute("src");
loadImage(imgSrc, storeImgValues);
} else {
img = null;
}
imgInput.hide();
}
//all pixels are stored in lists
function storeImgValues() {
img.loadPixels();
console.log('hi')
let pixel = 0;
for (let l = 0; l < img.pixels.length; l += 4) {
reds[pixel] = img.pixels[l];
greens[pixel] = img.pixels[l+1];
blues[pixel] = img.pixels[l+2];
alphas[pixel] = img.pixels[l+3];
}
}
//when button clicked, hide it and show input for image
function start() {
startBtn.hide();
imgInput.show();
}
I tried adding in the callback to allow the image to load first from loadimage, this ended in the same error and this callback is included in the code.

I needed to set img = loadImage not just loadimage.

Related

Can't figure out how to fix this: "Uncaught TypeError (webcam.snap is not a function)" <-- javascript

I have a JavaScript script that I am getting an error for that I can't figure out. I am trying to take a picture of the webcam feed using JavaScript
The error is:
Uncaught TypeError: webcam.snap is not a function
I am using webcam.js to take the snapshot.
Here is my JavaScript code:
<script>
var video = document.createElement("video");
var canvasElement = document.getElementById("canvas");
var canvas = canvasElement.getContext("2d");
var loadingMessage = document.getElementById("loadingMessage");
var outputContainer = document.getElementById("output");
var outputMessage = document.getElementById("outputMessage");
var outputData = document.getElementById("outputData");
const jsQR = require("jsqr");
function drawLine(begin, end, color) {
canvas.beginPath();
canvas.moveTo(begin.x, begin.y);
canvas.lineTo(end.x, end.y);
canvas.lineWidth = 4;
canvas.strokeStyle = color;
canvas.stroke();
}
// Use facingMode: environment to attemt to get the front camera on phones
navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).then(function(stream) {
video.srcObject = stream;
video.setAttribute("playsinline", true); // required to tell iOS safari we don't want fullscreen
video.play();
requestAnimationFrame(tick);
});
function tick() {
loadingMessage.innerText = "⌛ Loading video..."
if (video.readyState === video.HAVE_ENOUGH_DATA) {
loadingMessage.hidden = true;
canvasElement.hidden = false;
outputContainer.hidden = false;
canvasElement.height = video.videoHeight;
canvasElement.width = video.videoWidth;
canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);
var imageData = canvas.getImageData(0, 0, canvasElement.width, canvasElement.height);
var code = jsQR(imageData.data, imageData.width, imageData.height, {
inversionAttempts: "invertFirst",
});
if (code) {
drawLine(code.location.topLeftCorner, code.location.topRightCorner, "#FF3B58");
drawLine(code.location.topRightCorner, code.location.bottomRightCorner, "#FF3B58");
drawLine(code.location.bottomRightCorner, code.location.bottomLeftCorner, "#FF3B58");
drawLine(code.location.bottomLeftCorner, code.location.topLeftCorner, "#FF3B58");
outputMessage.hidden = true;
outputData.parentElement.hidden = false;
outputData.innerText = code.data;
takeSnapShot();
}
else {
outputMessage.hidden = false;
outputData.parentElement.hidden = true;
}
}
requestAnimationFrame(tick);
}
// TAKE A SNAPSHOT.
takeSnapShot = function () {
webcam.snap(function (data_uri) {
downloadImage('video', data_uri);
});
}
// DOWNLOAD THE IMAGE.
downloadImage = function (name, datauri) {
var a = document.createElement('a');
a.setAttribute('download', name + '.png');
a.setAttribute('href', datauri);
a.click();
}
</script>
This is the first line that causes a problem:
webcam.snap(function (data_uri) {
downloadImage('video', data_uri);
});
This is the second line that causes a problem:
takeSnapShot();
how do I correct this properly?
****** UPDATE ******
The version of webcam.js I am using is WebcamJS v1.0.26. My application is a Django application that launches the HTML file as defined in main.js.
snap: function(user_callback, user_canvas) {
// use global callback and canvas if not defined as parameter
if (!user_callback) user_callback = this.params.user_callback;
if (!user_canvas) user_canvas = this.params.user_canvas;
// take snapshot and return image data uri
var self = this;
var params = this.params;
if (!this.loaded) return this.dispatch('error', new WebcamError("Webcam is not loaded yet"));
// if (!this.live) return this.dispatch('error', new WebcamError("Webcam is not live yet"));
if (!user_callback) return this.dispatch('error', new WebcamError("Please provide a callback function or canvas to snap()"));
// if we have an active preview freeze, use that
if (this.preview_active) {
this.savePreview( user_callback, user_canvas );
return null;
}
// create offscreen canvas element to hold pixels
var canvas = document.createElement('canvas');
canvas.width = this.params.dest_width;
canvas.height = this.params.dest_height;
var context = canvas.getContext('2d');
// flip canvas horizontally if desired
if (this.params.flip_horiz) {
context.translate( params.dest_width, 0 );
context.scale( -1, 1 );
}
// create inline function, called after image load (flash) or immediately (native)
var func = function() {
// render image if needed (flash)
if (this.src && this.width && this.height) {
context.drawImage(this, 0, 0, params.dest_width, params.dest_height);
}
// crop if desired
if (params.crop_width && params.crop_height) {
var crop_canvas = document.createElement('canvas');
crop_canvas.width = params.crop_width;
crop_canvas.height = params.crop_height;
var crop_context = crop_canvas.getContext('2d');
crop_context.drawImage( canvas,
Math.floor( (params.dest_width / 2) - (params.crop_width / 2) ),
Math.floor( (params.dest_height / 2) - (params.crop_height / 2) ),
params.crop_width,
params.crop_height,
0,
0,
params.crop_width,
params.crop_height
);
// swap canvases
context = crop_context;
canvas = crop_canvas;
}
// render to user canvas if desired
if (user_canvas) {
var user_context = user_canvas.getContext('2d');
user_context.drawImage( canvas, 0, 0 );
}
// fire user callback if desired
user_callback(
user_canvas ? null : canvas.toDataURL('image/' + params.image_format, params.jpeg_quality / 100 ),
canvas,
context
);
};
// grab image frame from userMedia or flash movie
if (this.userMedia) {
// native implementation
context.drawImage(this.video, 0, 0, this.params.dest_width, this.params.dest_height);
// fire callback right away
func();
}
else if (this.iOS) {
var div = document.getElementById(this.container.id+'-ios_div');
var img = document.getElementById(this.container.id+'-ios_img');
var input = document.getElementById(this.container.id+'-ios_input');
// function for handle snapshot event (call user_callback and reset the interface)
iFunc = function(event) {
func.call(img);
img.removeEventListener('load', iFunc);
div.style.backgroundImage = 'none';
img.removeAttribute('src');
input.value = null;
};
if (!input.value) {
// No image selected yet, activate input field
img.addEventListener('load', iFunc);
input.style.display = 'block';
input.focus();
input.click();
input.style.display = 'none';
} else {
// Image already selected
iFunc(null);
}
}
else {
// flash fallback
var raw_data = this.getMovie()._snap();
// render to image, fire callback when complete
var img = new Image();
img.onload = func;
img.src = 'data:image/'+this.params.image_format+';base64,' + raw_data;
}
return null;
},
Your implementation doesn't need Webcamjs, because you're using navigator media devices.
You can either use WebcamJS by initializing it at first and attaching it to some canvas, like in the following code
Webcam.set({
width: 320,
height: 240,
image_format: 'jpeg',
jpeg_quality: 90
});
Webcam.attach( '#my_camera' );
Or you can update your takeSnapShot function to the following :
takeSnapShot = function () {
downloadImage('video',canvasElement.toDataURL())
// Webcam.snap(function (data_uri) {
// downloadImage('video', data_uri);
// });
}
Here's a working example based on your code https://codepen.io/majdsalloum/pen/RwVKBbK
It seems like either:
the webcam's code are missing (not imported)
in this case you need to first call the script from the URL and add it with script tag
<script src="WEBCAM_JS_SOURCE">
or
they are imported, but used with typo. From the webcam source code it is defined as:
var Webcam = {
version: '1.0.26',
// globals
...
};
so you should use with a capital one.

correct way to use loadImage in p5.js?

I'm loading image URLs from a json file. I have everything working fine, except displaying the actual images.
It's a simple clik carousel. Hit the click and it moves index onto the next one. I want to make sure the images display at the same time, obviously but it's not working (images are referenced, but don't display).
Anyone know what I am doing wrong?
var w = window.innerWidth;
var h = window.innerHeight;
var xPos = w/2;
var yPos = h/2;
var index = 0;
var imageData;
var imgList = [];
var indexMax;
function preload() {
loadJSON("image_search_result.json", resultLoaded);
}
function resultLoaded(data) {
imageData = data;
indexMax = imageData.items.length;
for (i = 0; i < indexMax; i++) {
imgList.push(imageData.items[i]['link']);
}
}
function mouseReleased() {
index = index + 1;
if (index == indexMax){
index = index - indexMax;
}
}
function setup() {
createCanvas(w,h);
}
function draw() {
background(0);
image(loadImage(imgList[index]),xPos,yPos,w,h);
text(index,20,60); // index counter
text(imgList[index],80,60); // image list number
textSize(20);
fill(255);
}
I think the problem is that you're trying to upload images every call to the drawing function. As you wrote the code, even if the images to be uploaded will always be the same, p5.js will reload them from scratch. You should load them before starting the program. As I did below:
var w = window.innerWidth;
var h = window.innerHeight;
var xPos = w / 2;
var yPos = h / 2;
var index = 0;
var imageData;
var imgList = [];
var indexMax;
function preload() {
loadJSON("img.json", resultLoaded);
}
function resultLoaded(data) {
imageData = data;
indexMax = imageData.items.length;
for (i = 0; i < indexMax; i++) {
url = imageData.items[i]["link"];
imgList.push(loadImage(url));
}
}
function mouseReleased() {
index = index + 1;
if (index == indexMax) {
index = index - indexMax;
}
}
function setup() {
createCanvas(w, h);
}
function draw() {
background(0);
image(imgList[index], xPos, yPos, w, h);
text(index, 20, 60);
textSize(20);
fill(255);
}
P.S. #George Profenza gave the same answer while I was writing the code. Sorry
Finally I got this to work by adding a callback in loadImage. I was unaware you could do this (hadn't seen it referenced in any documentation), but it works very efficiently.
function draw() {
nextImg = loadImage(imgList[index], imageLoaded);
text(index,20,60); // index counter
text(imgList[index],80,60); // image list number
textSize(20);
fill(255);
}
function imageLoaded() {
background(0);
image(nextImg,xPos,yPos,w,h);
imageMode(CENTER);
}

Canvas HTML loading time

I try to make a game with HTML Canvas but I've a problem.
When I call my 'init' function with 'window.onload', my program doesn't load any images. The canvas has been created, but images aren't there, and when I submit my function in chrome console, images appears... So I want to load my images and execute the function when it's done.
Thanks for help !
var requestAnimId;
// Initialisation
function init() {
var tilemapCtx = new Canvas('tilemap', 800, 600, 1);
var tilemap = new Tilemap("sand", tilemapCtx);
requestAnimId = window.requestAnimationFrame(update);
}
// Boucle de rafraîchissement
function update() {
requestAnimId = window.requestAnimationFrame(update);
}
window.onload = init;
Here is Tilemap :
var Tilemap = function(map, canvas) {
for (var y = 0; y < 10; y++) {
for (var x = 0; x < 10; x++) {
// Création de la tile
this.image = new Image();
// Définition de l'image
switch(tilemaps[map][y][x]) {
case " ":
this.image.src = "assets/sand.png";
break;
case "#":
this.image.src = "assets/wall.png";
break;
}
// Affichage de l'image
//canvas.drawImage(this.image, tileX, tileY, tilesetWidth, tilesetHeight, x, y, 32, 32);
canvas.drawImage(this.image, 32, 32);
}
}
}
and Canvas :
var Canvas = function(name, width, height, zIndex) {
this.canvas = window.document.createElement("canvas");
this.canvas.id = name;
this.canvas.style.position = "absolute";
this.canvas.width = width;
this.canvas.height = height;
this.canvas.style.zIndex = zIndex;
document.body.appendChild(this.canvas);
return this.canvas.getContext('2d');
}
Notice that for your tile you only use two images. You use a double loop that will load 100 images.
One problem is you are creating 100 images when in the end you only need two.
Now the reason why your images don't get drawn is because they haven't been loaded. You need to give time for your images to load before you try drawing them.
You need to create an array list of images and make sure they are loaded before you call your init()
This will ensure all your images have been loaded before init() is called.
var len = pics.length;
var loadCounter = 0;
for(var i = 0; i < len; i++) {
$(document.createElement(img)).attr('src', pics[i]).load(function() {
alert(pics[i] + 'is loaded');
loadCounter++;
if(loadCounter === len) {
alert("all are loaded");
}
});
}

Canvas multiply hover effect

I´m trying to make a hover multiply effect over an image. I´m following this tutorial:
Demo:
http://albertogasparin.it/demo/multiply-filter/
http://albertogasparin.it/articles/2011/05/html5-multiply-filter-canvas/
The problem I have is that I don´t know how to pass to function so it works just on hover over the "id="multiply_hover", because right now the multiply filter is shown after page load.
This is my markup:
<div class="item">
<img id="multiply_hover" src="img/coleccionesII_1.jpg" alt="coleccionesII_1" width="195" height="343">
<div class="item_info">
<div class="item_text">
SEDA BLOUSE BOLSILLOS
CREP PANTALÓN
Zapato Rojo
</div>
</div>
</div>
With this script:
var multiplyFilter = (function() {
//** private vars **//
var multiplyColor,
imageBottom, imageId,
canvas;
//** private functions **//
function createCanvas() {
canvas = document.createElement('canvas');
canvas.width = imageBottom.width;
canvas.height = imageBottom.height;
imageBottom.parentNode.insertBefore(canvas, imageBottom);
// no canvas support?
if (!canvas.getContext) { return; }
draw();
}
function draw() {
var context, imgData, pix,
w = imageBottom.width,
h = imageBottom.height;
// get 2d context
context = canvas.getContext('2d');
// draw the image on the canvas
context.drawImage(imageBottom, 0, 0);
// Get the CanvasPixelArray from the given coordinates and dimensions.
imgData = context.getImageData(0, 0, w, h);
pix = imgData.data;
// Loop over each pixel and change the color.
for (var i = 0, n = pix.length; i < n; i += 4) {
pix[i ] = multiplyPixels(multiplyColor[0], pix[i ]); // red
pix[i+1] = multiplyPixels(multiplyColor[1], pix[i+1]); // green
pix[i+2] = multiplyPixels(multiplyColor[2], pix[i+2]); // blue
// pix[i+3] is alpha channel (ignored)
// another check to see if image is still empty
if(i < 5 && !pix[i] && !pix[i+1] && !pix[i+2] && !pix[i+3]) {
canvas.parentNode.removeChild(canvas);
setTimeout(function() { multiplyFilter.init(imageId, multiplyColor); }, 500);
return false;
}
}
// Draw the result on the canvas
context.putImageData(imgData, 0, 0);
}
//** helper function **//
function multiplyPixels(topValue, bottomValue) {
// the multiply formula
return topValue * bottomValue / 255;
}
//** public functions **//
return {
init : function(imgId, color) {
imageId = imgId;
imageBottom = document.getElementById(imageId);
multiplyColor = color;
// lauch the draw function as soon as the image is loaded
if(imageBottom && imageBottom.clientWidth > 50) { // image loaded
createCanvas();
} else { // not yet ready
setTimeout(function() { multiplyFilter.init(imageId, color); }, 1000);
}
}
}
})();
multiplyFilter.init('multiply_hover', [0, 0, 210]);
I tried using something like this, which on hover works but not well at all, cause it creates on every hover a new canvas element:
// Hover effect
$(".item").bind('mouseenter', function() {
$(this).children(".item_info").fadeIn();
multiplyFilter.init('multiply_hover', [0, 0, 210]);
});
$(".item").bind('mouseleave', function() {
$(this).children(".item_info").fadeOut();
});
Any ideas on how to properly pass the function on hover ?
Try this code:
<!DOCTYPE html>
<html>
<body>
<div class="item">
<img id="multiply_hover" src="img/coleccionesII_1.jpg" alt="coleccionesII_1">
<div class="item_info">
<div class="item_text">
SEDA BLOUSE BOLSILLOS
CREP PANTALÓN
Zapato Rojo
</div>
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
var multiplyFilter = (function() {
//** private vars **//
var multiplyColor,
imageBottom, imageId,
canvas;
//** private functions **//
function createCanvas() {
canvas = document.createElement('canvas');
canvas.width = imageBottom.width;
canvas.height = imageBottom.height;
imageBottom.parentNode.insertBefore(canvas, imageBottom);
// no canvas support?
if (!canvas.getContext) { return; }
draw();
}
function draw() {
var context, imgData, pix,
w = imageBottom.width,
h = imageBottom.height;
// get 2d context
context = canvas.getContext('2d');
// draw the image on the canvas
context.drawImage(imageBottom, 0, 0);
// Get the CanvasPixelArray from the given coordinates and dimensions.
imgData = context.getImageData(0, 0, w, h);
pix = imgData.data;
// Loop over each pixel and change the color.
for (var i = 0, n = pix.length; i < n; i += 4) {
pix[i ] = multiplyPixels(multiplyColor[0], pix[i ]); // red
pix[i+1] = multiplyPixels(multiplyColor[1], pix[i+1]); // green
pix[i+2] = multiplyPixels(multiplyColor[2], pix[i+2]); // blue
// pix[i+3] is alpha channel (ignored)
// another check to see if image is still empty
if(i < 5 && !pix[i] && !pix[i+1] && !pix[i+2] && !pix[i+3]) {
canvas.parentNode.removeChild(canvas);
setTimeout(function() { multiplyFilter.init(imageId, multiplyColor); }, 500);
return false;
}
}
// Draw the result on the canvas
context.putImageData(imgData, 0, 0);
}
//** helper function **//
function multiplyPixels(topValue, bottomValue) {
// the multiply formula
return topValue * bottomValue / 255;
}
//** public functions **//
return {
init : function(imgId, color) {
imageId = imgId;
imageBottom = document.getElementById(imageId);
multiplyColor = color;
// lauch the draw function as soon as the image is loaded
if(imageBottom && imageBottom.clientWidth > 50) { // image loaded
createCanvas();
} else { // not yet ready
setTimeout(function() { multiplyFilter.init(imageId, color); }, 1000);
}
//Hide the original image
$('#'+imgId).hide();
}
}
})();
// Hover effect
$(".item").bind('mouseenter', function() {
$(this).children(".item_info").fadeIn();
multiplyFilter.init('multiply_hover', [0, 0, 210]);
});
$(".item").bind('mouseleave', function() {
$(this).children(".item_info").fadeOut();
$('canvas').hide(); //hide the canvas
$('#multiply_hover').show(); //show the original image
});
</script>
</body>
</html>
I modified the multiply script to make it hide the original image, and the hover effect to make it hide the canvas and show the original image
Do you try this http://api.jquery.com/hover/ ?
You can use this to add a function to $("#multiply_hover"). Hover Jquery has handler IN and OUT

Delay function until an asset has loaded?

I am playing with the canvas, and it seems to be working great in FF6, but in Chrome 13, the sprite that I am drawing does not appear reliably. I have done some research and found that the problem stems from the function firing before the asset has loaded completely.
Fiddle here:
http://jsfiddle.net/LqHY9/
Relevant Javascript:
function sprite(ipath, sh, sw, ih, iw){
/* BASIC INFO FOR SPRITE */
this.frameWidth = sw;
this.frameHeight= sh;
frame_rows = ih/sh;
frame_columns = iw/sw;
num_frames = frame_columns*frame_rows ;
this.frame = new Array();
frameNumber = 0;
for(row = 0; row<frame_rows; row++){
for(i=0;i<frame_columns;i++){
this.frame[frameNumber] = {};
this.frame[frameNumber].offsetX = this.frameWidth*i;
this.frame[frameNumber].offsetY = this.frameHeight*row;
frameNumber++
}
}
this.sheight = sh;
this.swidth = sw;
this.raw = new Image();
this.raw.src = ipath;
}
animation=new sprite("http://www.melonjs.org/tutorial/tutorial_final/data/sprite/gripe_run_right.png",64,64,64,512);
context.drawImage(animation.raw, animation.frame[0].offsetX, animation.frame[0].offsetY, animation.frameWidth, animation.frameHeight, 0, 0, animation.frameWidth,animation.frameHeight)
(Don't worry, my context variable is defined, I just cut that bit out, you can see the whole thing in the JSFiddle.)
The Image object has an onload event which you should hook into.
Assuming you have more than one image, you could implement a sort of a "loader". This would basically just take an array of image URLs, load each of them, and listen to their onload events. Once each image has loaded, it would in turn call some other function, which would signal that every resource has finished loading.
the Image() object has an onload(and onerror) event. If you need to execute something after it loads you can attach a function.
e.g.
var img = new Image();
img.onload = function() {
//do something
};
Just make sure you attach the onload before setting the src.
You need to use the onload handler for the image. You must set the handler before you set the .src for the object because in some browsers, the load event may fire immediately upon setting .src if the image is in the browser cache. Here's a piece of pseudo code:
var img = new Image();
img.onload = function () {
// image is now loaded and ready for handling
// you can safely start your sprite animation
}
img.src = "xxx";
You can see related sample code from another answer I wrote here: jQuery: How to check when all images in an array are loaded?.
function Sprite(urls, speed, box)
{
var that = this, running = false, interval = 0, loaded = false;
that.urls = urls;
that.speed = speed;
that.images = new Array();
that.box = box || { x: 0.0, y: 0.0, w: 64, h: 64 };
for(var i = 0; i < that.urls.length; ++i)
{
that.images[i] = new Image();
that.images[i].src = that.urls[i];
that.images[i].id = i;
var len = that.urls.length;
that.images[i].onload = function(){ if(parseInt(this.id) === len) { loaded = true; } };
}
that.current = 0;
var Draw = function(ctx)
{
if(loaded)
{
var curr = that.images[that.current];
ctx.drawImage(curr, 0.0, 0.0, curr.width, curr.height, that.box.x, that.box.y, that.box.w, that.box.h);
}
};
that.Run = function(ctx)
{
if(!running)
{
running = true;
interval = setInterval(function(){
Draw(ctx);
if(that.current < that.urls.length)
{
that.current++;
}
else
{
that.current = 0;
}
}, that.speed);
}
};
that.Clear = function()
{
if(running)
{
running = false;
clearInterval(interval);
}
};
}
// Exemple
var test = new Sprite(["image1.png", "image2.png", "image3.png"], 250);
test.Run(myContext);

Categories