Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get the color of one pixel at Photoshop scripts

I am trying to figure out how to get the color of one defined Pixel.

In my imagination its shoud look like:

color = get.color.Pixel(x,y);

Maybe someone can help me with this piece of code?

like image 667
lphd Avatar asked Jan 10 '18 13:01

lphd


People also ask

How do I make all pixels one color in Photoshop?

Using the Color Range Tool to Select Color in Photoshop Once the image is loaded into Photoshop, go to Select > Color Range. In the Color Range menu, make sure it says Sampled Colors in the dropdown menu. Set the Fuzziness slider to 0, and set Range to 100 percent.

How do you determine the color of a pixel?

With the get() function we can read the color of any pixel in our program window. We can specify which pixel we are interested in by using x and y coordinates as parameters. For example, color mycolor = get(100, 200); would grab the color of pixel 100, 200 and put that color into the mycolor variable.

How do I find the color value in Photoshop?

Click somewhere on an open design, hold down and drag, and then you can actually sample color from anywhere on your screen. To get the HEX code, just double click the foreground color and a window with color information will pop up. Then copy the hexidemal #123456 value (HEX code) to your clipboard.


2 Answers

Photoshop's JavaScript API doesn't provide a mechanism like you imagine in your question.

You'll need to utilize the Document.colorSamplers.add([x, y]) method, then read each component color value via its properties:

The following gist shows how to obtain either rgb or cmyk values for a given x,y coordinate:

#target photoshop

// Define the x and y coordinates for the pixel to sample.
var x = 1;
var y = 1;

// Add a Color Sampler at a given x and y coordinate in the image.
var pointSample = app.activeDocument.colorSamplers.add([(x - 1),(y - 1)]);

// Obtain array of RGB values.
var rgb = [
    pointSample.color.rgb.red,
    pointSample.color.rgb.green,
    pointSample.color.rgb.blue
];

// Obtain array of rounded CMYK values.
var cmyk = [
    Math.round(pointSample.color.cmyk.cyan),
    Math.round(pointSample.color.cmyk.magenta), 
    Math.round(pointSample.color.cmyk.yellow),
    Math.round(pointSample.color.cmyk.black)
];

// Remove the Color Sampler.
pointSample.remove();

// Display the complete RGB values and each component color.
alert('RGB: ' + rgb)
alert('red: ' + rgb[0])
alert('green: ' + rgb[1])
alert('blue: ' + rgb[2])

// Display the complete CMYK values and each component color.
alert('CMYK: ' + cmyk)
alert('cyan: ' + cmyk[0])
alert('magenta: ' + cmyk[1])
alert('yellow: ' + cmyk[2])
alert('black: ' + cmyk[3])
like image 132
RobC Avatar answered Nov 14 '22 19:11

RobC


Here's a simple script using a ColorSampler. It's set up to return RGB values.

function PixelSampler(doc) {
    this.doc = doc
    this.doc.colorSamplers.removeAll();
    this.sampler = this.doc.colorSamplers.add([0, 0]);
}

// Return an array of R, G, B pixel values for a particular coordinate.
PixelSampler.prototype.get = function (x, y) {
    this.sampler.move([x, y]);
    const R = this.sampler.color.rgb.red;
    const G = this.sampler.color.rgb.green;
    const B = this.sampler.color.rgb.blue;
    return [R, G, B];
}

////////////////////////////////////////////////////////
/// SOME TESTS /////////////////////////////////////////
////////////////////////////////////////////////////////

const p = new PixelSampler(app.activeDocument);
alert("Pixel 0 =\n\n" + p.get(0, 0));

$.hiresTimer;
var n = 1000; //p.width * p.height;
for (var i = 0; i < n; i++) p.get(i, 0);
sec = ($.hiresTimer / 1000 / 1000);
alert("Got " + (n / 1000) + " kilopixels in " + sec.toFixed(2) + " seconds.");

This gives me pixel values at about 100 pixels per second on my machine.

I found this and cleaned up the script a bit. Basically, the idea is to:

  1. Save the current image as a raw bitmap.
  2. Read it back in, but on the javascript side.
  3. Do all access to pixels on the javascript side.

This gives me pixel values at about 72,000 pixels per second, not including the overhead of writing the raw data to disk and reading it back in. It has the added benefit that you can set pixel values, too.

// Adapted from https://community.adobe.com/t5/photoshop/get-index-of-each-pixel/td-p/10022899?page=1
// The purpose is to query (and change) pixel values quickly.
//
// The secret to speed is doing everything on the script side rather than ask Photoshop to do things.
// We use files on disk as an intermediary; on the script side, we read / write it as a binary file; on the
// Photoshop side, we save / open it as a raw bitmap.
//
// Only works on RGB 8bpp images, but this could be easily extended to support others.
function RawPixels(doc) {
    this.doc = doc;

    const currentActiveDoc = app.activeDocument;

    // Obtain the width and height in pixels of the desired document.
    const currentRulerUnits = app.preferences.rulerUnits;
    app.preferences.rulerUnits = Units.PIXELS;
    app.activeDocument = doc;
    this.width = Number(doc.width.value);
    this.height = Number(doc.height.value);
    this.length = this.width * this.height;
    this.pixelData = "";

    // Return the ruler to its previous state.
    app.preferences.rulerUnits = currentRulerUnits;

    try {
        // We're going to save this document as a raw bitmap to be able to read back in the pixel values
        // themselves.
        const file = new File(Folder.temp.fsName + "/" + Math.random().toString().substr(2) + ".raw");

        // Set up the save action.
        // See https://helpx.adobe.com/photoshop/using/file-formats.html#photoshop_raw_format for some info,
        // and more technical at https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/
        var rawFormat = new ActionDescriptor();
        rawFormat.putString(stringIDToTypeID("fileCreator"), "8BIM");
        rawFormat.putBoolean(stringIDToTypeID("channelsInterleaved"), true);
        
        var saveAction = new ActionDescriptor();
        saveAction.putObject(stringIDToTypeID("as"), stringIDToTypeID("rawFormat"), rawFormat);
        saveAction.putPath(stringIDToTypeID("in"), file);
        saveAction.putBoolean(stringIDToTypeID("copy"), false);
        executeAction(stringIDToTypeID("save"), saveAction, DialogModes.NO);

        // File is saved; now read it back in as raw bytes.
        file.open("r");
        file.encoding = "BINARY";
        this.pixelData = file.read();

        const err = file.error;
        file.close();
        file.remove();
        file = null;
        if (err) alert(err);
    }
    catch (e) { alert(e); }

    // Return focus to whatever the user had.
    app.activeDocument = currentActiveDoc;
}

// Calculate offset from x, y coordinates. Does not check for valid bounds.
getOffset = function(x, y) {
    if (y == undefined) {
        // allow linear indices too
        y = Math.floor(x / this.width); 
        x = x - y * this.width;
    }
    return (y * this.width + x) * 3;
}

// Return an array of R, G, B pixel values for a particular coordinate.
RawPixels.prototype.get = function (x, y) {
    const off = getOffset(x, y);
    const R = this.pixelData.charCodeAt(off + 0);
    const G = this.pixelData.charCodeAt(off + 1);
    const B = this.pixelData.charCodeAt(off + 2);
    return [R, G, B];
}

// Set the pixel at x, y to the values in RGB.
RawPixels.prototype.set = function (RGB, x, y) {
    const off = getOffset(x, y);
    // note: note checking that length of p = 3!
    const R = String.fromCharCode(RGB[0]);
    const G = String.fromCharCode(RGB[1]);
    const B = String.fromCharCode(RGB[2]);

    this.pixelData = this.pixelData.substr(0, off) + R + G + B + this.pixelData.substr(off + 3);
}

// If any changes were made to the pixels, we need to save them to disk and have Photoshop read that file back in.
// We do that by creating a new layer in the desired document.
RawPixels.prototype.create_layer = function () {
    try {
        const file = new File(Folder.temp.fsName + "/" + Math.random().toString().substr(2) + ".raw");
        file.open("w");
        file.encoding = "BINARY";
        file.write(this.pixelData);

        const err = file.error;
        file.close();
        if (err) { file.remove(); alert(err); return; }

        var rawFormat = new ActionDescriptor();
        rawFormat.putInteger(stringIDToTypeID("width"), this.width);
        rawFormat.putInteger(stringIDToTypeID("height"), this.height);
        rawFormat.putInteger(stringIDToTypeID("channels"), 3);
        rawFormat.putBoolean(stringIDToTypeID("channelsInterleaved"), true);
        rawFormat.putInteger(stringIDToTypeID("depth"), 8);

        var openAction = new ActionDescriptor();
        openAction.putPath(stringIDToTypeID("null"), file);
        openAction.putObject(stringIDToTypeID("as"), stringIDToTypeID("rawFormat"), rawFormat);
        executeAction(stringIDToTypeID("open"), openAction, DialogModes.NO);
        file.remove();

        // The new active document is the file we just opened. Duplicate its contents into 
        // a new layer in our desired document, then close this temporary file.
        app.activeDocument.activeLayer.duplicate(this.doc.layers[0], ElementPlacement.PLACEBEFORE);
        const tempDoc = app.activeDocument;
        app.activeDocument = this.doc;
        this.doc.layers[0].name = "Pixels";
        app.activeDocument = tempDoc;
        app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
        app.activeDocument = this.doc;
    }
    catch (e) { alert(e); }
}

////////////////////////////////////////////////////////
/// SOME TESTS /////////////////////////////////////////
////////////////////////////////////////////////////////

$.hiresTimer;
const p = new RawPixels(app.activeDocument);
var sec = ($.hiresTimer / 1000 / 1000);
alert("Init RawPixels in " + sec.toFixed(2) + " seconds");

alert("Pixel 0 =\n\n" + p.get(0));
var a = new Array();
for (var i = 0; i < 100; i++) a.push(p.get(i));
alert("Pixel 0-99 = \n\n" + a.toSource());

p.set(0, [1, 200, 3]);
alert("New Pixel 0=\n\n" + p.get(0));

$.hiresTimer;
var n = p.width * p.height;
for (var i = 0; i < n; i++) p.get(i);
sec = ($.hiresTimer / 1000 / 1000);
alert("Got " + (n / 1000 / 1000) + " megapixels in " + sec.toFixed(2) + " seconds.");

$.hiresTimer;
n = 10;
for (var i = 0; i < n; i++) p.set([255, i * 20, i * 10], 1 + i * 2);
sec = ($.hiresTimer / 1000 / 1000);
//alert("Set " + n + " pixels in " + sec.toFixed(2) + " seconds");

p.create_layer();
alert("New layer created  with new pixels");
like image 22
darda Avatar answered Nov 14 '22 20:11

darda