skip to Main Content

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?

2

Answers


  1. 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])
    
    Login or Signup to reply.
  2. 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 =nn" + 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 =nn" + p.get(0));
    var a = new Array();
    for (var i = 0; i < 100; i++) a.push(p.get(i));
    alert("Pixel 0-99 = nn" + a.toSource());
    
    p.set(0, [1, 200, 3]);
    alert("New Pixel 0=nn" + 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");
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search