The implementation of functional "Levels" like in Photoshop - javascript

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.

Related

Get Color Data of HTML5 Canvas

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

JavaScript canvas, manually cloning a canvas onto another generates a weird pattern

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 :)

ColorPicker implementation using JavaScript and Canvas

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

Looping through pixels in an image

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>

Find height of rendered/visible text

I know how to get this height of a font:
By placing the text in a div and getting offset height of the div.
But I would like to get this actual height (Which will depend on font family):
Is that in any way possible using web based programming?
Is there a simple solution? I think the answer is no.
If you're ok with a more involved (and processor-intensive) solution, you could try this:
Render the text to a canvas, then use canvasCtx.getImageData(..) to retrieve pixel information. Next you would do something similar to what this pseudo code describes:
first_y : null
last_y : null
for each y:
for each x:
if imageData[x][y] is black:
if first_y is null:
first_y = y
last_y = y
height = last_y - first_y
This basically looks for the top (lowest y-index) of the lettering (black pixels) and the bottom (highest y-index) then subtracts to retrieve the height.
I was writing the code while Jason answered, but I decided to post it anyway:
http://jsfiddle.net/adtn8/2/
If you follow the comments you should get the idea what's going on and why. It works pretty fast and it's not so complicated as it may sound. Checked with GIMP and it is accurate.
(code to be sure it wont be lost):
// setup variables
var c = document.createElement('canvas'),
div = document.getElementsByTagName('div')[0],
out = document.getElementsByTagName('output')[0];
// set canvas's size to be equal with div
c.width = div.offsetWidth;
c.height = div.offsetHeight;
var ctx = c.getContext('2d');
// get div's font from computed style and apply it to context
ctx.font = window.getComputedStyle(div).font;
// use color other than black because all pixels are 0 when black and transparent
ctx.fillStyle = '#bbb';
// draw the text near the bottom of the canvas
ctx.fillText(div.innerText, 0, div.offsetHeight);
// loop trough the canvas' data to find first colored pixel
var data = ctx.getImageData(0, 0, c.width, c.height).data,
minY = 0, len = data.length;
for (var i = 0; i < len; i += 4) {
// when you found it
if (data[i] != 0) {
// get pixel's y position
minY = Math.floor(i / 4 / c.width);
break;
}
}
// and print out the results
out.innerText = c.height - minY + 'px';
EDIT:
I even made jQuery plugin for this: https://github.com/maciek134/jquery-textHeight
Enjoy.

Categories

Resources