在 Photoshop 脚本中获取一个像素的颜色
Posted
技术标签:
【中文标题】在 Photoshop 脚本中获取一个像素的颜色【英文标题】:Get the color of one pixel at Photoshop scripts 【发布时间】:2018-06-19 17:11:02 【问题描述】:我正在尝试弄清楚如何获得一个已定义像素的颜色。
在我的想象中应该是这样的:
color = get.color.Pixel(x,y);
也许有人可以帮助我处理这段代码?
【问题讨论】:
【参考方案1】:Photoshop 的 javascript API 没有提供您在问题中想象的机制。
您需要使用Document.colorSamplers.add([x, y])
方法,然后通过其属性读取每个组件的颜色值:
以下要点显示如何获取给定x,y
坐标的rgb
或cmyk
值:
#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])
【讨论】:
如果您需要扫描大面积而不是一个像素(((您将等待数小时扫描 1000x1000 像素图像 @bodich colorSamplers 可以移动 - 如果你移动而不是为每个像素重新创建它,它仍然很慢吗? @darda 我刚刚测量了 100x100 像素区域的时间。重新创建颜色采样器时,需要 60 秒才能完成循环。移动颜色采样器时,需要 32 秒。速度快了两倍,但速度仍然非常慢。如果您移动颜色采样器,12 兆像素的图片将在 10 小时内处理完毕。 PS:8核Intel i7【参考方案2】:这是一个使用ColorSampler
的简单脚本。它设置为返回 RGB 值。
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.");
这使我的机器上的像素值约为 100 像素/秒。
我找到了this 并稍微清理了脚本。基本上,这个想法是:
-
将当前图像保存为原始位图。
重新读入,但在 javascript 端。
在 javascript 端对像素进行所有访问。
这为我提供了大约 72,000 像素每秒的像素值,不包括将原始数据写入磁盘并将其读回的开销。它还有一个额外的好处是您可以设置像素值,也是。
// 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");
【讨论】:
以上是关于在 Photoshop 脚本中获取一个像素的颜色的主要内容,如果未能解决你的问题,请参考以下文章