Is there any way in JQuery to change part of an image? - javascript

if i have an image (jpg, png, gif .doesn't really matter ) and i want to have some javascript code change part of an image.
for example, lets say i have this picture of a cookie like here.
and i have a color picker that lets a person select a color. I want to change the color of part of the image (in this case, lets say the color of the chocolate chips) to the color that is picked.
is that possible to do in javascript / jquery ?

Its possible with Javascript and the canvas element by directly manipulating the pixel data. The below example turns blue into red.
Live Demo
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
image = document.getElementById("testImage");
canvas.height = canvas.width = 45;
ctx.drawImage(image,0,0);
var imgd = ctx.getImageData(0, 0, 45, 45),
pix = imgd.data;
for (var i = 0, n = pix.length; i <n; i += 4) {
if(pix[i + 2] > 20){ // Blue threshold
// Swap red and blue component values.
var redVal = pix[i]; // Copy the current red component value
pix[i] = pix[i + 2]; // Assign the current blue component value to red
pix[i+2] = redVal; // Assign the old red value to blue.
}
}
ctx.putImageData(imgd, 0, 0);
Its not very fast to do it this way however, and for larger images you would see a noticeable performance drop depending on the browser. AS for jQuery, there is nothing related to this that jQuery provides that would help.

Related

HTML 5 canvas drawing image not showing css

When I try to draw an image on a canvas with a pre-loaded image with css like:
img.style.backgroundColor="red";
ctx.drawImage(img,0,0,100,100);
I find that the image is being drawn as it would appear without the css. Do HTML canvases support css? If not is there a way to overlay a png with transparency with a color only on the pixels that are not transparent?
Can you specify more about our requirement?
You can not set the background colour of an image which is going to draw directly from the canvas. if you altering a color, it will reflect in the source image.
You have to make it from the source element. If you want to fill the box size with some color, you could use fillStyle of the ctx before draw.
Have a look at the example here on w3schools that will get you started on how to load the image and copy it into the canvas. If you don't need the original image to be shown on the page then add 'style="display:none;"' to the img tag. To tint the image have a look at globalAlpha in combination with a filled rect - something along these lines:
window.onload = function() {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var img = document.getElementById("scream");
ctx.globalAlpha = 0.5;
ctx.beginPath();
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#ff0000";
ctx.fill();
ctx.drawImage(img, 10, 10);
};
Canvas do not render css no matter what css you use for image it will always render plain image unless you draw border or background by yourself
It is possible to overlay the image drawn on a canvas by analyzing each pixel of the drawn image and then overlaying a 1x1 rectangle with the desired color. Here is an example of how to do so:
function overlayColor(img,x,y,a,r,g,b,drawWidth,drawHeight){
//create a canvas in memory and draw the image there to analyze it
var tmpCanvas = document.createElement('canvas');
var context = tmpCanvas.getContext('2d');
tmpCanvas.width = drawWidth;
tmpCanvas.height = drawHeight;
context.drawImage(img,0,0,drawWidth,drawHeight);
let pData = new Array();
for (let i = 0; i < tmpCanvas.width; i++){
for (let j = 0; j < tmpCanvas.height; j++){
if (context.getImageData(i, j, 1, 1).data[3] != 0){//if the pixel is not transparent then add the coordinates to the array
pData.push([i,j]);
}
}
}
//then every pixel that wasn't transparent will be overlayed with a 1x1 rectangle of the inputted color
//drawn at the (x,y) origin which was also given by the user
//this uses ctx, the context of the canvas visible to the user
ctx.fillStyle="rgba(" + r + "," + g + "," + b + "," + a + ")";
for (let i = 0; i < pData.length; i++){
ctx.fillRect(x + pData[i][0],y + pData[i][1],1,1);
}
}
Since the function takes an x and y value the image given by the user will be analyzed and overlayed only on pixels that are not transparent at the coordinate given by the user with the rgba value also given. I have found that this process can result in some lag but it could be overcome by saving the pData array and using the second half of the function to draw the array on screen again.

how to apply alpha layer mask to make some of the image transparent using canvas

Can someone help me to fix the issue?
I want to apply alpha layer mask to make some of the image transparent using canvas.
Thanks a lot.
var redImageData = redCanvas.getContext("2d").getImageData(0, 0, 200, 200); //overlay
var ImageData = imageCanvas.getContext("2d").getImageData(0, 0, 200, 200);
var px = redImageData.data;
var px2 = ImageData.data;
for(var i = 0; i < px.length; i += 4) {
if(px[i + 0] == 0 && px[i + 1] == 0 && px[i+2] == 0){
px[i + 3] = 0;
} else {
px[i + 0] = px2[i + 0];
px[i + 1] = px2[i + 1];
px[i + 2] = px2[i + 2];
px[i + 3] = px2[i + 3];
}
}
ctx.imageSmoothingEnabled = true;
ctx.putImageData(redImageData, 0, 0);
alpha mask overly https://i.stack.imgur.com/zCzOf.png
The linked image is not really an alpha mask but a matte image. The difference is that a matte image represent what would be an alpha-channel but showing it as RGB (or gray-scale) components. It doesn't actually have an alpha-channel. Matte images are common in video-compositing software but not so useful on web.
Canvas, or the Porter-Duff methods it uses, does not support matte images directly so they have to first be converted to an actual alpha-channel. To do this you have to iterate over each pixel and move one of the component values (from red, green or blue - doesn't matter which) into the alpha-channel.
When that is done you can use the canvas object that now has proper alpha-channel with composite operations which only uses the alpha information (blending modes is a different chapter).
The better approach is of course to provide the images as PNG with a proper alpha channel. But in any case, to show it's possible to also work with matte images, although not as efficient, we can do the following:
Converting matte image into alpha channel
First step: this code section shows how you can efficiently do the pre-step of converting the matte image into an alpha channel. The resulting colors are not important for the main compositing step as it will only use the alpha-channel, as already mentioned.
Just make sure the image has loaded properly before trying to use the image by either using the image's onload callback or running the script after everything has loaded.
This code will simply shift a component (blue in this case) using the full 32-bit value of the pixel (for efficiency) into the alpha-channel which leaves the image looking cyan but with proper alpha as you can see with the orange background showing through (most of the code is to handle loading and setup though).
window.onload = function() {
// at this stage the image has loaded:
var img = document.getElementById("img");
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
// - setup canvas to match image
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
// - draw image
ctx.drawImage(img, 0, 0);
// CONV. STEP: move a component channel to alpha-channel
var idata = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data32 = new Uint32Array(idata.data.buffer);
var i = 0, len = data32.length;
while(i < len) {
data32[i] = data32[i++] << 8; // shift blue channel into alpha (little-endian)
}
// update canvas
ctx.putImageData(idata, 0, 0);
};
body {background: #f72; font-size:44px; color:rgba(0,0,0,0.5)}
<img id="img" crossorigin="anonymous" src="https://i.imgur.com/QRGYuWg.png"> ►
<canvas id="c"></canvas>
Compositing
The second part then becomes about compositing using the new alpha-channel.
In this case the matte image's black becomes fully transparent while the white becomes fully opaque. You could have reversed this in the conversion step but it does not really matte as long as you're aware of the how the matte image looks.
To replace the interior content we use compositing mode source-in. This will replace the content depending on the alpha value while keeping the alpha-channel as it is.
Dealing with the interior part first using the same mode allows us to do additional things with the content before drawing the frame (think vignette, shadows etc.).
As the final step we fill in the transparent areas, the frame itself, by using the composite mode destination-over which replaces the more transparent ares with the content being drawn to canvas (conceptually it draws "behind" the existing content).
The code below uses simple colored boxes - just replace those with whatever you want to draw.
window.onload = function() {
var img = document.getElementById("img");
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
ctx.drawImage(img, 0, 0);
var idata = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data32 = new Uint32Array(idata.data.buffer);
var i = 0, len = data32.length;
while(i < len) data32[i] = data32[i++] << 8;
ctx.putImageData(idata, 0, 0);
// COMP. STEPS: use the mask with composite operation. Since our frame
// is black (= transparent as alpha) we can use the following mode:
ctx.globalCompositeOperation = "source-in";
// draw something, here a blue box, replace with whatever you want
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// to fill the frame area, still transparent, use this mode:
ctx.globalCompositeOperation = "destination-over";
ctx.fillStyle = "yellow";
ctx.fillRect(0, 0, canvas.width, canvas.height);
};
body {background: #f72; font-size:44px; color:rgba(0,0,0,0.5)}
<img id="img" crossorigin="anonymous" src="https://i.imgur.com/QRGYuWg.png"> ►
<canvas id="c"></canvas>

HTML5 Canvas Make Black Transparent

I have a large amount of images with a black background, here is one for example:
Is it possible through Javascript to have to ignore the black (#000000) and have it draw on canvas? to appear like this?
Basically trying to take the black pixels and make it an alpha channel.
So you'll need to run through all the pixels and change the alpha value of all the black pixels.
https://jsfiddle.net/0kuph15a/2/
This code creates a buffer (empty canvas) to draw the original image to. Once thats done, it takes all the pixels of this buffer canvas and then iterates over all the pixels and checks their values. I add up the Red, Blue, and Green values to see if they are less then 10 (just incase some pixels aren't pure black 0), but would visually appear black to the human eye. If it is less then 10 it simply turns the alpha value of that pixel to 0.
var canvas = document.getElementById('main');
var ctx = document.getElementById('main').getContext('2d');
var tile = new Image();
tile.src = document.getElementById('image').src;
tile.onload = function() {
draw(tile);
}
function draw(img) {
var buffer = document.createElement('canvas');
var bufferctx = buffer.getContext('2d');
bufferctx.drawImage(img, 0, 0);
var imageData = bufferctx.getImageData(0,0,buffer.width, buffer.height);
var data = imageData.data;
var removeBlack = function() {
for (var i = 0; i < data.length; i += 4) {
if(data[i]+ data[i + 1] + data[i + 2] < 10){
data[i + 3] = 0; // alpha
}
}
ctx.putImageData(imageData, 0, 0);
};
removeBlack();
}
You can easily change this line if(data[i]+ data[i + 1] + data[i + 2] < 10){ to if(data[i]+ data[i+1] + data[i+2]==0){ if you know for a fact only #000 blacks are used.
You can accomplish that using blend modes.
Change the context globalCompositeOperation to screen, and you can get that result. Here's an example:
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var image = new Image();
image.src = "https://images.blogthings.com/thecolorfulpatterntest/pattern-4.png";
image.onload = function() {
context.drawImage(image, 0, 0, canvas.width, canvas.height);
var blackImage = new Image();
blackImage.src="http://www.printmag.com/wp-content/uploads/Sillitoe-black-white.gif";
blackImage.onload = function(){
context.globalCompositeOperation = "screen";
context.drawImage(blackImage, 0, 0, canvas.width, canvas.height);
}
};
<canvas id="canvas" width="300" height="250"></canvas>
<hr/>
<h1>Images used:</h1>
<img src="https://images.blogthings.com/thecolorfulpatterntest/pattern-4.png"/>
<img src="http://www.printmag.com/wp-content/uploads/Sillitoe-black-white.gif"/>
How about saving the picture as an .svg file...from there you can change all colors and other settings
Felipe's answer addressed my issue. Alpha pixel manipulation does not work
(eg, setting every 4th pixel to 0) for preserving alphatransparency with multiple images added into the same context at the same time.
eg:
this.ctx1.putImageData(output, 0, 0); // without setting the context's globalCompositeOperation, this image is written over by the next putImage operation
this.ctx1.putImageData(output, 20, 0);
Go here to review the blending options. https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation

HTML5 Canvas transparency weirdness with pngs

I'm trying to apply transparency to some ImageData retrieved from a canvas. Simple, right? Just set an appropriate value to every fourth datapoint... only, it only seems to work with pixels that don't have pre-existing alpa values.
//create 2 canvases
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);
var ctx = canvas.getContext("2d");
canvas.id = "tempCanvas";
var canvas2 = document.createElement("canvas");
document.body.appendChild(canvas2);
var ctx2 = canvas2.getContext("2d");
canvas2.id = "tempCanvas2";
//draw the png in the first canvas
var testimg = document.getElementById("testimg");
ctx.drawImage(testimg, 0, 0, 200, 200);
//helper function to opacity to the pixels
opacity = function(pixels, value){
var d = pixels.data;
for(var i=0;i<d.length;i+=4){
d[i+3] = value*255; //scale by 255
if(d[i+3] > 255) d[i+3] = 255; //clamp
}
return pixels;
}
//make the first canvas' image data opaque
var data = ctx.getImageData(0,0,200,200);
data = opacity(data, 0.5);
//draw to second canvas
ctx2.fillStyle="#900";
ctx2.fillRect(0,0,200,200);
//should get a jsfiddle logo overlayed on red background
//instead, get yucky grey background
ctx2.putImageData(data, 0, 0);
(It's a little hard to reproduce because of same-origin policy, but here's a fiddle that uses the png jsfiddle logo: http://jsfiddle.net/97c0eetr/ )
Why does this opacity algorithm not work for the jsfiddle logo (and my other transparent pngs that I'm testing on http://localhost)?
Edit: FF42 Ubuntu, if it makes a difference.
You've hit the big endian / little endian issue : On most of today's computers/devices, the endianness is little endian, meaning that for a 32 bit value, byte order is 3-2-1-0.
So the opacity, which is the 4th byte (index 3) will be found at pixelIndex * 4 + 0 ... == pixelIndex * 4.
Just change, in your code, your opacity function to :
opacity = function(pixels, value){
var d = pixels.data;
for(var i=0;i<d.length;i+=4){
d[i] = value*255; //scale by 255
if(d[i] > 255) d[i] = 255; //clamp
}
return pixels;
}
updated fiddle here :
http://jsfiddle.net/97c0eetr/5/
The best 100% solution of course is to have two opacity function that you choose depending on endinannes of the browser. But by betting on little endian, you cover like 99% of the devices.
If you want to know endianness, i once made a small gist to get it, find it here :
https://gist.github.com/TooTallNate/4750953
Here's the code :
function endianness () {
var b = new ArrayBuffer(4);
var a = new Uint32Array(b);
var c = new Uint8Array(b);
a[0] = 0xdeadbeef;
if (c[0] == 0xef) return 'LE';
if (c[0] == 0xde) return 'BE';
throw new Error('unknown endianness');
}
And by the way, your function could simplify to
opacity = function(pixels, value){
value = Math.floor(value * 255);
if (value>255) value = 255;
var d = pixels.data;
for(var i=0;i<d.length;i+=4){
d[i] = value;
}
return pixels;
}
Edit : So why the background isn't red ? -->> When using putImageData, you are using a raw 1 pixel for 1 pixel copy method. So everything gets replaced, whatever, for instance, the globalCompositeOperation or any setting. So the red background is just overwritten by the new data.
Solution is to putImageData on a temporary canvas -it will be written with the right opacity-, then to drawImage it on your target canvas, so that the opacity is taken into account for the draw.
Since you already had two canvases, change was easy, see updated fiddle here :
http://jsfiddle.net/97c0eetr/4/

Get average color of image via Javascript

Not sure this is possible, but looking to write a script that would return the average hex or rgb value for an image. I know it can be done in AS but looking to do it in JavaScript.
AFAIK, the only way to do this is with <canvas/>...
DEMO V2: http://jsfiddle.net/xLF38/818/
Note, this will only work with images on the same domain and in browsers that support HTML5 canvas:
function getAverageRGB(imgEl) {
var blockSize = 5, // only visit every 5 pixels
defaultRGB = {r:0,g:0,b:0}, // for non-supporting envs
canvas = document.createElement('canvas'),
context = canvas.getContext && canvas.getContext('2d'),
data, width, height,
i = -4,
length,
rgb = {r:0,g:0,b:0},
count = 0;
if (!context) {
return defaultRGB;
}
height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height;
width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width;
context.drawImage(imgEl, 0, 0);
try {
data = context.getImageData(0, 0, width, height);
} catch(e) {
/* security error, img on diff domain */
return defaultRGB;
}
length = data.data.length;
while ( (i += blockSize * 4) < length ) {
++count;
rgb.r += data.data[i];
rgb.g += data.data[i+1];
rgb.b += data.data[i+2];
}
// ~~ used to floor values
rgb.r = ~~(rgb.r/count);
rgb.g = ~~(rgb.g/count);
rgb.b = ~~(rgb.b/count);
return rgb;
}
For IE, check out excanvas.
Figured I'd post a project I recently came across to get dominant color:
Color Thief
A script for grabbing the dominant color or a representative color palette from an image. Uses javascript and canvas.
The other solutions mentioning and suggesting dominant color never really answer the question in proper context ("in javascript"). Hopefully this project will help those who want to do just that.
"Dominant Color" is tricky. What you want to do is compare the distance between each pixel and every other pixel in color space (Euclidean Distance), and then find the pixel whose color is closest to every other color. That pixel is the dominant color. The average color will usually be mud.
I wish I had MathML in here to show you Euclidean Distance. Google it.
I have accomplished the above execution in RGB color space using PHP/GD here: https://gist.github.com/cf23f8bddb307ad4abd8
This however is very computationally expensive. It will crash your system on large images, and will definitely crash your browser if you try it in the client. I have been working on refactoring my execution to:
- store results in a lookup table for future use in the iteration over each pixel.
- to divide large images into grids of 20px 20px for localized dominance.
- to use the euclidean distance between x1y1 and x1y2 to figure out the distance between x1y1 and x1y3.
Please let me know if you make progress on this front. I would be happy to see it. I will do the same.
Canvas is definitely the best way to do this in the client. SVG is not, SVG is vector based. After I get the execution down, the next thing I want to do is get this running in the canvas (maybe with a webworker for each pixel's overall distance calculation).
Another thing to think about is that RGB is not a good color space for doing this in, because the euclidean distance between colors in RGB space is not very close to the visual distance. A better color space for doing this might be LUV, but I have not found a good library for this, or any algorythims for converting RGB to LUV.
An entirely different approach would be to sort your colors in a rainbow, and build a histogram with tolerance to account for varying shades of a color. I have not tried this, because sorting colors in a rainbow is hard, and so are color histograms. I might try this next. Again, let me know if you make any progress here.
First: it can be done without HTML5 Canvas or SVG.
Actually, someone just managed to generate client-side PNG files using JavaScript, without canvas or SVG, using the data URI scheme.
Edit: You can simply create a PNG with document.getElementById("canvas").toDataURL("image/png", 1.0)
Second: you might actually not need Canvas, SVG or any of the above at all.
If you only need to process images on the client side, without modifying them, all this is not needed.
You can get the source address from the img tag on the page, make an XHR request for it - it will most probably come from the browser cache - and process it as a byte stream from Javascript.
You will need a good understanding of the image format. (The above generator is partially based on libpng sources and might provide a good starting point.)
function get_average_rgb(img) {
var context = document.createElement('canvas').getContext('2d');
if (typeof img == 'string') {
var src = img;
img = new Image;
img.setAttribute('crossOrigin', '');
img.src = src;
}
context.imageSmoothingEnabled = true;
context.drawImage(img, 0, 0, 1, 1);
return context.getImageData(0, 0, 1, 1).data.slice(0,3);
}
console.log(get_average_rgb(document.querySelector('#img1')));
console.log(get_average_rgb(document.querySelector('#img2')));
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M/wHwAEBgIApD5fRAAAAABJRU5ErkJggg==" width="32" height="32" id="img1">
<img src="https://lh3.googleusercontent.com/a/AEdFTp4Wi1oebZlBCwFID8OZZuG0HLsL-xIxO5m2TNw=k-s32" id="img2" crossOrigin="anonymous">
Less accurate but fastest way to get average color of the image with datauri support:
function get_average_rgb(img) {
var context = document.createElement('canvas').getContext('2d');
if (typeof img == 'string') {
var src = img;
img = new Image;
img.setAttribute('crossOrigin', '');
img.src = src;
}
context.imageSmoothingEnabled = true;
context.drawImage(img, 0, 0, 1, 1);
return context.getImageData(0, 0, 1, 1).data.slice(0,3);
}
I would say via the HTML canvas tag.
You can find here a post by #Georg talking about a small code by the Opera dev :
// Get the CanvasPixelArray from the given coordinates and dimensions.
var imgd = context.getImageData(x, y, width, height);
var pix = imgd.data;
// Loop over each pixel and invert the color.
for (var i = 0, n = pix.length; i < n; i += 4) {
pix[i ] = 255 - pix[i ]; // red
pix[i+1] = 255 - pix[i+1]; // green
pix[i+2] = 255 - pix[i+2]; // blue
// i+3 is alpha (the fourth element)
}
// Draw the ImageData at the given (x,y) coordinates.
context.putImageData(imgd, x, y);
This invert the image by using the R, G and B value of each pixel. You could easily store the RGB values, then round up the Red, Green and Blue arrays, and finally converting them back into an HEX code.
This is #350D's answer but async (as some images may take time to load) and in typescript
async function get_average_rgb(src: string): Promise<Uint8ClampedArray> {
/* https://stackoverflow.com/questions/2541481/get-average-color-of-image-via-javascript */
return new Promise(resolve => {
let context = document.createElement('canvas').getContext('2d');
context!.imageSmoothingEnabled = true;
let img = new Image;
img.src = src;
img.crossOrigin = "";
img.onload = () => {
context!.drawImage(img, 0, 0, 1, 1);
resolve(context!.getImageData(0, 0, 1, 1).data.slice(0,3));
};
});
}
I recently came across a jQuery plugin which does what I originally wanted https://github.com/briangonzalez/jquery.adaptive-backgrounds.js in regards to getting a dominiate color from an image.
EDIT: Only after posting this, did I realize that #350D's answer does the exact same thing.
Surprisingly, this can be done in just 4 lines of code:
const canvas = document.getElementById("canvas"),
preview = document.getElementById("preview"),
ctx = canvas.getContext("2d");
canvas.width = 1;
canvas.height = 1;
preview.width = 400;
preview.height = 400;
function getDominantColor(imageObject) {
//draw the image to one pixel and let the browser find the dominant color
ctx.drawImage(imageObject, 0, 0, 1, 1);
//get pixel color
const i = ctx.getImageData(0, 0, 1, 1).data;
console.log(`rgba(${i[0]},${i[1]},${i[2]},${i[3]})`);
console.log("#" + ((1 << 24) + (i[0] << 16) + (i[1] << 8) + i[2]).toString(16).slice(1));
}
// vvv all of this is to just get the uploaded image vvv
const input = document.getElementById("input");
input.type = "file";
input.accept = "image/*";
input.onchange = event => {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = readerEvent => {
const image = new Image();
image.onload = function() {
//shows preview of uploaded image
preview.getContext("2d").drawImage(
image,
0,
0,
preview.width,
preview.height,
);
getDominantColor(image);
};
image.src = readerEvent.target.result;
};
reader.readAsDataURL(file, "UTF-8");
};
canvas {
width: 200px;
height: 200px;
outline: 1px solid #000000;
}
<canvas id="preview"></canvas>
<canvas id="canvas"></canvas>
<input id="input" type="file" />
How it works:
Create the canvas context
const context = document.createElement("canvas").getContext("2d");
This will draw the image to only one canvas pixel, making the browser find the dominant color for you.
context.drawImage(imageObject, 0, 0, 1, 1);
After that, just get the image data for the pixel:
const i = context.getImageData(0, 0, 1, 1).data;
Finally, convert to rgba or HEX:
const rgba = `rgba(${i[0]},${i[1]},${i[2]},${i[3]})`;
const HEX = "#" + ((1 << 24) + (i[0] << 16) + (i[1] << 8) + i[2]).toString(16).slice(1);
There is one problem with this method though, and that is that getImageData will sometimes throw errors Unable to get image data from canvas because the canvas has been tainted by cross-origin data., which is the reason you need to upload images in the demo instead of inputting a URL for example.
This method can also be used for pixelating images by increasing the width and height to draw the image.
This works on chrome but may not on other browsers.
Javascript does not have access to an image's individual pixel color data. At least, not maybe until html5 ... at which point it stands to reason that you'll be able to draw an image to a canvas, and then inspect the canvas (maybe, I've never done it myself).
All-In-One Solution
I would personally combine Color Thief along with this modified version of Name that Color to obtain a more-than-sufficient array of dominant color results for images.
Example:
Consider the following image:
You can use the following code to extract image data relating to the dominant color:
let color_thief = new ColorThief();
let sample_image = new Image();
sample_image.onload = () => {
let result = ntc.name('#' + color_thief.getColor(sample_image).map(x => {
const hex = x.toString(16);
return hex.length === 1 ? '0' + hex : hex;
}).join(''));
console.log(result[0]); // #f0c420 : Dominant HEX/RGB value of closest match
console.log(result[1]); // Moon Yellow : Dominant specific color name of closest match
console.log(result[2]); // #ffff00 : Dominant HEX/RGB value of shade of closest match
console.log(result[3]); // Yellow : Dominant color name of shade of closest match
console.log(result[4]); // false : True if exact color match
};
sample_image.crossOrigin = 'anonymous';
sample_image.src = document.getElementById('sample-image').src;
This is about "Color Quantization" that has several approachs like MMCQ (Modified Median Cut Quantization) or OQ (Octree Quantization). Different approach use K-Means to obtain clusters of colors.
I have putted all together here, since I was finding a solution for tvOS where there is a subset of XHTML, that has no <canvas/> element:
Generate the Dominant Colors for an RGB image with XMLHttpRequest
As pointed out in other answers, often what you really want the dominant color as opposed to the average color which tends to be brown. I wrote a script that gets the most common color and posted it on this gist
To get the average (not the dominant) color of an image, create a tiny canvas of the scaled-down original image (of max size i.e: 10 px). Then loop the canvas area imageData to construct the average RGB:
const getAverageColor = (img) => {
const max = 10; // Max size (Higher num = better precision but slower)
const {naturalWidth: iw, naturalHeight: ih} = img;
const ctx = document.createElement`canvas`.getContext`2d`;
const sr = Math.min(max / iw, max / ih); // Scale ratio
const w = Math.ceil(iw * sr); // Width
const h = Math.ceil(ih * sr); // Height
const a = w * h; // Area
img.crossOrigin = 1;
ctx.canvas.width = w;
ctx.canvas.height = h;
ctx.drawImage(img, 0, 0, w, h);
const data = ctx.getImageData(0, 0, w, h).data;
let r = g = b = 0;
for (let i=0; i<data.length; i+=4) {
r += data[i];
g += data[i+1];
b += data[i+2];
}
r = ~~(r/a);
g = ~~(g/a);
b = ~~(b/a);
return {r, g, b};
};
const setBgFromAverage = (img) => {
img.addEventListener("load", () => {
const {r,g,b} = getAverageColor(img);
img.style.backgroundColor = `rgb(${r},${g},${b})`;
});
};
document.querySelectorAll('.thumb').forEach(setBgFromAverage);
.thumbs { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.thumb { height: 100px; padding: 20px; }
<div class="thumbs">
<img class="thumb" alt="image" src="https://i.imgur.com/22BrBjx.jpeg">
<img class="thumb" alt="image" src="https://i.imgur.com/MR0dUpw.png">
<img class="thumb" alt="image" src="https://i.imgur.com/o7lpiDR.png">
<img class="thumb" alt="image" src="https://i.imgur.com/egYvHp6.jpeg">
<img class="thumb" alt="image" src="https://i.imgur.com/62EAzOY.jpeg">
<img class="thumb" alt="image" src="https://i.imgur.com/3VxBMeF.jpeg">
</div>
There is a online tool pickimagecolor.com that helps you to find the average or the dominant color of image.You just have to upload a image from your computer and then click on the image. It gives the average color in HEX , RGB and HSV. It also find the color shades matching that color to choose from. I have used it multiple times.

Categories