如何根据一个特定的图像调整大量图像的颜色?
How to adjust the colors of a large number of images based on one spesific?
我有大量照片想要使用相同的 'level' - 相同的颜色/亮度/对比度等。为此,我有一个带有黑白颜色检查器的初始/指南(基本上是带颜色的方块),我将其添加到所有其他照片中。
这是初始/指南https://imgur.com/a/Jlozy1e and these are some of the photos https://imgur.com/JUsKMt2 , https://imgur.com/PvqsleR , https://imgur.com/tcMROU9
正如我所见,所有照片中带有小方块颜色(颜色控制方块)的区域必须具有相同的颜色(十六进制值),以便它们处于同一水平 - 这样我可以获得有意义的数据从下面的地带。
有没有办法在 photoshop 或其他工具中使用自动/批处理方式?
编辑:请注意,可能有比控制方块中的区域更暗/更亮的区域,我想保留它们(相应地得到 lighter/darker 但不完全用阈值颜色替换它们)
我不知道这是否可以通过任何高级工具实现,但这是我对 Photoshop 的看法。这个想法很简单——使用渐变贴图将目标颜色重新映射到源值(因此这不适用于 32 位 tiff):
- 来自活动文档(源文档)的样本源颜色;
- 求一个带其他文件打开的路径,开始一个一个打开;
- 采样目标颜色并获取它们在渐变贴图中的位置
- 使用源颜色和目标位置创建渐变贴图
这是我得到的结果:左行是原始文档,上面有一块源方块供参考,右行是应用了渐变图的结果文档,以及来自源文档的相同切片顶部(几乎看不见):
这是我制作的脚本。
请注意,我使用的是您的 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);
我会用 ImageMagick 自动执行此操作,它安装在大多数 Linux 发行版上,可用于 macOS 和 Windows.
首先,我会 运行 一个脚本来从您的校准图像中获取黑白点。这从校准条的黑色和白色端裁剪出 50x50 正方形,并计算它们在 50x50 正方形上的平均值。看起来像这样:
#!/bin/bash
# Check parameters
if [ $# -ne 1 ] ; then
echo "Usage: calibrate CALIBRATIONIMAGE" >&2
exit 1
fi
# Pick up parameter
image=
check="check-$image"
# User-adjustable x and y corrdinates of top-left corner of black and white rectangles
blkx0=660
blky0=300
whtx0=40
whty0=300
# Calculate bottom-right corners of rectangles, given top-left
((blkx1=blkx0+50))
((blky1=blky0+50))
((whtx1=whtx0+50))
((whty1=whty0+50))
# Output a check showing where we got black and white points from
convert "$image" -fill none \
-stroke red -draw "rectangle $blkx0,$blky0 $blkx1,$blky1" \
-stroke blue -draw "rectangle $whtx0,$whty0 $whtx1,$whty1" \
"$check"
# Output black and white points (as rounded percentages)
blkpt=$(convert "$image" -crop 50x50+$blkx0+$blky0 -format "%[fx:round(mean*100)]" info:)
whtpt=$(convert "$image" -crop 50x50+$whtx0+$whty0 -format "%[fx:round(mean*100)]" info:)
echo "[$image]: Black point: $blkpt, white point: $whtpt. Check image: [$check]"
你会 运行:
./calibrate calibration.png
并获得以下输出:
./calibrate calibration.png
[calibration.png]: Black point: 5, white point: 91. Check image: [check-calibration.png]
所以现在我们知道红色方块的平均亮度是 5,蓝色方块的平均亮度是 91,我们也可以检查方块是从哪里提取的。
现在我们需要将其应用于其他图像。让我们先做一个。 apply
的代码是:
#!/bin/bash
# Check parameters
if [ $# -ne 3 ] ; then
echo "Usage: apply blackpoint whitepoint image" >&2
exit 1
fi
# Pick up parameters
newblkpt=
newwhtpt=
image=
newname="corrected-$image"
# User-adjustable x and y coordinates of top-left corner of black and white rectangles
blkx0=670
blky0=100
whtx0=50
whty0=100
# Calculate bottom-right corners of rectangles, given top-left
((blkx1=blkx0+50))
((blky1=blky0+50))
((whtx1=whtx0+50))
((whty1=whty0+50))
# Output a check showing where we got black and white points from
convert "$image" -fill none \
-stroke red -draw "rectangle $blkx0,$blky0 $blkx1,$blky1" \
-stroke blue -draw "rectangle $whtx0,$whty0 $whtx1,$whty1" \
check-$image.png
# Get current black and white points
blkpt=$(convert "$image" -crop 50x50+$blkx0+$blky0 -format "%[fx:round(mean*100)]" info:)
whtpt=$(convert "$image" -crop 50x50+$whtx0+$whty0 -format "%[fx:round(mean*100)]" info:)
# The following line actually does the entire calibration!
convert "$image" -level ${blkpt},${whtpt}% +level ${newblkpt},${newwhtpt}% "$newname"
echo "[$image]: Black point: $blkpt, white point: $whtpt => [$newname]: Black point: $newblkpt, white point: $newwhtpt"
因此,如果我们 运行 将我们刚刚学到的 5、91 的校准应用到 im1.png
,我们得到:
./apply 5 91 im1.png
[im1.png]: Black point: 4, white point: 71 => [corrected-im1.png]: Black point: 5, white point: 91
这给了我们这个校正后的图像(白色明显凸起):
这张检查图像显示了我们校准的区域:
那么我们只需要一个循环来处理目录中的所有图像:
for f in *.png ; do
./apply 5 91 "$f"
done
这给了我们这些结果:
关键词: ImageMagick, command line, command line, image, image processing, calibrate, calibration, calibration strip, test strip.
请注意,如果您使用 ImageMagick v7 或更新版本,请将两个脚本中的命令 convert
替换为 magick
。
如果您想使用 Photoshop 执行此操作,您需要通过打开直方图 window 来获取校准图像中黑色校准方块的平均值,然后在黑色方块上绘制一个选取框并注意平均值 (11.89):
然后同样注意白色校准方块的平均值 231:
然后您需要在未校准图像中获得相同的两个值。黑色值为10:
而白色值为180:
现在添加一个色阶调整图层(见绿色区域)并输入上面的值(蓝色区域):
所以,我想您可以创建一个快捷方式,添加一个 色阶调整层 ,其中包含编程中校准图像中的两个值,然后将其批量应用于所有图像。然后您需要为每个特定图像手动添加其他两个值。
我有大量照片想要使用相同的 'level' - 相同的颜色/亮度/对比度等。为此,我有一个带有黑白颜色检查器的初始/指南(基本上是带颜色的方块),我将其添加到所有其他照片中。
这是初始/指南https://imgur.com/a/Jlozy1e and these are some of the photos https://imgur.com/JUsKMt2 , https://imgur.com/PvqsleR , https://imgur.com/tcMROU9
正如我所见,所有照片中带有小方块颜色(颜色控制方块)的区域必须具有相同的颜色(十六进制值),以便它们处于同一水平 - 这样我可以获得有意义的数据从下面的地带。
有没有办法在 photoshop 或其他工具中使用自动/批处理方式?
编辑:请注意,可能有比控制方块中的区域更暗/更亮的区域,我想保留它们(相应地得到 lighter/darker 但不完全用阈值颜色替换它们)
我不知道这是否可以通过任何高级工具实现,但这是我对 Photoshop 的看法。这个想法很简单——使用渐变贴图将目标颜色重新映射到源值(因此这不适用于 32 位 tiff):
- 来自活动文档(源文档)的样本源颜色;
- 求一个带其他文件打开的路径,开始一个一个打开;
- 采样目标颜色并获取它们在渐变贴图中的位置
- 使用源颜色和目标位置创建渐变贴图
这是我得到的结果:左行是原始文档,上面有一块源方块供参考,右行是应用了渐变图的结果文档,以及来自源文档的相同切片顶部(几乎看不见):
这是我制作的脚本。
请注意,我使用的是您的 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);
我会用 ImageMagick 自动执行此操作,它安装在大多数 Linux 发行版上,可用于 macOS 和 Windows.
首先,我会 运行 一个脚本来从您的校准图像中获取黑白点。这从校准条的黑色和白色端裁剪出 50x50 正方形,并计算它们在 50x50 正方形上的平均值。看起来像这样:
#!/bin/bash
# Check parameters
if [ $# -ne 1 ] ; then
echo "Usage: calibrate CALIBRATIONIMAGE" >&2
exit 1
fi
# Pick up parameter
image=
check="check-$image"
# User-adjustable x and y corrdinates of top-left corner of black and white rectangles
blkx0=660
blky0=300
whtx0=40
whty0=300
# Calculate bottom-right corners of rectangles, given top-left
((blkx1=blkx0+50))
((blky1=blky0+50))
((whtx1=whtx0+50))
((whty1=whty0+50))
# Output a check showing where we got black and white points from
convert "$image" -fill none \
-stroke red -draw "rectangle $blkx0,$blky0 $blkx1,$blky1" \
-stroke blue -draw "rectangle $whtx0,$whty0 $whtx1,$whty1" \
"$check"
# Output black and white points (as rounded percentages)
blkpt=$(convert "$image" -crop 50x50+$blkx0+$blky0 -format "%[fx:round(mean*100)]" info:)
whtpt=$(convert "$image" -crop 50x50+$whtx0+$whty0 -format "%[fx:round(mean*100)]" info:)
echo "[$image]: Black point: $blkpt, white point: $whtpt. Check image: [$check]"
你会 运行:
./calibrate calibration.png
并获得以下输出:
./calibrate calibration.png
[calibration.png]: Black point: 5, white point: 91. Check image: [check-calibration.png]
所以现在我们知道红色方块的平均亮度是 5,蓝色方块的平均亮度是 91,我们也可以检查方块是从哪里提取的。
现在我们需要将其应用于其他图像。让我们先做一个。 apply
的代码是:
#!/bin/bash
# Check parameters
if [ $# -ne 3 ] ; then
echo "Usage: apply blackpoint whitepoint image" >&2
exit 1
fi
# Pick up parameters
newblkpt=
newwhtpt=
image=
newname="corrected-$image"
# User-adjustable x and y coordinates of top-left corner of black and white rectangles
blkx0=670
blky0=100
whtx0=50
whty0=100
# Calculate bottom-right corners of rectangles, given top-left
((blkx1=blkx0+50))
((blky1=blky0+50))
((whtx1=whtx0+50))
((whty1=whty0+50))
# Output a check showing where we got black and white points from
convert "$image" -fill none \
-stroke red -draw "rectangle $blkx0,$blky0 $blkx1,$blky1" \
-stroke blue -draw "rectangle $whtx0,$whty0 $whtx1,$whty1" \
check-$image.png
# Get current black and white points
blkpt=$(convert "$image" -crop 50x50+$blkx0+$blky0 -format "%[fx:round(mean*100)]" info:)
whtpt=$(convert "$image" -crop 50x50+$whtx0+$whty0 -format "%[fx:round(mean*100)]" info:)
# The following line actually does the entire calibration!
convert "$image" -level ${blkpt},${whtpt}% +level ${newblkpt},${newwhtpt}% "$newname"
echo "[$image]: Black point: $blkpt, white point: $whtpt => [$newname]: Black point: $newblkpt, white point: $newwhtpt"
因此,如果我们 运行 将我们刚刚学到的 5、91 的校准应用到 im1.png
,我们得到:
./apply 5 91 im1.png
[im1.png]: Black point: 4, white point: 71 => [corrected-im1.png]: Black point: 5, white point: 91
这给了我们这个校正后的图像(白色明显凸起):
这张检查图像显示了我们校准的区域:
那么我们只需要一个循环来处理目录中的所有图像:
for f in *.png ; do
./apply 5 91 "$f"
done
这给了我们这些结果:
关键词: ImageMagick, command line, command line, image, image processing, calibrate, calibration, calibration strip, test strip.
请注意,如果您使用 ImageMagick v7 或更新版本,请将两个脚本中的命令 convert
替换为 magick
。
如果您想使用 Photoshop 执行此操作,您需要通过打开直方图 window 来获取校准图像中黑色校准方块的平均值,然后在黑色方块上绘制一个选取框并注意平均值 (11.89):
然后同样注意白色校准方块的平均值 231:
然后您需要在未校准图像中获得相同的两个值。黑色值为10:
而白色值为180:
现在添加一个色阶调整图层(见绿色区域)并输入上面的值(蓝色区域):
所以,我想您可以创建一个快捷方式,添加一个 色阶调整层 ,其中包含编程中校准图像中的两个值,然后将其批量应用于所有图像。然后您需要为每个特定图像手动添加其他两个值。