I would like to know if it is possible to match the colors of a defined area (by a mask or a saved selection) between several images from a reference image, using a script.
For example, a packshot of a purple t-shirt which acts as a reference image, is it possible to align the color of the T-Shirt with other images with the same t-shirt. The idea would be to keep the color with its nuances of brightness, so that it looks as natural as possible. So a model wearing the purple t-shirt will be correctly rendered. The goal having the complete series of images with the same t-shirt aligned with the color purple. I'm writing here to find out if this question is relevant, or if the project is more complicated than it seems because I haven't thought of specific cases that prevent automation through a script. Thank you in advance for your informed advice, and has this subject already been discussed?
Thank you in advance for your collaboration,
Sincerely,
Christian
Question about matching color between images
-
- Posts: 1
- Joined: Mon Dec 04, 2023 1:39 pm
Re: Question about matching color between images
I am also looking for something similar to this. A coworker of mine apparently worked with a guy that did more or less what you're asking for, so it should be possible. I've asked him multiple time for the contact to that guy but he cannot remember his name etc... For the past couple of days I've been searching for something like the described script.
Here's my specifications (in broad strokes) :
- Let the user choose one reference image
- Let the user choose one or more images to be colour corrected
- Based on the reference image's average RGB values, analyze and adjust each of the images to be colour corrected
- The script should utilize one or more of adjustment layers such as "Curves", "Hue/Satuation", "Brightness/Contrast", "Color Balance", "Exposure", "Levels", "Selective Color" to achieve it's goal of balancing the image to the reference image.
- The script should be written in a way that doesn't analyze pixel-by-pixel, rather it should aim at sampling an average in order to maintain a fast workflow
So far the things I've found/written/mashed together doesn't do what I want, but I am more than happy to collaborate to get something working.
Here's my specifications (in broad strokes) :
- Let the user choose one reference image
- Let the user choose one or more images to be colour corrected
- Based on the reference image's average RGB values, analyze and adjust each of the images to be colour corrected
- The script should utilize one or more of adjustment layers such as "Curves", "Hue/Satuation", "Brightness/Contrast", "Color Balance", "Exposure", "Levels", "Selective Color" to achieve it's goal of balancing the image to the reference image.
- The script should be written in a way that doesn't analyze pixel-by-pixel, rather it should aim at sampling an average in order to maintain a fast workflow
So far the things I've found/written/mashed together doesn't do what I want, but I am more than happy to collaborate to get something working.
Last edited by Scriptor on Fri Nov 08, 2024 1:56 pm, edited 1 time in total.
Re: Question about matching color between images
Nothing shiny, but it does a job - there's room for MASSIVE improvement - please help out if you find it useful:
Code: Select all
// Function to get all layer names in a document
function getLayerNames(doc) {
var layers = [];
for (var i = 0; i < doc.layers.length; i++) {
layers.push(doc.layers[i].name);
}
return layers;
}
// Function to get the documents and layers structure
function getOpenDocumentsStructure() {
var open_documents_structure = [];
// Ensure there are open documents
if (app.documents.length === 0) {
alert("No documents are open.");
return open_documents_structure;
}
// Loop through all open documents
for (var i = 0; i < app.documents.length; i++) {
var doc = app.documents[i];
var docName = doc.name;
var layerNames = getLayerNames(doc);
// Add document and layer info to the array
open_documents_structure.push({
documentName: docName,
layers: layerNames
});
}
return open_documents_structure;
}
var docLayers = getOpenDocumentsStructure();
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
// DIALOG
// ======
var dialog = new Window("dialog");
dialog.text = "Color Correcting Script";
dialog.orientation = "column";
dialog.alignChildren = ["center","top"];
dialog.spacing = 10;
dialog.margins = 16;
// GROUP1
// ======
var group1 = dialog.add("group", undefined, {name: "group1"});
group1.orientation = "row";
group1.alignChildren = ["left","center"];
group1.spacing = 10;
group1.margins = 0;
// PANEL1
// ======
var panel1 = group1.add("panel", undefined, undefined, {name: "panel1"});
panel1.text = "Source Document";
panel1.preferredSize.width = 250;
panel1.orientation = "column";
panel1.alignChildren = ["left","top"];
panel1.spacing = 10;
panel1.margins = 10;
// GROUP2
// ======
var group2 = panel1.add("group", undefined, {name: "group2"});
group2.orientation = "row";
group2.alignChildren = ["left","center"];
group2.spacing = 10;
group2.margins = 0;
// Create first dropdown with document names
var dropdown1_array = [];
for (var i = 0; i < docLayers.length; i++) {
dropdown1_array.push(docLayers[i].documentName);
}
var dropdown1 = group2.add("dropdownlist", undefined, undefined, {name: "dropdown1", items: dropdown1_array});
dropdown1.selection = 0;
// Create second dropdown with layers from selected document
var dropdown2 = group2.add('dropdownlist', undefined, []);
dropdown2.selection = -1;
// Function to update dropdown2 with layers of the selected document
function updateLayerDropdown2(selectedDocIndex) {
var layers = docLayers[selectedDocIndex].layers;
// Clear existing items
dropdown2.removeAll();
// Add layers from the selected document
for (var i = 0; i < layers.length; i++) {
dropdown2.add('item', layers[i]);
}
dropdown2.selection = 0;
}
// PANEL2
// ======
var panel2 = group1.add("panel", undefined, undefined, {name: "panel2"});
panel2.text = "Target Documents";
panel2.preferredSize.width = 250;
panel2.orientation = "column";
panel2.alignChildren = ["left","top"];
panel2.spacing = 10;
panel2.margins = 10;
// GROUP3
// ======
var group3 = panel2.add("group", undefined, {name: "group3"});
group3.orientation = "row";
group3.alignChildren = ["left","center"];
group3.spacing = 10;
group3.margins = 0;
var dropdown3_array = ["Layers"];
var dropdown3 = group3.add("dropdownlist", undefined, undefined, {name: "dropdown3", items: dropdown3_array});
dropdown3.selection = -1;
// Function to update dropdown3 with layers from other documents
function updateLayerDropdown3(selectedDocIndex) {
// Array to keep track of unique layer names
var uniqueLayers = [];
// Add layers from all documents except the selected one
for (var i = 0; i < docLayers.length; i++) {
if (i !== selectedDocIndex) { // Exclude the selected document
var layers = docLayers[i].layers;
for (var j = 0; j < layers.length; j++) {
var isDuplicate = false;
// Manually check for duplicates
for (var k = 0; k < uniqueLayers.length; k++) {
if (uniqueLayers[k] === layers[j]) {
isDuplicate = true;
break;
}
}
// Add layer if it's not a duplicate
if (!isDuplicate) {
uniqueLayers.push(layers[j]);
}
}
}
}
// Clear existing items
dropdown3.removeAll();
// Add unique layers to the dropdown
for (var l = 0; l < uniqueLayers.length; l++) {
dropdown3.add('item', uniqueLayers[l]);
}
// Set the first layer as selected if there are any layers
if (dropdown3.items.length > 0) {
dropdown3.selection = 0;
} else {
dropdown3.selection = -1; // No selection if there are no layers
}
}
// Event handler for dropdown1 to update both dropdown2 and dropdown3
dropdown1.onChange = function() {
var selectedDocIndex = dropdown1.selection.index;
// Update layers in selected document
updateLayerDropdown2(selectedDocIndex);
// Update layers from other documents
updateLayerDropdown3(selectedDocIndex);
};
// Initialize dropdowns with default selections
dropdown1.onChange();
// GROUP4
// ======
var group4 = dialog.add("group", undefined, {name: "group4"});
group4.orientation = "row";
group4.alignChildren = ["left","center"];
group4.spacing = 10;
group4.margins = 0;
group4.alignment = ["left","top"];
// Create an "Execute" button
var button1 = group4.add("button", undefined, "Execute", {name: "button1"});
// Create a "Cancel" button
var button2 = group4.add("button", undefined, "Cancel", {name: "button2"});
// Set return values for buttons
button1.onClick = function() {
dialog.close(1); // Return 1 for "Execute" button
};
button2.onClick = function() {
dialog.close(2); // Return 2 for "Cancel" button
};
// Show the dialog and capture the result
var result = dialog.show();
var targetDoc;
var targetLayer;
var colorCorrectedLayer;
// Execute functions based on which button was pressed
if (
result == 1) {
executeScript();
// Add your function or code to execute here
} else if (result == 2) {
//alert("Cancelled");
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
// Define your custom function that will be executed on button click
function executeScript() {
var otherDocumentNames = [];
// Loop through all documents and exclude the selected one
for (var i = 0; i < docLayers.length; i++) {
if (i !== dropdown1.selection.index) { // Exclude the selected document
otherDocumentNames.push(docLayers[i].documentName); // Add document name to array
}
}
// Get reference image & layer
var referenceImage = dropdown1.selection.text;
var referenceLayer = dropdown2.selection.text;
// Target images and layers (user selects these)
var targetImages = otherDocumentNames;
var targetLayers = dropdown3.selection.text;
//Make a function that gives all other document names
//propagate the selected layername from dropdown3 to all documents.
//alert(referenceImage + ", " + referenceLayer + ", " + targetImages + ", " + targetLayers);
// Apply color correction
applyColorCorrectionV1(referenceImage, referenceLayer, targetImages, targetLayers);
//Please note: The following function is unfinished and does no good job of colourcorrecting things at all.
// Function to create a new layer and apply color correction
function applyColorCorrectionV1(referenceImageName, referenceLayerName, targetImageNames, targetLayerName) {
// Loop through each target image and layer to apply the correction
//alert(targetImageNames + " " + targetLayerNames);
for (var i = 0; i < targetImageNames.length; i++) {
var targetImageName = targetImageNames[i];
// Open the target image
var targetDoc = app.documents.getByName(targetImageName);
app.activeDocument = targetDoc;
// Select the target layer
//alert(referenceLayerName);
//alert(targetLayerName);
var targetLayer = targetDoc.artLayers.getByName(targetLayerName);
targetDoc.activeLayer = targetLayer;
// Create a new empty layer called "Colour Corrected"
var colorCorrectedLayer = targetDoc.activeLayer.duplicate();
colorCorrectedLayer.name = "Colour Corrected";
targetDoc.activeLayer = colorCorrectedLayer;
// Set blend mode of the new layer to 'Normal'
colorCorrectedLayer.blendMode = BlendMode.NORMAL;
// =======================================================
var idmatchColor = stringIDToTypeID("matchColor");
var desc3 = new ActionDescriptor();
var idLght = charIDToTypeID("Lght");
desc3.putInteger(idLght, 100);
var idClrR = charIDToTypeID("ClrR");
desc3.putInteger(idClrR, 100);
var idFade = charIDToTypeID("Fade");
desc3.putInteger(idFade, 0);
var idneutralizeColor = stringIDToTypeID("neutralizeColor");
desc3.putBoolean(idneutralizeColor, false);
var idfsel = charIDToTypeID("fsel");
desc3.putBoolean(idfsel, true);
var idSrce = charIDToTypeID("Srce");
// Reference the source (reference image and layer)
var ref1 = new ActionReference();
var idLyr = charIDToTypeID("Lyr ");
ref1.putName(idLyr, referenceLayerName); // Reference layer
var idDcmn = charIDToTypeID("Dcmn");
ref1.putName(idDcmn, referenceImageName); // Reference image
desc3.putReference(idSrce, ref1);
// Execute match color adjustment
executeAction(idmatchColor, desc3, DialogModes.NO);
}
}
}