Skip to content

Instantly share code, notes, and snippets.

@abalter
Created August 24, 2017 20:19
Show Gist options
  • Save abalter/c19a3e94b8591c559b75bbbc1470dd1c to your computer and use it in GitHub Desktop.
Save abalter/c19a3e94b8591c559b75bbbc1470dd1c to your computer and use it in GitHub Desktop.
Script for FIJI/ImageJ used for this paper: https://www.ncbi.nlm.nih.gov/pubmed/28380359
/*
Code used for processing images for the paper
"Quantitative Multiplex Immunohistochemistry Reveals Myeloid-Inflamed Tumor-Immune Complexity Associated with Poor Prognosis."
Cell Rep. 2017 Apr 4;19(1):203-217. doi: 10.1016/j.celrep.2017.03.037.
https://www.ncbi.nlm.nih.gov/pubmed/28380359
*/
var debug = true;
var SINGLE_IMAGE_CONFIGURATION_FILENAME = "single_image_configuration.csv";
var IMAGE_GRID_CONFIGURATION_FILENAME = "image_grid_configuration.csv";
// The stack has 3 images in successively smaller size.
// The first is the biggest.
var IMAGE_SERIES_INDEX = 1;
var SINGLES_OUTPUT_DIRECTORY_NAME = "cropped_singles_tiffs";
var IMAGE_GRID_OUTPUT_DIRECTORY_NAME = "cropped_image_grid_tiffs";
var NUM_GRID_IMAGE_GROUP_ROWS = 9;
var SINGLE_IMAGE_FIRST_DATA_COLUMN = 10; // this is the COLUMN number. The array index is one less.
// Import java packages for dialoges and Bio Formats
if (debug)
{
print("importing packages");
}
importClass(Packages.loci.plugins.BF);
importClass(Packages['loci.plugins.in.ImporterOptions']); // 'in' is a reserved word, hence the different syntax
importClass(Packages.loci.common.Region);
importPackage(Packages.ij.io);
importClass(Packages.ij.IJ);
importClass(Packages.ij.ImagePlus);
importClass(Packages.ij.plugin.RGBStackMerge);
importPackage(Packages.java.io);
// set processed timestamp in UTC time
var TIMESTAMP = (new Date()).toUTCString();
/*******************************************************************/
/********************* MAIN SECTION ********************************/
var currentFolder = selectFolder();
createOutputDirectories(currentFolder);
print("getting crop data");
var cropDataForSingles = getCropDataForSingles(currentFolder);
processSingles(cropDataForSingles);
//writeSinglesMetadata();
var cropDataForImageGrid = getCropDataForImageGrid(currentFolder);
processImageGrid(cropDataForImageGrid);
//writeGridMetadata();
/*******************************************************************/
/**************************************************************************/
/***************** IMAGE CROP CONFIGURATION CLASS ************************/
function imageCropConfiguration
(
inputImageName,
gridX,
gridY,
X,
Y,
deltaX,
deltaY,
timestampUTC
)
{
this.inputImageName = inputImageName;
this.gridX = gridX,
this.gridY = gridY,
this.X = X;
this.Y = Y;
this.deltaX = deltaX;
this.deltaY = deltaY;
this.timestampUTC = timestampUTC;
var gridXpart = gridX ? "gridX" + gridX : "";
var gridYpart = gridY ? "gridY" + gridY : "";
print("gridXpart: " + gridXpart + " gridYpart: " + gridYpart);
// Image name in format, for example: some_image_cropped_X2Y3_21234_11493_5000x5000.tiff
var imageNamePart = (this.inputImageName.split("."))[0];
print("input image name type: " + (typeof this.inputImageName) + " image name part type: " + (typeof imageNamePart));
print("input image name: " + this.inputImageName + " image name part: " + imageNamePart);
this.outputImageName =
imageNamePart
+ gridXpart
+ gridYpart
+ "_"
+ "X"
+ X
+ "_"
+ "Y"
+ Y
+ "_"
+ deltaX
+ "x"
+ deltaY
+ ".tiff";
}
/**************************************************************************/
/**************************************************************************/
/***************************** SELECT FOLDER *****************************/
function selectFolder()
{
var directoryObject = DirectoryChooser("Choose a folder");
var folder = directoryObject.getDirectory();
return folder;
}
/**************************************************************************/
/*******************************************************************************/
/************************** CREATE OUTPUT DIRECTORIES **************************/
function createOutputDirectories()
{
if (debug)
{
print("making directory " + currentFolder + SINGLES_OUTPUT_DIRECTORY_NAME);
}
File(currentFolder + SINGLES_OUTPUT_DIRECTORY_NAME).mkdir();
if (debug)
{
print("making directory " + currentFolder + IMAGE_GRID_OUTPUT_DIRECTORY_NAME);
}
File(currentFolder + IMAGE_GRID_OUTPUT_DIRECTORY_NAME).mkdir();
}
/*******************************************************************************/
/*******************************************************************************/
/************************** GET CROP DATA FOR SINGLES **************************/
function getCropDataForSingles(folder)
{
if (debug)
{
print("getting singles crop data in folder " + folder);
}
print("full path: " + folder + SINGLE_IMAGE_CONFIGURATION_FILENAME);
var singlesConfigurationRawData = IJ.openAsString(folder + SINGLE_IMAGE_CONFIGURATION_FILENAME);
var arrayOfData = singlesConfigurationRawData.split("\n");
var singlesImageConfigurations = [];
for (var index in arrayOfData)
{
var dataLine = arrayOfData[index].split(",");
print("dataline: " + dataLine)
var imageName = new String(dataLine[0]);
print("imageName: " + imageName);
print("typeof imageName: " + (typeof imageName));
var X = dataLine[SINGLE_IMAGE_FIRST_DATA_COLUMN - 1]; // first data column. array index = column number - 1
var Y = dataLine[SINGLE_IMAGE_FIRST_DATA_COLUMN - 1 + 1]; // next data column
var deltaX = dataLine[SINGLE_IMAGE_FIRST_DATA_COLUMN - 1 + 2]; // next data column
var deltaY = dataLine[SINGLE_IMAGE_FIRST_DATA_COLUMN - 1 + 3]; // next data column
print("X: " + X + " Y: " + Y + " deltaX: " + deltaX + " deltaY: " + deltaY);
var cropConfiguration = new imageCropConfiguration
(
inputImageName = imageName,
gridX = null,
gridY = null,
X = X,
Y = Y,
deltaX = deltaX,
deltaY = deltaY,
timestampUTC = TIMESTAMP
);
singlesImageConfigurations[index] = cropConfiguration;
}
return singlesImageConfigurations;
}
/*******************************************************************************/
/*******************************************************************************/
/*********************** GET CROP DATA FOR IMAGE GRID **************************/
function getCropDataForImageGrid(folder)
{
if (debug)
{
print("getting image grid crop data in folder " + folder);
}
print("image grid configuration file name: " + (folder + IMAGE_GRID_CONFIGURATION_FILENAME));
var gridConfigurationRawData = IJ.openAsString(folder + IMAGE_GRID_CONFIGURATION_FILENAME);
var arrayOfData = gridConfigurationRawData.split("\n");
var totalRows = arrayOfData.length;
// Some configuration parameters in first 4 lines of config file
dataLine = arrayOfData[0].split(",");
var deltaX = parseInt(dataLine[1]);
//print("deltaX: " + deltaX);
//print("arrayOfData[1]: " + arrayOfData[1]);
dataLine = arrayOfData[1].split(",");
var deltaY = parseInt(dataLine[1]);
dataLine = arrayOfData[2].split(",");
var imageRows = parseInt(dataLine[1]);
dataLine = arrayOfData[3].split(",");
var imageCols = parseInt(dataLine[1]);
print("deltaX: " + deltaX + " deltaY: " + deltaY + " imageRows:" + imageRows + " imageCols: " + imageCols);
print("arrayOfData.length: " + arrayOfData.length);
var numImages = (arrayOfData.length + 1 - 4)/NUM_GRID_IMAGE_GROUP_ROWS;
// Each group is 9 rows, so make sure the collection of
// image data is evenly divisible by 9. Adding 1 is
// because the last group doesn't have a blank line after it
var check = (arrayOfData.length + 1 - 4) % NUM_GRID_IMAGE_GROUP_ROWS;
if (check !== 0)
{
print("problem with number of rows");
return false;
}
print("numImages: " + numImages);
// List of image configuration objects
var gridImageConfigurations = [];
print("starting main loop");
// Loop through image sections
for (var imageNumber = 1 ; imageNumber < numImages + 1 ; imageNumber++)
{
// This is the actual starting row of the image group
// where the image name is
// actual row in csv file
imageNameRow = (imageNumber-1)*NUM_GRID_IMAGE_GROUP_ROWS // skip number of rows in image group
+ 4 // number of rows at top of sheet for metadata
+ 1; // row index starts at 1 not 0
print("image number: " + imageNumber + " imageNameRow: " + imageNameRow);
dataLine = arrayOfData[imageNameRow-1].split(","); //array index = actual row number - 1
var possibleImageName = new String(dataLine[0]);
print("possibleImageName: " + possibleImageName);
print("typeof possibleImageName: " + (typeof possibleImageName));
var imageNameParts = possibleImageName.split(".");
print("imageNameParts.length: " + imageNameParts.length);
var extension = imageNameParts[imageNameParts.length - 1];
print("extension: " + extension);
var imageName = imageNameParts[0];
print("imageName: " + imageName);
// gridY is the y-coordinate (vertical) of the image grid
for (var gridY = 1 ; gridY < imageRows + 1 ; gridY++)
{
// actual row of config file for given image in given image group
var row = imageNameRow
+ 1 // row after image name that labels x and y columns
+ gridY; // position in image grid coupr for this image
print("current row: " + row + " gridY: " + gridY);
print("imageRows: " + imageRows + " gridY: " + gridY + " gridY < imageRows + 1: " + (gridY < imageRows + 1));
dataLine = arrayOfData[row-1].split(","); // array index = actual row number - 1
// gridX is x-cordinate (horizontal) of image grid
for (var gridX = 1 ; gridX < imageCols + 1 ; gridX++)
{
// x,y pairs in adjecent columns under each gridX,
// start at column 2 (array index 1) and skip 2 for each next x value
col = 2 // column 2
+ 2*(gridX - 1); // skip 0 for first and then skip 2
print("col: " + col + " gridX: " + gridX);
X = dataLine[col - 1]; // array index = column index - 1
Y = dataLine[col];
print("X: " + X + " Y: " + Y);
var cropConfiguration = new imageCropConfiguration
(
imageName + ".svs",
gridX,
gridY,
X,
Y,
deltaX,
deltaY,
TIMESTAMP
);
print("outputImageName: " + cropConfiguration.outputImageName);
//print("imageNumber: " + imageNumber);
//for (key in cropConfiguration){print(key + ": " + cropConfiguration[key]);}
//print("X: " + cropConfiguration['X'] + " Y: " + cropConfiguration['Y']);
//print("inputImageName: " + cropConfiguration['inputImageName']);
//print("deltaX: " + cropConfiguration['deltaX'] + " deltaY: " + cropConfiguration['deltaY']);
// load configuration object into list
gridImageConfigurations.push(cropConfiguration);
print("gridImageConfigurations.length: " + gridImageConfigurations.length);
}
}
}
print("***************************************************************");
print ("******** grid images *************");
for (index in gridImageConfigurations)
{
print("/n/n--------- image " + index + "-----------");
configuration = gridImageConfigurations[index];
for (key in configuration)
{
print(key + ": " + configuration[key]);
}
}
print("********* done with grid images **********");
return gridImageConfigurations;
}
/*******************************************************************************/
/*******************************************************************************/
/****************************** PROCESS SINGLES ********************************/
function processSingles(cropData)
{
if (debug)
{
print("***************processing singles*********************");
}
for (objectIndex in cropData)
{
print("\n\n\n-----------------------------");
print("image " + objectIndex);
var data = cropData[objectIndex];
for (key in data){print(key + ": " + data[key]);}
var image = openImageSection
(
data.inputImageName,
data.X,
data.Y,
data.deltaX,
data.deltaY
)
print("image class: " + image.getClass());
//RGBStackMerge.mergeStacks(image, false);
var outputFileName = currentFolder + SINGLES_OUTPUT_DIRECTORY_NAME + File.separator + data.outputImageName;
if (debug)
{
print("output file name: " + outputFileName);
}
IJ.saveAs(image, "tif", outputFileName);
//IJ.saveAsTiff(image, outputFileName);
//image.close();
}
if (debug)
{
print("*************** done processing singles*********************");
}
return "singles";
}
/*******************************************************************************/
/*******************************************************************************/
/**************************** PROCESS IMAGE GRID *******************************/
function processImageGrid(cropData)
{
if (debug)
{
print("processing image grid");
}
for (objectIndex in cropData)
{
print("\n\n\n-----------------------------");
print("image " + objectIndex);
var data = cropData[objectIndex];
for (key in data){print(key + ": " + data[key]);}
var cropedImage = openImageSection
(
data.inputImageName,
data.X,
data.Y,
data.deltaX,
data.deltaY
)
var outputFileName = currentFolder + IMAGE_GRID_OUTPUT_DIRECTORY_NAME + File.separator + data.outputImageName;
if (debug)
{
print("output file name: " + outputFileName);
}
IJ.saveAs(cropedImage, "tif", outputFileName);
image.close();
}
if (debug)
{
print("*************** done processing grid *********************");
}
return "image grid";
}
/*******************************************************************************/
/*******************************************************************************/
/**************************** OPEN SIMAGE SECTION *******************************/
function openImageSection
(
imageName,
X,
Y,
deltaX,
deltaY
)
{
if (debug)
{
print("opening file " + currentFolder + "images" + File.separator + imageName);
print("cropping at: " + X + ", " + Y + ", " + deltaX + ", " + deltaY);
}
var path = currentFolder + "images" + File.separator + imageName;
var options = new ImporterOptions();
options.setId(path);
options.setAutoscale(true);
options.setCrop(true);
options.setCropRegion(0, new Region(X, Y, deltaX, deltaY));
options.setColorMode(ImporterOptions.COLOR_MODE_COMPOSITE);
var croppedImage = new ImagePlus();
croppedImage = BF.openImagePlus(options);
print("cropped image class: " + croppedImage[0].getClass());
finalImage = new ImagePlus();
finalImage = croppedImage[0].flatten();
//finalImage = RGBStackMerge.mergeChannels(croppedImage[0], false);
print ("final image class: " + finalImage.getClass() + " length: " + finalImage.length);
//finalImage.show();
return finalImage;
}
/*******************************************************************************/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment