How to Get a Hex Color Code from a Solid-Color Image for a Script

How to get a Hex Color Code from a solid-color image for a script?

I would highly recommend to use ImageMagick for this task. The documentation mentions how to extract data from an image.

From "Extracting the average colour":

The average color of an image can be found very quickly by using "-scale" to reduce an image to a single pixel. Here for example is the average color of the built-in "rose:" image. I output the color using the FX Escape Format which
returns a color string that can be used directly IM without change.

user@laptop:~$ convert rose: -scale 1x1\! -format '%[pixel:s]\n' info:-

Will output: srgb(146,89,80)

In your case, replace rose: with an image file of yours, like foo.png.

If you want the output directly in hex notation, use this:

convert rose: -scale 1x1\! -format '%[pixel:s]\n' info:- | awk -F '[(,)]' '{printf("#%x%x%x\n",$2,$3,$4)}'

Get Hex Color Code & Coordinate of The Pixel in A Image

If you want to have iteration along the x-axis, you can write like:

from PIL import Image

img = Image.open('img.png')
pixels = img.load()
width, height = img.size

for y in range(height): # this row
for x in range(width): # and this row was exchanged
r, g, b = pixels[x, y]

# in case your image has an alpha channel
# r, g, b, a = pixels[x, y]

print(x, y, f"#{r:02x}{g:02x}{b:02x}")

Thinking of the for statement, x and y are going to change like (x,y) = (0,0), (0,1),... in your initial code.
You want to first iterate over x and then iterate over y; you can write iteration over y, wrapping iteration over x.

How to get effective result (6-digit hex value) of solid background color and translucent overlay color?

You need alpha compositing formula:

result_red =  fg_red * α_fg + bg_red * (1 - α_fg)
result_blue = fg_blue * α_fg + bg_blue * (1 - α_fg)
result_green = fg_green * α_fg + bg_green * (1 - α_fg)

You can found more information and examples in W3C Candidate Recommendation

Find Hex Colors and X,Y in Images

Must check all pixels

To find a pixel that matches a color will require, in the worst case (pixel of that color not in image), that you step over every pixel in the image.

How not to do it

Converting every pixel to a DOM string is about the worst way to do it, as DOM string use a lot of memory and CPU overhead, especially if instantiated using jQuery (which has its own additional baggage)

Hex color to array

To find the pixel you need only check each pixels color data against the HEX value. You convert the hex value to an array of 3 Bytes.

The following function will convert from CSS Hex formats "#HHH" "#HHHH", "#HHHHHH" and "#HHHHHHHH" ignoring the alpha part if included, to an array of integers 0-255

const hex2RGB = h => {
if(h.length === 4 || h.length === 5) {
return [parseInt(h[1] + h[1], 16), parseInt(h[2] + h[2], 16), parseInt(h[3] + h[3], 16)];
}
return [parseInt(h[1] + h[2], 16), parseInt(h[3] + h[4], 16), parseInt(h[5] + h[6], 16)];
}

Finding the pixel

I do not know how you plan to use such a feature so the example below is a general purpose method that will help and can be modified as needed

It will always find a pixel if you let it even if there is no perfect match. It does this by finding the closest color to the color you are looking for.

The reason that of finds the closest match is that when you draw an image onto a 2D canvas the pixel values are modified slightly if the image has transparent pixels (pre-multiplied alpha)

The function finds the pixel by measuring the spacial distance between the pixel and the hex color (simple geometry Pythagoras). The closest color is the one that is the smallest distance.

It will return the object

{
x, // the x coordinate of the match
y, // the y coordinate of the match
distance, // how closely the color matches the requested color.
// 0 means a perfect match
// to 441 completely different eg black and white
// value is floored to an integer value
}

If the image is tainted (cross origin, local device storage), or you pass something that can not be converted to pixels the function will return undefined

The function keeps a canvas that it uses to get pixel data as it assumes that it will be use many times. If the image is tainted it will catch the error (add a warning to the console), cleanup the tainted canvas and be ready for another image.

Usage

To use the function add it to your code base, it will setup automatically.

Get an image and a hex value and call the function with the image, CSS hex color, and optionally the threshold distance for the color match.

Eg find exact match for #FF0000

const result = findPixel(origImage, "#FF0000", 0); // find exact match for red
if (result) { // only if found
console.log("Found color #FF0000 at pixel " + result.x + ", " + result.y);
} else {
console.log("The color #FF0000 is not in the image");
}

or find color close to

const result = findPixel(origImage, "#FF0000", 20); // find a match for red
// within 20 units.
// A unit is 1 of 256
if (result) { // only if found
console.log("Found closest color within " + result.distance + "units of #FF0000 at pixel " + result.x + ", " + result.y);
}

or find closest

// find the closest, no threshold ensures a result
const result = findPixel(origImage, "#FF0000");
console.log("Found closest color within " + result.distance + "units of #FF0000 at pixel " + result.x + ", " + result.y);

Code

The function is as follows.

const findPixel = (() => {
var can, ctx;
function createCanvas(w, h) {
if (can === undefined){
can = document.createElement("canvas");
ctx = can.getContext("2d");
}
can.width = w;
can.height = h;
}
function getPixels(img) {
const w = img.naturalWidth || img.width, h = img.naturalHeight || img.height;
createCanvas(w, h);
ctx.drawImage(img, 0, 0);
try {
const imgData = ctx.getImageData(0, 0, w, h);
can.width = can.height = 1; // make canvas as small as possible so it wont
// hold memory. Leave in place to avoid instantiation overheads
return imgData;
} catch(e) {
console.warn("Image is un-trusted and pixel access is blocked");
ctx = can = undefined; // canvas and context can no longer be used so dump them
}
return {width: 0, height: 0, data: []}; // return empty pixel data
}
const hex2RGB = h => { // Hex color to array of 3 values
if(h.length === 4 || h.length === 5) {
return [parseInt(h[1] + h[1], 16), parseInt(h[2] + h[2], 16), parseInt(h[3] + h[3], 16)];
}
return [parseInt(h[1] + h[2], 16), parseInt(h[3] + h[4], 16), parseInt(h[5] + h[6], 16)];
}
const idx2Coord = (idx, w) => ({x: idx % w, y: idx / w | 0});
return function (img, hex, minDist = Infinity) {
const [r, g, b] = hex2RGB(hex);
const {width, height, data} = getPixels(img);
var idx = 0, found;
while (idx < data.length) {
const R = data[idx] - r;
const G = data[idx + 1] - g;
const B = data[idx + 2] - b;
const d = R * R + G * G + B * B;
if (d === 0) { // found exact match
return {...idx2Coord(idx / 4, width), distance: 0};
}
if (d < minDist) {
minDist = d;
found = idx;
}
idx += 4;
}
return found ? {...idx2Coord(found / 4, width), distance: minDist ** 0.5 | 0 } : undefined;
}
})();

This function has been tested and works as described above.

Note Going by the code in the your question the alpha value of the image and CSS hex color is ignored.

Note that if you intend to find many colors from the same image this function is not the best suited for you needs. If this is the case let me know in the comment and I can make changes or instruct you how to optimism the code for such uses.

Note It is not well suited for single use only. However if this is the case change the line const findPixel = (() => { to var findPixel = (() => { and after you have used it remove the reference findpixel = undefined; and JS will clean up any resources it holds.

Note If you also want to get the actual color of the closest found color that is trivial to add as well. Ask in the comments.

Note It is reasonably quick (you will be hard pressed to get a quicker result) but be warned that for very large images 4K and above it may take a bit, and on very low end devices it may cause a out of memory error. If this is a problem then another solution is possible but is far slower.

Any way to use external list of color hex codes to change layer fill in Photoshop?

Maybe learning JavaScript would ultimately allow a simplified approach, but this is what I did.

  1. First I created a CSV file of the color hex codes I wanted:



















    filenameredgreenblue
    BackGround1255255255

    Create a hexadecimal colour based on a string with JavaScript

    Just porting over the Java from Compute hex color code for an arbitrary string to Javascript:

    function hashCode(str) { // java String#hashCode
    var hash = 0;
    for (var i = 0; i < str.length; i++) {
    hash = str.charCodeAt(i) + ((hash << 5) - hash);
    }
    return hash;
    }

    function intToRGB(i){
    var c = (i & 0x00FFFFFF)
    .toString(16)
    .toUpperCase();

    return "00000".substring(0, 6 - c.length) + c;
    }

    To convert you would do:

    intToRGB(hashCode(your_string))

    Batch extract Hex colour from images to file

    I would suggest ImageMagick which is installed on most Linux distros and is available for OSX (via homebrew) and Windows.

    So, just at the command-line, in a directory full of JPG images, you could run this:

    convert *.jpg -gravity center -crop 1x1+0+0 -format "%f,%[fx:int(mean.r*255)],%[fx:int(mean.g*255)],%[fx:int(mean.b*255)]\n" info:

    Sample Output

    a.png,127,0,128
    b.jpg,127,0,129
    b.png,255,0,0

    Notes:

    If you have more files in a directory than your shell can glob, you may be better of letting ImageMagick do the globbing internally, rather than using the shell, with:

    convert '*.jpg' ...

    If your files are large, you may better off doing them one at a time in a loop rather than loading them all into memory:

    for f in *.jpg; do convert "$f" ....... ; done

    How to get hex color value rather than RGB value?

    var hexDigits = new Array
    ("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f");

    //Function to convert rgb color to hex format
    function rgb2hex(rgb) {
    rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
    return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
    }

    function hex(x) {
    return isNaN(x) ? "00" : hexDigits[(x - x % 16) / 16] + hexDigits[x % 16];
    }

    (Source)

    How can ImageMagick output hex colours instead of SRGB?

    You got an error most likely because your version of ImageMagick is too old. The changelog says:

    2017-06-02 6.9.8-9 Cristy <quetzlzacatenango@image...>
    Add support for 'hex:' property.

    If that version or later use:

    convert rose: -scale 1x1\! -format "%[hex:u]\n" info:
    925950

    convert rose: -scale 1x1\! -format "%[hex:s]\n" info:
    925950

    convert rose: -scale 1x1\! -format "%[hex:u.p{0,0}]\n" info:
    925950

    convert rose: -scale 1x1\! -format "#%[hex:u]\n" info:
    #925950

    convert rose: -scale 1x1\! -format "#%[hex:s]\n" info:
    #925950

    convert rose: -scale 1x1\! -format "#%[hex:u.p{0,0}]\n" info:
    #925950

    If earlier, then

    convert rose: -scale 1x1\! txt: | tail -n +2 | sed -n 's/^.*[#]\(.*\) .*$/\1/p'
    925950

    convert rose: -scale 1x1\! txt: | tail -n +2 | sed -n 's/^.*\([#].*\) .*$/\1/p'
    #925950

    It is always best to provide your ImageMagick version and platform, when asking questions about ImageMagick commands, since syntax may vary and new feature may be added or bugs fixed.



    Related Topics



    Leave a reply



    Submit