I want to get the percentage of a color in a HTML5 Canvas.
I have a Canvas, where you can draw lines in three colors with the mouse or clean it with a white line.
Now I want to get the percentage of a color in the whole canvas.
I try that:
var c = document.getElementById("can");
var ctx = c.getContext("2d");
var imgData = ctx.getImageData(0, 0, c.width, c.height);
var allPixel=0;
for (var i = 0; i < imgData.data.length; i += 4) {
allPixel+=1;
if(imgData.data[i+2]==255)
blue+=1;
else if (imgData.data[i]==255)
red+=1;
else if (imgData.data[i+1]==green)
green +=1;
}
But this seems to be wrong. The values are strange and always go very high. How can I get the right values of the whole canvas and a color to calculate the percentage.
Can you help me?
And how is it possible with another color instead of the full RGB colors , for example Yellow or white?
Thank you!
White is equal to rgb(255,255,255) so you might be counting that as blue. Also it might be easier to loop through each x and y and get the data, where this code gets the data at exactly one pixel.
var col = ctx.getImageData(x,y,1,1).data
var red = col[0];
var green = col[1];
var blue = col[2];
// increment values
Related
I'm trying to make a text effect similar to the effect found at the bottom of this article
My proposed approach is:
Make two canvasses, one is visible, the other is invisible I use this as a buffer.
Draw some text on the buffer canvas
Loop over getImageData pixels
if pixel alpha is not equal to zero (when there is a pixel drawn on the canvas buffer) with a small chance, ie 2%, draw a randomly generated circle with cool effecs at that pixel on the visible canvas.
I'm having trouble at step 4. With the code below, I'm trying to replicate the text on the second canvas, in full red. Instead I get this weird picture.
code
// create the canvas to replicate the buffer text on.
var draw = new Drawing(true);
var bufferText = function (size, textFont) {
// set the font to Georgia if it isn't defined
textFont = textFont || "Georgia";
// create a new canvas buffer, true means that it's visible on the screen
// Note, Drawing is a small library I wrote, it's just a wrapper over the canvas API
// it creates a new canvas and adds some functions to the context
// it doesn't change any of the original functions
var buffer = new Drawing(true);
// context is just a small wrapper library I wrote to make the canvas API a little more bearable.
with (buffer.context) {
font = util.format("{size}px {font}", {size: size, font: textFont});
fillText("Hi there", 0, size);
}
// get the imagedata and store the actual pixels array in data
var imageData = buffer.context.getImageData(0, 0, buffer.canvas.width, buffer.canvas.height);
var data = imageData.data;
var index, alpha, x, y;
// loop over the pixels
for (x = 0; x < imageData.width; x++) {
for (y = 0; y < imageData.height; y++) {
index = x * y * 4;
alpha = data[index + 3];
// if the alpha is not equal to 0, draw a red pixel at (x, y)
if (alpha !== 0) {
with (draw.context) {
dot(x/4, y/4, {fillColor: "red"})
}
}
}
}
};
bufferText(20);
Note that here, my buffer is actually visible to show where the red pixels are supposed to go compared to where they actually go.
I'm really confused by this problem.
If anybody knows an alternative approach, that's very welcome too.
replace this...
index = x * y * 4;
with...
index = (imageData.width * y) + x;
the rest is good :)
I'm trying to implement ColorPicker using Canvas just for fun. But i seem lost. as my browser is freezing for a while when it loads due to all these for loops.
I'm adding the screenshot of the result of this script:
window.onload = function(){
colorPicker();
}
function colorPicker(){
var canvas = document.getElementById("colDisp"),
frame = canvas.getContext("2d");
var r=0,
g=0,
b= 0;
function drawColor(){
for(r=0;r<255;r++){
for(g=0;g<255;g++){
for(b=0;b<255;b++){
frame.fillStyle="rgb("+r+","+g+","+b+")";
frame.fillRect(r,g,1,1);
}
}
}
}
drawColor();
Currently , i only want a solution about the freezing problem with better algorithm and it's not displaying the BLACK and GREY colors.
Please someone help me.
Instead of calling fillRect for every single pixel, it might be a lot more efficient to work with a raw RGBA buffer. You can obtain one using context.getImageData, fill it with the color values, and then put it back in one go using context.putImageData.
Note that your current code overwrites each single pixel 255 times, once for each possible blue-value. The final pass on each pixel is 255 blue, so you see no grey and black in the output.
Finding a good way to map all possible RGB values to a two-dimensional image isn't trivial, because RGB is a three-dimensional color-space. There are a lot of strategies for doing so, but none is really optimal for any possible use-case. You can find some creative solutions for this problem on AllRGB.com. A few of them might be suitable for a color-picker for some use-cases.
If you want to fetch the rgba of the pixel under the mouse, you must use context.getImageData.
getImageData returns an array of pixels.
var pixeldata=context.getImageData(0,0,canvas.width,canvas.height);
Each pixel is defined by 4 sequential array elements.
So if you have gotten a pixel array with getImageData:
// first pixel defined by the first 4 pixel array elements
pixeldata[0] = red component of pixel#1
pixeldata[1] = green component of pixel#1
pixeldata[2] = blue component of pixel#1
pixeldata[4] = alpha (opacity) component of pixel#1
// second pixel defined by the next 4 pixel array elements
pixeldata[5] = red component of pixel#2
pixeldata[6] = green component of pixel#2
pixeldata[7] = blue component of pixel#2
pixeldata[8] = alpha (opacity) component of pixel#2
So if you have a mouseX and mouseY then you can get the r,g,b,a values under the mouse like this:
// get the offset in the array where mouseX,mouseY begin
var offset=(imageWidth*mouseY+mouseX)*4;
// read the red,blue,green and alpha values of that pixel
var red = pixeldata[offset];
var green = pixeldata[offset+1];
var blue = pixeldata[offset+2];
var alpha = pixeldata[offset+3];
Here's a demo that draws a colorwheel on the canvas and displays the RGBA under the mouse:
http://jsfiddle.net/m1erickson/94BAQ/
A way to go, using .createImageData():
window.onload = function() {
var canvas = document.getElementById("colDisp");
var frame = canvas.getContext("2d");
var width = canvas.width;
var height = canvas.height;
var imagedata = frame.createImageData(width, height);
var index, x, y;
for (x = 0; x < width; x++) {
for (y = 0; y < height; y++) {
index = (x * width + y) * 4;
imagedata.data[index + 0] = x;
imagedata.data[index + 1] = y;
imagedata.data[index + 2] = x + y - 255;
imagedata.data[index + 3] = 255;
}
}
frame.putImageData(imagedata, 0, 0);
};
http://codepen.io/anon/pen/vGcaF
i drew an image using drawImage() api of html5 canvas. now i want to fill the white spaces of that drew image with different colors (individual box individual color). how can i do it? i am using html5 canvas and jquery.
i want to fill the white spaces with different color and those white spaces are not proper rectangular box.
thanks in advance.
[ changed answer after clarification from questioner ]
Given: an image that has many fully-enclosed transparent areas.
This is a method to fill each transparent area with a different color:
Use context.getImageData to get an array of each canvas pixels [r,g,b,a] value.
Loop thru the array and find the first transparent pixel (the "a" value ==0)
Floodfill the entire transparent area containing that pixel with a new opaque color (replace the r,g,b values with your new color and replace the "a" value ==255).
Repeat step#2 until all the transparent areas have been filled with new unique colors.
To get you started...
William Malone wrote a very nice article on how to get and use canvas's [r,g,b,a] color array.
His article also shows you how to "floodfill" -- replace an existing color with a new color in an entire contiguous area.
In your case, you would replace transparent pixels with a new color.
This is his article:
http://www.williammalone.com/articles/html5-canvas-javascript-paint-bucket-tool/
[Addition to question: insert images into areas ]
You need to make each colorized area transparent again—one at a time
If you save each area's starting pixel from when you originally colorized the areas, you can start with that pixel and re-floodfill an area. This time you’ll set the alpha component of each pixel in that area to 0 (transparent).
As each single area is transparent you use compositing to draw the new image only where the existing pixels are transparent. The composite you want is context.globalCompositeOperation=”source-out”.
These examples show:
After uniquely colorizing each area.
After making 1 area transparent (the top-right area is transparent).
After compositing an image into the transparent area.
//draw some white, green, and blue stripes
for (var i = 0; i < canvas.width; i += 10) {
for (var j = 0; j < canvas.height; j += 10) {
context.fillStyle = (i % 20 === 0) ? "#fff" : ((i % 30 === 0) ? "#0f0" : "#00f");
context.fillRect(i, j, 10, 10);
}
}
var imagedata = context.getImageData(0, 0, canvas.width, canvas.height),
pixels = imagedata.data;
//if found white pixel i.e 255,255,255 changes it to 0,102,204
//you can change it to another color as u wish
for (var offset = 0, len = pixels.length; offset < len; offset += 4) {
if(pixels[offset]==255 &&
pixels[offset+1]==255 &&
pixels[offset+2]==255)
{pixels[offset] = 0; //
pixels[offset + 1] = 102; //
pixels[offset + 2] = 204; //
}
}
context.putImageData(imagedata, 0, 0);
My project is to input an image into a canvas tag in an HTML page, and then loop through the pixels and RGBA values of the pixels. While looping through the red values,so every fourth value in the pixel, I want to log the position of the pixels that represent a white pixel. Now, I have the image loading down with some code I got from this blog, http://www.phpied.com/photo-canvas-tag-flip/ .
He has another post in which he gives some code on how to loop through the pixels and log the information I want to log, but I don't understand it, and I don't want to copy his code without knowing what it is I'm doing. So could anybody please either explain the method he's using or perhaps show me another way to do what he's doing? This is the link to the other post http://www.phpied.com/pixel-manipulation-in-canvas/ .
It's straightforward.
All the pixel data for a canvas are stored sequentially in an array.
The first pixel's data occupy array elements #0-3 (red=0/green=1/blue=2/alpha=3).
The second pixel's data occupy array elements #4-7 (red=4/green=5/blue=6/alpha=7).
And so on...
You can load that pixel data by using context.getImageData() and enumerating through the array:
const imgData = context.getImageData(0, 0, canvas.width, canvas.height);
const data = imgData.data;
// enumerate all pixels
// each pixel's r,g,b,a datum are stored in separate sequential array elements
for(let i = 0; i < data.length; i += 4) {
const red = data[i];
const green = data[i + 1];
const blue = data[i + 2];
const alpha = data[i + 3];
}
You can also change those array values and then save the array back to the image using context.putImageData().
// save any altered pixel data back to the context
// the image will reflect any changes you made
context.putImageData(imgData, 0, 0);
The image will then change according to the changes you made to its pixel array.
Each pixel contains 4 components red, green, blue, alpha - each of them is number 0-255. The loop starts from top-left to bottom-right.
I recommend you to use an image processing framework in order to focus on the algorithms instead of manipulating arrays of values. Some frameworks:
fabric.js
processing.js
MarvinJ
In the case of MarvinJ, you can simply loop through pixels iterating column and row coordinates. I use the methods getIntComponentX() to access color components.
for(var y=0; y<image.getHeight(); y++){
for(var x=0; x<image.getWidth(); x++){
var red = image.getIntComponent0(x,y);
var green = image.getIntComponent1(x,y);
var blue = image.getIntComponent2(x,y);
}
}
Therefore you don't need to worry about how the pixel data is represented. In order to check if a pixel is white:
// Is white?
if(red >= 250 && blue >= 250 && green >= 250){
console.log("Pixel at "+x+","+y+" is white");
}
Runnable Example:
var canvas = document.getElementById("canvas");
image = new MarvinImage();
image.load("https://i.imgur.com/eLZVbQG.png", imageLoaded);
function imageLoaded(){
var whitePixels=0;
for(var y=0; y<image.getHeight(); y++){
for(var x=0; x<image.getWidth(); x++){
var red = image.getIntComponent0(x,y);
var green = image.getIntComponent1(x,y);
var blue = image.getIntComponent2(x,y);
var alpha = image.getAlphaComponent(x,y);
// Is white?
if(red >= 250 && green >= 250 && blue >= 250 && alpha > 0){
whitePixels++;
}
}
}
image.draw(canvas);
document.getElementById("result").innerHTML = "white pixels: "+whitePixels;
}
<script src="https://www.marvinj.org/releases/marvinj-0.7.js"></script>
<canvas id="canvas" width="500" height="344"></canvas>
<div id="result"></div>
Faced with such a problem. Need to implement the ability to corrections for Levels, as in Photoshop:
The problem is this: there are all the data for each pixel of image, there is data entered by the user InputWhite, InputBlack, OutputWhite, OutputBlack. (as shown in the figure).
I need to change every 3 channel specifically for each pixel (RGB).
How Can I change their ? I can not understand. Thanks in advance.
You can use the imageData of the canvas to modify the RGB values of individual pixels however you please.
For instance to "increase" the green "channel" of all pixels you could do this, which adds 30 to the value of each G in the RGB of every pixel:
var imageData = ctx.getImageData(0,0,can.width, can.height);
var pixels = imageData.data;
var numPixels = pixels.length;
ctx.clearRect(0, 0, can.width, can.height);
for (var i = 0; i < numPixels; i++) {
var average = (pixels[i*4] + pixels[i*4+1] + pixels[i*4+2]) /3;
// set red green and blue pixels to the average value
pixels[i*4] = 0;
pixels[i*4+1] += 30;
pixels[i*4+2] += 0;
}
ctx.putImageData(imageData, 0, 0);
Example:
http://jsfiddle.net/YxYuF/
From there it's just a question of making a nice UI to present to the user.