首页 > 解决方案 > 如何根据一个特定调整大量图像的颜色?

问题描述

我有大量照片,我想将它们带到相同的“水平”——相同的颜色/亮度/对比度等。为此,我有一个带有黑白颜色检查器的初始/指南(基本上是带颜色的正方形) ,我将其添加到所有其他照片中。

这是初始/指南https://imgur.com/a/Jlozy1e这些是一些照片https://imgur.com/JUsKMt2、https://imgur.com/PvqsleR、https://imgurcom/tcMROU9

正如我所看到的那样,所有照片中带有小方块颜色(颜色控制方块)的区域必须是相同的颜色(十六进制值),以便它们处于同一水平 - 所以我可以从下面的条带中获得有意义的数据。

有没有办法在 Photoshop 或其他工具中使用自动/批处理方式?

编辑:请注意,可能比我想保留它们的控制方块中的区域更暗/更亮(只是相应地变亮/变暗,但不能完全用阈值颜色替换它们)

标签: photoshoptiff

解决方案


我不知道这是否可以使用任何高级工具,但这是我对 Photoshop 的看法。这个想法很简单——使用渐变映射将目标颜色重新映射到源值(因此这不适用于 32 位 tiff):

  1. 来自活动文档(源文档)的样本源颜色;
  2. 要求打开其他文件的路径,开始一一打开;
  3. 对目标颜色进行采样并获取它们在渐变映射中的位置
  4. 使用源颜色和目标位置创建渐变图

这是我得到的结果:左行是原始文档,上面有一块源方块供参考,右行是应用了渐变图的结果文档,顶部是源文档的相同切片(几乎不可见) :

在此处输入图像描述

这是我制作的脚本。

请注意,我使用的是您的 png 文件,因此如果您的文件大小不同,您可能需要调整颜色采样器的坐标。

var sampler, sampledColors, sourceCoords, targetCoords;

// defining coordinates to sample 6 colors from the active source-document
sourceCoords = [
    [55, 318],
    [190, 318],
    [310, 318],
    [420, 318],
    [560, 318],
    [690, 318],
];

// defining coordinates to sample target colors from target documents
targetCoords = [
    [78, 120],
    [206, 120],
    [328, 120],
    [453, 120],
    [577, 120],
    [709, 120],
]

// a library
var Utils = Utils ||
{

    // will add photoshop Color Sampler to document
    addSample: function(coord)
    {
        return app.activeDocument.colorSamplers.add(coord);
    },

    // reads color from a Color Sampler
    readSample: function(sample)
    {
        return sample.color;
    },

    // gets a collection of Color Samplers
    getSamplers: function()
    {
        return app.activeDocument.colorSamplers;
    },

    // deletes a Color Sampler
    deleteSample: function(sample)
    {
        sample.remove();
    },

    // RGB > YUV color translation
    rgb2yuv: function(rgb)
    {
        var r = rgb[0] / 255,
            g = rgb[1] / 255,
            b = rgb[2] / 255;

        var y = (r * 0.299) + (g * 0.587) + (b * 0.114);
        var u = (r * -0.14713) + (g * -0.28886) + (b * 0.436);
        var v = (r * 0.615) + (g * -0.51499) + (b * -0.10001);

        return [y, u, v];
    },

    // Linear transformation
    linear: function(X, A, B, C, D, _cut)
    {
        var _cut = _cut !== undefined ? _cut : false;
        var Y = (X - A) / (B - A) * (D - C) + C
        if (_cut)
        {
            if (Y > D) Y = D;
            if (Y < C) Y = C;
        }
        return Y;
    },

    // changes active document color space to RGB
    docToRgb: function()
    {
        var desc16 = new ActionDescriptor();
        desc16.putClass(charIDToTypeID('T   '), charIDToTypeID('RGBM'));
        desc16.putBoolean(charIDToTypeID('Fltt'), false);
        desc16.putBoolean(charIDToTypeID('Rstr'), false);
        executeAction(charIDToTypeID('CnvM'), desc16, DialogModes.NO);
    },

    /**
     * @description Creates a rectangle selection in a specific coordinates with a predefined delta: -7 / +7 to 'coord' values
     * @param  {array}  - [0] is X, [1] is Y coordinates
     *
     * @return nothing
     */
    rectangleSelection: function(coord)
    {
        var delta = 7;
        var descRectangleSelection = new ActionDescriptor();
        var rectSelectionRef = new ActionReference();
        rectSelectionRef.putProperty(charIDToTypeID('Chnl'), charIDToTypeID('fsel'));
        descRectangleSelection.putReference(charIDToTypeID('null'), rectSelectionRef);
        var descCoords = new ActionDescriptor();
        descCoords.putUnitDouble(charIDToTypeID('Top '), charIDToTypeID('#Pxl'), coord[1] - delta);
        descCoords.putUnitDouble(charIDToTypeID('Left'), charIDToTypeID('#Pxl'), coord[0] - delta);
        descCoords.putUnitDouble(charIDToTypeID('Btom'), charIDToTypeID('#Pxl'), coord[1] + delta);
        descCoords.putUnitDouble(charIDToTypeID('Rght'), charIDToTypeID('#Pxl'), coord[0] + delta);
        descRectangleSelection.putObject(charIDToTypeID('T   '), charIDToTypeID('Rctn'), descCoords);
        executeAction(charIDToTypeID('setd'), descRectangleSelection, DialogModes.NO);
    },

    /**
     * @description saves an active document as a TIF file
     * @param  {object} data - .name (without extension) for a name and data.path for a path
     *
     * @return nothing
     */
    saveTIF: function(data)
    {
        if (!new Folder(data.path).exists) new Folder(data.path).create();
        var desc = new ActionDescriptor();
        var descOptions = new ActionDescriptor();
        descOptions.putEnumerated(charIDToTypeID('BytO'), charIDToTypeID('Pltf'), charIDToTypeID('Mcnt'));
        descOptions.putEnumerated(stringIDToTypeID('layerCompression'), charIDToTypeID('Encd'), stringIDToTypeID('RLE'));
        desc.putObject(charIDToTypeID('As  '), charIDToTypeID('TIFF'), descOptions);
        desc.putPath(charIDToTypeID('In  '), new File(data.path + "/" + data.name + ".tif"));
        executeAction(charIDToTypeID('save'), desc, DialogModes.NO);
    },
};

// this will get colors from the source document
var getSamplersData = function(coordinates)
{
    var colors = [];
    var color, sampler;

    // makes sure the doc is in rgb
    Utils.docToRgb();

    // for all coordinates..
    for (var i = 0; i < coordinates.length; i++)
    {
        // create a rectangular selection of 14x14 pixels in the coordinate
        Utils.rectangleSelection(coordinates[i]);

        // average blur it to make sure color sampler samples an average color from noisy square because there's no option for color sample size for Color Samplers
        activeDocument.activeLayer.applyAverage();
        activeDocument.selection.deselect();

        // ads a color sample
        sampler = Utils.addSample(coordinates[i]);

        // reads a color sample
        color = Utils.readSample(sampler);

        // color is added to [colors]
        colors.push(color);
        Utils.deleteSample(sampler);
    }
    return colors;
};

// creates gradient maps for new documents
var setSamplerData = function()
{
    var workFolder;

    var controller = function(originalColors)
    {
        var docs, doc, docSampler, sampledColors, gradientColors;

        try
        {
            docs = getDocs(); // asks for a folder to work with
        }
        catch (e)
        {
            return false;
        }

        // for all found documents...
        for (var i = 0; i < docs.length; i++)
        {
            try
            {
                // opening it and makes sure it's in rgb mode
                doc = openDocument(docs[i]);
            }
            catch (e)
            {
                return false;
            }

            // getting current colors in the color boxes
            sampledColors = getSamplersData(targetCoords);

            // create an array of color for a gradient map using current colors positions and original colors
            gradientColors = createGradientDataFromColors(originalColors, sampledColors);

            // creates a gradient map
            createGradient(gradientColors);

            // saves a file
            Utils.saveTIF(
            {
                path: workFolder + "/export",
                name: activeDocument.name
            });
        }
    };

    /////////////////////////////////////////////////////////////////////////////////////
    // this will as for a folder and will return found docs
    var getDocs = function()
    {
        var docs;

        workFolder = Folder.selectDialog();
        if (workFolder == null) throw 'cancelled';

        docs = workFolder.getFiles('*');

        for (var i = docs.length - 1; i >= 0; i--)
        {

            if (docs[i] instanceof Folder) docs.splice(i, 1);
        }

        if (docs.length == 0) throw 'no files in the folder';

        return docs;
    }; // end of getDocs()

    // opens a doc and makes sure it's in rgb color mode
    var openDocument = function(path)
    {
        var doc;
        try
        {
            doc = app.open(new File(path));
            Utils.docToRgb();
            return doc;
        }
        catch (e)
        {
            alert("can't open " + path + "\nAborting");
            throw e;
        }
    };

    // this will create a gradient map 
    var createGradientDataFromColors = function(original, sampled)
    {
        var colors = [];
        var rgbOriginal, rgbSampled, positionSampled;

        for (var i = 0; i < original.length; i++)
        {
            rgbOriginal = getRGB(original[i]); // get an array of [r,g,b] from SolidColor object
            rgbSampled = getRGB(sampled[i]); // get an array of [r,g,b] from SolidColor object
            positionSampled = Math.round(Utils.rgb2yuv(rgbSampled)[0] * 10000) / 100; // getting positions from the current document colors

            colors.push(
            {
                color: rgbOriginal,
                pos: positionSampled
            });
        }

        return colors;
    }; // end of createGradientDataFromColors()

    // this will convert an rgb from Solid Color to an array of [r, g and b]
    var getRGB = function(color)
    {
        return [color.rgb.red, color.rgb.green, color.rgb.blue];
    }; // end of getRGB()

    // creates a gradient map
    // colors are from the original doc, positions are from the target docs
    var createGradient = function(data)
    {
        var descGradMap = new ActionDescriptor();
        var referenceMap = new ActionReference();
        referenceMap.putClass(charIDToTypeID('AdjL'));
        descGradMap.putReference(charIDToTypeID('null'), referenceMap);
        var desc5 = new ActionDescriptor();
        var desc6 = new ActionDescriptor();
        var desc7 = new ActionDescriptor();

        desc7.putEnumerated(charIDToTypeID('GrdF'), charIDToTypeID('GrdF'), charIDToTypeID('CstS'));
        desc7.putDouble(charIDToTypeID('Intr'), 4096.000000);

        var list1 = new ActionList();
        var el;

        for (var i = 0; i < data.length; i++)
        {
            el = data[i];

            var descTemp = new ActionDescriptor();
            var descColor = new ActionDescriptor();
            descColor.putDouble(charIDToTypeID('Rd  '), el.color[0]);
            descColor.putDouble(charIDToTypeID('Grn '), el.color[1]);
            descColor.putDouble(charIDToTypeID('Bl  '), el.color[2]);
            descTemp.putObject(charIDToTypeID('Clr '), charIDToTypeID('RGBC'), descColor);
            descTemp.putEnumerated(charIDToTypeID('Type'), charIDToTypeID('Clry'), charIDToTypeID('UsrS'));
            descTemp.putInteger(charIDToTypeID('Lctn'), Utils.linear(el.pos, 0, 100, 0, 4096));
            descTemp.putInteger(charIDToTypeID('Mdpn'), 50);
            list1.putObject(charIDToTypeID('Clrt'), descTemp);
        }

        desc7.putList(charIDToTypeID('Clrs'), list1);

        var list2 = new ActionList();
        var desc12 = new ActionDescriptor();
        desc12.putUnitDouble(charIDToTypeID('Opct'), charIDToTypeID('#Prc'), 100.000000);
        desc12.putInteger(charIDToTypeID('Lctn'), 0);
        desc12.putInteger(charIDToTypeID('Mdpn'), 50);
        list2.putObject(charIDToTypeID('TrnS'), desc12);
        var desc13 = new ActionDescriptor();
        desc13.putUnitDouble(charIDToTypeID('Opct'), charIDToTypeID('#Prc'), 100.000000);
        desc13.putInteger(charIDToTypeID('Lctn'), 4096);
        desc13.putInteger(charIDToTypeID('Mdpn'), 50);
        list2.putObject(charIDToTypeID('TrnS'), desc13);
        desc7.putList(charIDToTypeID('Trns'), list2);

        desc6.putObject(charIDToTypeID('Grad'), charIDToTypeID('Grdn'), desc7);
        desc5.putObject(charIDToTypeID('Type'), charIDToTypeID('GdMp'), desc6);

        descGradMap.putObject(charIDToTypeID('Usng'), charIDToTypeID('AdjL'), desc5);
        executeAction(charIDToTypeID('Mk  '), descGradMap, DialogModes.NO);
    };

    return controller;
};

sampledColors = getSamplersData(sourceCoords);

sampler = setSamplerData();
sampler(sampledColors);

推荐阅读