I am playing with canvases imageData and I am having some issues with speed. What I am doing is
$(document).ready(function(){
loadCanvas();
myImageData();
});
function loadCanvas(){
canvas = document.getElementById('myCanvas');
context = canvas.getContext('2d')
image = new Image();
image.src = '/static/images/teeth1.jpg';
image.onload = function (){
imageWidth = image.width;
imageHeight = image.height;
context.drawImage(image, 0, 0, image.width, image.height);
imageData = myImageData(context, image);
pixels = imageData.data;
console.log(pixels);
console.log(pixels.length);
console.log(imageData);
//Changing the value of each pixel
for (var y=0; y<=imageHeight; ++y){
for (var x=0; x<=imageWidth; ++x){
index = (y*imageWidth + x) * 4;
pixels[index] += 30;
pixels[++index] += 30;
pixels[++index] += 30;
}
}
}
}
function myImageData(context){
console.log("width: "+image.width+", height:"+image.height)
return context.getImageData(0, 0, image.width, image.height);
}
When I execute the above code outside the onload function from chrome's console it works very fast. But when executing the double for's inside the onload function (as it is right now) it hangs. Why is that? Is it because it is inside the onload? How can I make sure the image was fully loaded before executing the double fors(so to put them seperatelly outside the onload function)?
// Define functions before using them (Good to)
function loadCanvas(){
var canvas = document.getElementById('myCanvas'); // Define variables!
var context = canvas.getContext('2d');
var image = new Image();
image.onload = function (){
var imageWidth = image.width; // Variables again!
var imageHeight = image.height;
context.drawImage(image, 0, 0, imageWidth, imageHeight );
var imageData = myImageData(context, image); // Variables!
var pixels = imageData.data;
console.log(pixels);
console.log(pixels.length);
console.log(imageData);
//Changing the value of each pixel
for (var y=0; y<=imageHeight; ++y){
for (var x=0; x<=imageWidth; ++x){
var index = (y*imageWidth + x) * 4;
pixels[index] += 30;
pixels[++index] += 30;
pixels[++index] += 30;
}
}
};
image.src = '/static/images/teeth1.jpg'; // Set src here
}
function myImageData(ctx, img){ // Pass the actual image as argument
console.log("width: "+img.width+", height:"+img.height);
return ctx.getImageData(0, 0, img.width, img.height);
}
$(document).ready(function(){
loadCanvas(); // has myImageData() so...
//myImageData(); // why??
});
Related
I have a function that loops through an image's pixels to determine the average "brightness" of the image as a number between 0 (black) - 255 (white).
The problem is during testing I found that while an image that's entirely black (#000000) will correctly return 0, an image that's entirely white (#FFFFFF) returns a number around 200 instead of 255. Am I missing something?
Here's my function, any help would be appreciated!
function getBrightness(imageSrc, callback) {
const img = document.createElement("img");
img.src = imageSrc;
img.style.display = "none";
document.body.appendChild(img);
let colorSum = 0;
img.onload = function() {
const canvas = document.createElement("canvas");
canvas.width = this.width;
canvas.height = this.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
let r, g, b, avg;
for(let x=0, len=data.length; x<len; x+=4) {
r = data[x];
g = data[x+1];
b = data[x+2];
avg = Math.floor((r+g+b) / 3);
colorSum += avg;
}
const brightness = Math.floor(colorSum / (this.width * this.height));
callback(brightness);
}
}
Sorry, but as the snippet below proves, the result for the image you mentioned is 255.
Maybe you want to check out what file you were using in the tests ? you're probably using a wrong file...
function getBrightness(imageSrc, callback) {
const img = document.createElement("img");
img.src = imageSrc;
img.crossOrigin = 'anonymous';
img.style.display = "none";
document.body.appendChild(img);
let colorSum = 0;
img.onload = function() {
const canvas = document.createElement("canvas");
canvas.width = this.width;
canvas.height = this.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
let r, g, b, avg;
for(let x=0, len=data.length; x<len; x+=4) {
r = data[x];
g = data[x+1];
b = data[x+2];
avg = Math.floor((r+g+b) / 3);
colorSum += avg;
}
const brightness = Math.floor(colorSum / (this.width * this.height));
callback(brightness);
}
};
getBrightness(
'https://upload.wikimedia.org/wikipedia/commons/thumb/7/70/Solid_white.svg/768px-Solid_white.svg.png',
(b)=>console.log(b));
I just included the line img.crossOrigin = 'anonymous'; in order to avoid the cors security message.
I would like to use threshold filter on base64 string (data:image/png;base64,iVBOR...) using javaScript like this:
function threshold(base64) {
some action whith base64 string...
return base64; //base64 is updated by threshold filter
}
Is it possible and if it is, how can I do this?
var base64string = "data:image/png;base64,iVBOR..........",
threshold = 180, // 0..255
ctx = document.createElement("canvas").getContext("2d"),
image = new Image();
image.onload = function() {
var w = ctx.canvas.width = image.width,
h = ctx.canvas.height = image.height;
ctx.drawImage(image, 0, 0, w, h); // Set image to Canvas context
var d = ctx.getImageData(0, 0, w, h); // Get image Data from Canvas context
for (var i=0; i<d.data.length; i+=4) { // 4 is for RGBA channels
// R=G=B=R>T?255:0
d.data[i] = d.data[i+1] = d.data[i+2] = d.data[i+1] > threshold ? 255 : 0;
}
ctx.putImageData(d, 0, 0); // Apply threshold conversion
document.body.appendChild(ctx.canvas); // Show result
};
image.src = base64string;
MDN - putImageData
MDN - getImageData
I'm loading a PNG and try to check, whether it is fully opaque, meaning alpha channel is near 100% over the whole image. Therefore, I use the canvas and 2D-Context to get the pixel data and loop through it checking the alpha value.
To my surprise i get whole areas of zeros (RGBA = [0000]) where it obviously shouldn't.
Browser in focus: chrome 50.0.2661.87
Here is my code, it is embedded in a ThreeJS environment:
var imageData = zipHandler.zip.file(src); // binary image data
var texture = new THREE.Texture();
var img = new Image();
img.addEventListener( 'load', function ( event ) {
texture.imageHasTransparency = false; // extend THREEJS Texture by a flag
if (img.src.substring(imgSrc.length-4).toLowerCase().indexOf("png") > -1
|| img.src.substring(0, 15).toLowerCase().indexOf("image/png") > -1) {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0, img.width, img.height );
var pixDataContainer = context.getImageData(0, 0, img.width, img.height);
var pixData = pixDataContainer.data;
// search for pixel.alpha < 250
for (var pix = 0, pixDataLen = pixData.length; pix < pixDataLen; pix += 4) {
if (pixData[pix+3] < 250) {
texture.imageHasTransparency = true;
break;
}
}
}
texture.image = img;
texture.needsUpdate = true;
this.removeEventListener('load', arguments.callee, false);
}, false );
var fileExtension = src.substr(src.lastIndexOf(".") + 1);
img.src = "data:image/"+fileExtension+";base64,"+ btoa(imageData.asBinary());
The order is correct: first define new Image(), then the onload function and then the source.
Solved. The image was larger than the canvas, so when context.drawImage() was started, only parts of the area was filled. I've set the canvas dimensions according to the image size, so now it works and we have
// ...
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
canvas.width = img.width; // !!!!!
canvas.height = img.height; // !!!!
context.drawImage(img, 0, 0, img.width, img.height );
var pixDataContainer = context.getImageData(0, 0, img.width, img.height);
var pixData = pixDataContainer.data;
// ...
I have researched issues with the base64 conversion and jspdf function quite a bit. ( PS this is my first question on stackoverflow, please bare with any rookie mistakes ).
All seems to work fine with the below code except that the pdf is generated and saved before the loop where the images are converted to base64 and placed to the document is finished. I added a couple alerts to check timing. Would the solution be to check when the loop is finished, the images placed before continuing with the pdf function? if so, how? please help.
$(document).ready(function(){
$("a#getdoc").click(function(){
var doc = new jsPDF('landscape, in, legal');
var myimages = 'img1.jpg|img2.jpg|img3.png';
var myimgarray = myimages.split('|');
function convertImgToBase64(url, callback, outputFormat){
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
return canvas.toDataURL("image/jpeg");
var dataURL = canvas.toDataURL("image/jpeg");
callback(dataURL);
canvas = null;
}
for(var i = 0; i < myimgarray.length; i++)
{
icount.count = i;
var img = new Image();
alert(checkoutimgarray);
img.src = '/Portals/0/repair-images/' + myimgarray[i];
img.onload = function(){
newData = convertImgToBase64(img);
alert(newData);
doc.addImage(newData, 'JPEG', (icount * 100), 10, 70, 15); // would doc be undefined here? out of scope?
};
}
doc.setFontSize(20);
doc.text(100, 20, "This is a test to see if images will show");
doc.save('My_file.pdf');
});
});
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
function convertImgToBase64(img, outputFormat){
// clear canvas
canvas.width = img.width;
canvas.height = img.height;
// draw image
ctx.drawImage(img, 0, 0);
// get data url of output format or defaults to jpeg if not set
return canvas.toDataURL("image/" + (outputFormat || "jpeg"));
}
var images = [];
for(var i = 0; i < myimgarray.length; i++) {
var img = new Image();
img.onload = function() {
images.push({
base64: convertImgToBase64(this),
width: this.width,
height: this.height
});
// all images loaded
if(images.length === myimgarray.length) {
for(var j = 0; j < images.length; j++) {
doc.addImage(images[j].base64, 'JPEG', (j * 100), 10, 70, 15);
}
doc.setFontSize(20);
doc.text(100, 20, "This is a test to see if images will show");
doc.save('My_file.pdf');
}
};
img.src = '/Portals/0/repair-images/' + myimgarray[i];
}
I got this script:
function capture(video, scaleFactor) {
if(scaleFactor == null){
scaleFactor = 1;
}
var w = video.videoWidth * scaleFactor;
var h = video.videoHeight * scaleFactor;
var canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, w, h);
return canvas;
}
function shoot(){
var video = document.getElementById(videoId);
var output = document.getElementById('output');
var canvas = capture(video, scaleFactor);
canvas.onclick = function(){
window.open(this.toDataURL());
};
snapshots.unshift(canvas);
output.innerHTML = '';
for(var i=0; i<1; i++){
output.appendChild(snapshots[i]);
}
}
What I want to do is export the snapshot to a bitmap image. I read that I could use this line:
canvas.toDataURL('image/jpeg');
But I don't know where to add it.
Any ideas?
ctx.drawImage(video, 0, 0, w, h);
canvas.toDataURL(...)
toDataURL will return you a string which is usually base64 encoded image (file) content. You can display it in image tag by < img src="the string"/>. Or you can use javascript to do whatever you want...
Pass it to window.open
canvas.onclick = function () {
window.open(canvas.toDataURL('image/png'));
};
Full Example : http://www.nihilogic.dk/labs/canvas2image/