PlateMontage

Creates a labeled plate montage image from BD Pathway 855 / AttoVision 1.6 data sets

/* PlateMontage macro */ var version  = '1.4a, 2009-10-13';
//
// Creates a labeled plate montage image from BD Pathway 855 / AttoVision 1.6 data sets
//
// Features:
//   Borders do not hide part of the images as in AttoVision
//   Simultaneous processing of up to three channels
//   Automatically saves the montage
//   Style is customizable
//   Well images can be inverted
//   Missing images are not a problem
//   All settings are remembered
//
// Shortcuts:
//   Shift-P: Run this macro
//   Shift-C: Close all windows
//   Shift-M: Reload this macro
//
// System requirements:
//   The screen height must be at least 1024 pixels
//
// BD Pathway 855 / AttoVision data set directory structure:
//   + 2009-09-20 exp 328
//     + 2009-09-21_000
//       + Well A001
//           Alexa 488 - n000000.tif
//           Alexa 594 - n000000.tif
//           Hoechst - n000000.tif
//
// Contact: njensen@mail.unc.edu
// This code has been released into the Public Domain (PD)
 
 
// global constants
var settingsFile   = 'PlateMontageSettings.txt';
var borderWidth    = 3;
var labelHeight    = 80;
var labelWidth     = 60;
var labelFont      = 'SansSerif';
var labelSize      = 60;
var labelStyle     = 'antialiased';
var labelColor     = 0;
var scaledWidth    = 200;
var montageColor   = 255;
var beepDone       = true;
var tab = '\t';
var macroDir = getDirectory('macros');
var settingsFileFull = macroDir + settingsFile;
 
// global variables
var inputPath;
var outputPath;
var folderFormat;
var imageUse1;
var image1;
var imageUse2;
var image2;
var imageUse3;
var image3;
var rowFromChar;
var colFrom;
var rowToChar;
var colTo;
var outputPath;
var invertImages;
var invertMontage;
var invertBorders;
var popupDone;
var i;
var j;
var parentDir;
var parentParentDir;
var rowFrom;
var	rowTo;
var reportMontage;
 
 
//
// PlateMontage: main program, gets and saves parameters
//
 
function PlateMontage() {
 
// default settings
	inputPath      = 'C:\\AttoVision\\Data\\';
	outputPath     = '..\\..\\';
	folderFormat   = '384';
	imageUse1      = true;
	image1         = 'Alexa 594 - n000000.tif';
	imageUse2      = true;
	image2         = 'Hoechst - n000000.tif';
	imageUse3      = false;
	image3         = '';
	rowFromChar    = 'A';
	colFrom        = 1;
	rowToChar      = 'P';
	colTo          = 24;
	invertImages   = false;
	invertMontage  = false;
	invertBorders  = false;
	popupDone      = true;
 
	rowFrom = charCodeAt(toUpperCase(rowFromChar), 0) - 64;
	rowTo = charCodeAt(toUpperCase(rowToChar), 0) - 64;
 
// read settings
	if (File.exists(settingsFileFull) == 1) {
		var settings = File.openAsString(settingsFileFull);
		var settingLines = split(settings, '\r\n');
		for (i = 0; i < lengthOf(settingLines); i ++) {
	  	setting = split(settingLines[i], tab);
			if (lengthOf(setting) == 1) {
				var name = setting[0];
				setting = newArray(2);
				setting[0] = name;
				setting[1] = '';
			}
			if (setting[0] == 'inputPath') {
				inputPath = setting[1];
			}
			else if (setting[0] == 'outputPath') {
				outputPath = setting[1];
			}
			else if (setting[0] == 'folderFormat') {
				folderFormat = setting[1];
			}
			else if (setting[0] == 'imageUse1') {
				imageUse1 = parseInt(setting[1]);
			}
			else if (setting[0] == 'image1') {
				image1 = setting[1];
			}
			else if (setting[0] == 'imageUse2') {
				imageUse2 = parseInt(setting[1]);
			}
			else if (setting[0] == 'image2') {
				image2 = setting[1];
			}
			else if (setting[0] == 'imageUse3') {
				imageUse3 = parseInt(setting[1]);
			}
			else if (setting[0] == 'image3') {
				image3 = setting[1];
			}
			else if (setting[0] == 'rowFromChar') {
				rowFromChar = setting[1];
			}
			else if (setting[0] == 'colFrom') {
				colFrom = parseInt(setting[1]);
			}
			else if (setting[0] == 'rowToChar') {
				rowToChar = setting[1];
			}
			else if (setting[0] == 'colTo') {
				colTo = parseInt(setting[1]);
			}
			else if (setting[0] == 'scaledWidth') {
				scaledWidth = parseInt(setting[1]);
			}
			else if (setting[0] == 'invertImages') {
				invertImages = parseInt(setting[1]);
			}
			else if (setting[0] == 'invertMontage') {
				invertMontage = parseInt(setting[1]);
			}
			else if (setting[0] == 'invertBorders') {
				invertBorders = parseInt(setting[1]);
			}
			else if (setting[0] == 'popupDone') {
				popupDone = parseInt(setting[1]);
			}
		}
	}
	rowFromChar = toUpperCase(rowFromChar);
	rowToChar = toUpperCase(rowToChar);
	rowFrom = charCodeAt(rowFromChar, 0) - 64;
	rowTo = charCodeAt(rowToChar, 0) - 64;
 
// display dialog
	requires('1.41j');
	Dialog.create('Plate Montage Settings');
	Dialog.addMessage('== PlateMontage Macro (Version ' + version + ') ==\n \nCreates a labeled plate montage from BD Pathway plate images\n \nShift-P: Run this macro\nShift-C: Close all windows\nShift-M: Reload this macro');
	Dialog.addMessage('Parent directory of images (DATE-NUMBER; leave empty for popup):');
	Dialog.addString('', inputPath, 90);
	Dialog.addMessage('Output directory (relative paths: ".\\" (same directory), "..\\" (one up); leave empty for popup):');
	Dialog.addString('', outputPath, 90);
	Dialog.addMessage('Image folder format (24: "A1"; 96: "A01", 384: "A001"):');
	Dialog.addChoice('', newArray('24', '96', '384'), folderFormat)
	Dialog.addCheckbox('Process filename 1 (leave empty for popup):', imageUse1);
	Dialog.addString('', image1, 30);
	Dialog.addCheckbox('Process filename 2 (leave empty for popup):', imageUse2);
	Dialog.addString('', image2, 30);
	Dialog.addCheckbox('Process filename 3 (leave empty for popup):', imageUse3);
	Dialog.addString('', image3, 30);
	Dialog.addMessage('Coordinates of block of wells (non-existing wells will be left empty):');
	Dialog.addString('From row:', rowFromChar, 1);
	Dialog.addNumber('From column:', colFrom);
	Dialog.addString('To row:', rowToChar, 1);
	Dialog.addNumber('To column:', colTo);
	Dialog.addMessage('Scaled image width in pixels:');
	Dialog.addNumber('', scaledWidth);
	Dialog.addCheckbox('Invert images', invertImages);
	Dialog.addCheckbox('Black canvas with white label text', invertMontage);
	Dialog.addCheckbox('Black borders and plate background', invertBorders);
	Dialog.addMessage('');
	Dialog.addCheckbox('Display message when done', popupDone);
	Dialog.addMessage('');
	Dialog.show();
 
// get settings from dialog
	inputPath     = Dialog.getString();
	outputPath    = Dialog.getString();
	folderFormat  = Dialog.getChoice();
	imageUse1     = Dialog.getCheckbox();
	image1        = Dialog.getString();
	imageUse2     = Dialog.getCheckbox();
	image2        = Dialog.getString();
	imageUse3     = Dialog.getCheckbox();
	image3        = Dialog.getString();
	rowFromChar   = Dialog.getString();
	colFrom       = Dialog.getNumber();
	rowToChar     = Dialog.getString();
	colTo         = Dialog.getNumber();
	scaledWidth   = Dialog.getNumber();
	invertImages  = Dialog.getCheckbox();
	invertMontage = Dialog.getCheckbox();
	invertBorders = Dialog.getCheckbox();
	popupDone     = Dialog.getCheckbox();
 
	rowFromChar = toUpperCase(rowFromChar);
	rowToChar = toUpperCase(rowToChar);
	rowFrom = charCodeAt(rowFromChar, 0) - 64;
	rowTo = charCodeAt(rowToChar, 0) - 64;
 
	var processingImages = '';
 
// ask for image 1
	if (imageUse2 == true) {
		if (image2 == '') {
			Popup('Please select an image 1', 'Image 1');
			open();
			image1 = File.getName(File.name);
 
// get input path from image path
			if (inputPath == '') {
				inputPath = replace(File.directory, '\\.*?\\.*?$', '\\');
			}
			close();
		}
		processingImages = processingImages + image1 + '\n';
	}
 
// ask for image 2
	if (imageUse2 == true) {
		if (image2 == '') {
			Popup('Please select an image 2', 'Image 2');
			open();
			image2 = File.getName(File.name);
 
// get input path from image path
			if (inputPath == '') {
				inputPath = replace(File.directory, '\\.*?\\.*?$', '\\');
			}
			close();
		}
		processingImages = processingImages + image2 + '\n';
	}
 
// ask for image 3
	if (imageUse3 == true) {
		if (image3 == '') {
			Popup('Please select an image 3', 'Image 3');
			open();
			image3 = File.getName(File.name);
 
// get input path from image path
			if (inputPath == '') {
				inputPath = replace(File.directory, '\\.*?\\.*?$', '\\');
			}
			close();
		}
		processingImages = processingImages + image3 + '\n';
	}
 
// select input path
	if (inputPath == '') {
		inputPath = getDirectory('Parent directory (DATE-NUMBER)');
	}
 
// add trailing \
	inputPath = replace(inputPath, '([^\\\\])$', '$1\\\\');
 
// select output path
	if (outputPath == '') {
		outputPath = getDirectory('Output directory');
	}
 
// add trailing \
	outputPath = replace(outputPath, '([^\\\\])$', '$1\\\\');
 
// save settings to file
	var file = File.open(settingsFileFull) ;
	print(file, 'inputPath'     + tab + inputPath);
	print(file, 'outputPath'    + tab + outputPath);
	print(file, 'folderFormat'  + tab + folderFormat);
	print(file, 'imageUse1'     + tab + imageUse1);
	print(file, 'image1'        + tab + image1);
	print(file, 'imageUse2'     + tab + imageUse2);
	print(file, 'image2'        + tab + image2);
	print(file, 'imageUse3'     + tab + imageUse3);
	print(file, 'image3'        + tab + image3);
	print(file, 'rowFromChar'   + tab + rowFromChar);
	print(file, 'colFrom'       + tab + colFrom);
	print(file, 'rowToChar'     + tab + rowToChar);
	print(file, 'colTo'         + tab + colTo);
	print(file, 'scaledWidth'   + tab + scaledWidth);
	print(file, 'invertImages'  + tab + invertImages);
	print(file, 'invertMontage' + tab + invertMontage);
	print(file, 'invertBorders' + tab + invertBorders);
	print(file, 'popupDone'     + tab + popupDone);
	File.close(file);
 
// convert relative o absolute output path
	if (matches(outputPath, '^(\\.|\\.\\.)\\\\.*?$') == true) {
		outputPath = inputPath + outputPath;
		var outputPathOld;
		do {
			outputPathOld = outputPath;
			outputPath = replace(outputPath, '\\\\\\.\\\\', '\\\\');
			outputPath = replace(outputPath, '\\\\[^\\\\]+\\\\\\.\\.\\\\', '\\\\');
		} while (outputPath != outputPathOld);
	}
 
// check values
	if (rowFrom < 1) {
		exit('Start row ' + rowFromChar + ' is not between A and P');
	}
	if (rowTo > 16) {
		exit('End row ' + rowToChar + ' is not between A and P');
	}
	if (rowFrom > rowTo) {
		exit('End row ' + rowToChar + ' is smaller than start row ' + rowFromChar);
	}
 
	if (colFrom < 1) {
		exit('Start column ' + colFrom + ' is not between 1 and 24');
	}
	if (colTo > 24) {
		exit('End col ' + colTo + ' is not between 1 and 24');
	}
	if (colFrom > colTo) {
		exit('End column ' + colTo + ' is smaller than start column ' + colFrom);
	}
 
// check paths
	if (File.isDirectory(inputPath) == false) {
		exit('Parent directory "' + inputPath + '" does not exist');
	}
	if (File.isDirectory(outputPath) == false) {
		exit('Output directory "' + outputPath + '" does not exist');
	}
 
// process images
	CloseImages();
 
// get name of parent directories
	parentDir = replace(inputPath, '^.*?\\\\([^\\\\]*?)\\\\$', '$1');
	parentParentDir = replace(inputPath, '^.*?\\\\([^\\\\]*?)\\\\[^\\\\]*?\\\\$', '$1');
	reportMontage = '';
 
// print to log
	print('Processing...\nDirectory: ' + parentParentDir + '\\' + parentDir + '\nPath: ' + inputPath + '\nImages:\n' + processingImages);
	ProcessMontage(image1, imageUse1);
	ProcessMontage(image2, imageUse2);
	ProcessMontage(image3, imageUse3);
 
// print to log
	print('Output path: ' + outputPath + '\nMontage files: ' + reportMontage + '\nDone!\n--------\n');
 
// beep
	if (beepDone == true) {
		beep();
	}
 
// popup
	if (popupDone == true) {
		Popup('Done!\n \nSaved to: \n' + outputPath + '\n \nMontage files:' + reportMontage, 'Done');
	}
 
	return;
}
 
 
//
// ProcessMontage: create and save a montage
//
 
function ProcessMontage(image, imageUse) {
 
	if (imageUse == true) {
 
// create montage file name
		var montageImage = parentParentDir + ', ' + parentDir + ', ' + replace(image, '\\.\\w+$', '') + '.png';
		CreateMontage(image, montageImage);
		if (isOpen(montageImage)) {
			reportMontage = reportMontage + '\n' + montageImage;
			selectWindow(montageImage);
			saveAs('PNG', outputPath + montageImage);
		}
		if (isOpen('Log')) {
			selectWindow('Log');
		}
	}
	return;
}
 
 
//
// CreateMontage: create a montage
//
 
function CreateMontage(image, montageImage) {
 
	if (isOpen('Log')) {
		selectWindow('Log');
	}
	setBatchMode(true);
 
	// image folder name format
	var folderDigits = '000';
	if (folderFormat == '24') {
		folderDigits = '0';
	}
	if (folderFormat == '96') {
		folderDigits = '00';
	}
	else if (folderFormat == '384') {
		folderDigits = '000';
	}
 
	var scalingFactor;
	var dimensionsCreated = false;
	var montageCreated = false;
 
	var numRows = rowTo - rowFrom + 1;
	var numCols = colTo - colFrom + 1;
 
// cycle through images
	for (row = 0; row < numRows; row ++) {
		var rowNumber = row + rowFrom;
		var rowChar = fromCharCode(rowNumber + 64);
 
		for (col = 0; col < numCols; col ++) {
		var colNumber = col + colFrom;
		var colStr = substring(folderDigits, lengthOf(toString(colNumber))) + colNumber;
 
// open image
			var imageFileFull = inputPath + 'Well ' + rowChar + colStr + '\\' + image;
			if (File.exists(imageFileFull) == true) {
				open(imageFileFull);
				rename('Image');
 
// get dimensions from first image
				if (dimensionsCreated == false) {
					dimensionsCreated = true;
					var width = getWidth();
					var height = getHeight();
					scalingFactor = scaledWidth / width;
					scaledHeight = floor(height * scalingFactor);
				}
 
// create montage with dimensions
				if (montageCreated == false) {
					montageCreated = true;
					montageWidth = labelWidth * 2 + scaledWidth * numCols + borderWidth * (numCols + 1);
					montageHeight = labelHeight + scaledHeight * numRows + borderWidth * (numRows + 1);
					newImage(montageImage, '16-bit Black' + montageColor, montageWidth, montageHeight, 1);
					setForegroundColor(255, 255, 255);
					run('Select All');
					run('Fill');
 
// label montage
					setFont(labelFont, labelSize, labelStyle);
					setColor(labelColor);
					setJustification('center');
 
// label columns
					var x = scaledWidth / 2 + labelWidth * 2 + borderWidth;
					var y = labelHeight - 10;
					for (colNumberLabel = colFrom; colNumberLabel <= colTo; colNumberLabel ++) {
						drawString(colNumberLabel, x, y);
						x += scaledWidth + borderWidth;
					}
 
// label rows
					var x1 = labelWidth / 2;
					var x2 = labelWidth / 2 + labelWidth;
					var y = labelHeight + borderWidth + scaledHeight / 2 + 30;
					for (rowNumberLabel = rowFrom; rowNumberLabel <= rowTo; rowNumberLabel ++) {
						var rowCharLabel = fromCharCode(rowNumberLabel + 64);
						drawString(rowNumberLabel, x1, y);
						drawString(rowCharLabel, x2, y);
						y += scaledHeight + borderWidth;
					}
 
//invert
					if ( (invertMontage == true) || (invertBorders == true) ) {
						makeRectangle(labelWidth * 2, labelHeight, montageWidth - labelWidth * 2, montageHeight - labelHeight);
 
// invert border and image background
						if (invertBorders == true) {
							run('Invert');
						}
 
// invert montage background and labels
						if (invertMontage == true) {
							run('Make Inverse');
							run('Invert');
						}
 
						run('Select None');
					}
				}
 
// scale image
				selectWindow('Image');
				run('Scale...', 'x=- y=- width=' + scaledWidth + ' height=' + scaledHeight + ' interpolate title=[Image]');
				run('Canvas Size...', 'width=' + scaledWidth + ' height=' + scaledHeight + ' position=Center');
				run('Multiply...', 'value=16');
 
// invert
				if (invertImages == true) {
					run('XOR...', 'value=1111111111111111');
				}
 
// copy scaled image into montage
				selectWindow('Image');
				run('Select All');
				run('Copy');
				selectWindow(montageImage);
				makeRectangle(labelWidth * 2 + scaledWidth * col + borderWidth * (col + 1), labelHeight + scaledHeight * row + borderWidth * (row + 1), scaledWidth, scaledHeight);
				run('Paste');
				run('Select None');
				selectWindow('Image');
				close();
			}
		}
  }
 
	selectWindow(montageImage);
	setBatchMode(false);
 
	return;
}
 
 
//
// Popup: popup a message
//
 
function Popup(text, title) {
	text = '' + text;
	title = '' + title;
	Dialog.create(title);
	Dialog.addMessage(text)
	Dialog.show();
	return;
}
 
 
//
// CloseAllImages
//
 
function CloseImages() {
 
	for (i = nImages(); i > 0; i -- ) {
		selectImage(i);
		close();
	}
	return;
}
 
 
//
// CloseAllImages
//
 
function CloseAllImages() {
 
	CloseImages();
 
// close message window
	if (isOpen('Log')) {
		selectWindow('Log');
    run('Close');
	}
 
	return;
}
 
 
// install macro: plate montage
macro 'Plate Montage [P]' {
	PlateMontage();
}
 
// install macro: reload the macro
macro 'Reload Plate Montage macro [M]' {
	run('Install...', 'install=[C:\\Program Files\\ImageJ\\macros\\PlateMontage.ijm]');
}
 
// install macro: close all images
macro 'Close all images [C]' {
	CloseAllImages();
}
macro/platemontage.txt · Last modified: 2010/01/26 11:01 (external edit)
Back to top
CC Attribution-Noncommercial-Share Alike 3.0 Unported
chimeric.de = chi`s home Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0