Question about matching color between images

Discussion of Automation, Image Workflow and Raw Image Workflow

Moderators: Tom, Kukurykus

ChristianK
Posts: 1
Joined: Mon Dec 04, 2023 1:39 pm

Question about matching color between images

Post by ChristianK »

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

Professional AI Audio Generation within Adobe Premiere Pro - Download Free Plugin here

User avatar
Scriptor
Posts: 26
Joined: Tue Oct 01, 2019 12:07 pm

Re: Question about matching color between images

Post by Scriptor »

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 askeing 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.
User avatar
Scriptor
Posts: 26
Joined: Tue Oct 01, 2019 12:07 pm

Re: Question about matching color between images

Post by Scriptor »

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);
		}
	}
}