Created
August 24, 2017 20:19
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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