Skip to content

Instantly share code, notes, and snippets.

@omsai
Last active December 15, 2015 01:19
Show Gist options
  • Save omsai/5179296 to your computer and use it in GitHub Desktop.
Save omsai/5179296 to your computer and use it in GitHub Desktop.
Split Stack ImageJ macro to fix MetaMorph dual camera image files, by splitting them into their separate channel files
/**
* Split Stack macro
*
* Fixes MetaMorph dual camera image files, by splitting them into
* their separate channel files.
*
* Usage: In ImageJ, use Plugins > Macros > Run...
*
* Description:
*
* MetaMorph 7 saves multi-camera images into a Left/Right merged
* image. This complicates analysis, and in fact MetaMorph's own 4D
* reader (accessed from File > Open 4D) does not reconstruct
* multi-camera data properly. Therefore it's necessary to split the
* data to make it appear as if it were obtained using a single
* camera.
*
* The split data and new "nd" file copy are saved into a "split"
* sub-folder. The original stacks are preserved for MetaMorph's
* original TIF header metadata, since ImageJ destroys that metadata
* in the split copy.
*
* Author: Rebecca Williams
*
* Contributor: Pariksheet Nanda (file renaming, documentation)
*
* Keywords: MetaMorph, dual camera, streaming
*
* Version: 2013-05-20a
*/
/* Settings */
indir="C:\\AALL USER IMAGES HERE\\SplitStack\\Files\\";
outdir = indir + "split" + File.separator;
channel_to_split = "cUltra 488-561";
replacement_channel_left = "GREEN";
replacement_channel_right = "RED";
no_nd_wavelengths = 0; // at least 1 wavelength in .nd file
// Create new .nd files. Allows multiple experiments to be processed.
nd_files = match_files(indir, ".*\\.nd$");
if (nd_files.length == 0)
exit("No .nd files found in directory:\n" + indir);
if (! File.exists(outdir))
File.makeDirectory(outdir);
for (i = 0; i < nd_files.length; i++)
nd_wavelengths = save_new_nd_file(toString(indir + nd_files[i]),
toString(outdir + nd_files[i]),
channel_to_split,
replacement_channel_left,
replacement_channel_right);
if (nd_wavelengths == 0)
no_nd_wavelengths = 1;
// Main processing loop to split image files.
if (no_nd_wavelengths)
list = match_files(indir, ".*\\.TIF$");
else
list = match_files(indir, ".*_w\\d" + channel_to_split + ".*\\.TIF$");
setOption("display labels", true);
setBatchMode(true);
for (l = 0; l < list.length; l++) {
dual_file = list[l];
showProgress(l, list.length);
IJ.redirectErrorMessages();
open(indir + dual_file);
getDimensions(xpix, ypix, nchannels, nslices, nframes);
ID_Old = getImageID();
newImage("D1", "8-bit black", xpix/2, ypix, nSlices);
selectImage("D1");
ID_1 = getImageID();
newImage("D2", "8-bit black", xpix/2, ypix, nSlices);
selectImage("D2");
ID_2 = getImageID();
for(k = 1; k <= nSlices; k++) {
selectImage(ID_Old);
setSlice(k);
makeRectangle(1,1,xpix/2,ypix);
run("Copy");
selectImage(ID_1);
setSlice(k);
run("Paste");
selectImage(ID_Old);
setSlice(k);
makeRectangle((xpix/2)+1, 1, xpix, ypix);
run("Copy");
selectImage(ID_2);
setSlice(k);
run("Paste");
}
if (no_nd_wavelengths) {
// Special case of no wavelength information in .nd file or
// filenames.
//
// According to the MDA file format explained here:
// http://mdc.custhelp.com/app/answers/detail/a_id/18979/kw/file%20naming
// Wavelength `_w' is the first dimension in the MDA file
// name, followed by stage position `_s' and then time `_t'.
// Ignoring stage position, since one cannot use it in
// streaming experiments. Thus we assume wavelength preceeds
// time.
ti = lastIndexOf(dual_file, "_t");
file_start = substring(dual_file, 0, ti);
file_end = substring(dual_file, ti, lengthOf(dual_file));
ch1_file = file_start + "_w1" + replacement_channel_left + file_end;
ch2_file = file_start + "_w2" + replacement_channel_right + file_end;
}
else {
wi = indexOf(dual_file, "_w") + lengthOf("_w");
w1 = substring(dual_file, wi, wi+1);
w2 = toString(parseInt(w1) + 1);
ch1_file = replace(dual_file,
"_w\\d" + channel_to_split,
"_w" + w1 + replacement_channel_left);
ch2_file = replace(dual_file,
"_w\\d" + channel_to_split,
"_w" + w2 + replacement_channel_right);
}
selectImage(ID_1);
save(outdir + ch1_file);
selectImage(ID_2);
save(outdir + ch2_file);
}
/**
* Modify .nd file as 2 channel and save a copy.
*
* @param infile path of input.nd file
* @param outfile path for output .nd file
* @param ch_dual name of dual channel
* @param ch1 name of first split channel
* @param ch2 name of second split channel
* @return number of initial wavelengths
*/
function save_new_nd_file(infile, outfile, ch_dual, ch1, ch2) {
ch_dual = nd_safe_wavename(ch_dual);
ch1 = nd_safe_wavename(ch1);
ch2 = nd_safe_wavename(ch2);
file_string = File.openAsString(infile);
w_key = '"NWavelengths", ';
wi = indexOf(file_string, w_key) + lengthOf(w_key);
w_value = substring(file_string, wi, wi+1);
if (w_value == "0") {
no_nd_wavelengths = 1;
file_string = replace(file_string,
'"DoWave", FALSE',
'"DoWave", TRUE');
file_string = replace(file_string,
'"WaveInFileName", FALSE',
'"WaveInFileName", TRUE');
do_z_1 = "";
do_z_2 = "";
if (indexOf(file_string, '"DoZSeries", TRUE') > -1) {
do_z_1 = '"WaveDoZ1", TRUE\n';
do_z_2 = '"WaveDoZ2", TRUE\n';
}
file_string = replace(file_string,
'"NWavelengths", 0\n',
'"NWavelengths", 2\n' +
'"WaveName1", "' + ch1 + '"\n' +
do_z_1 +
'"WaveName2", "' + ch2 + '"\n' +
do_z_2);
}
else {
new_w_value = toString(parseInt(w_value) + 1);
file_string = replace(file_string,
w_key + w_value,
w_key + new_w_value);
do_z_1 = "";
do_z_2 = "";
if (indexOf(file_string, '"DoZSeries", TRUE') > -1) {
do_z_1 = '"WaveDoZ' + w_value + '", TRUE\n';
do_z_2 = '"WaveDoZ' + new_w_value + '", TRUE\n';
// MetaMorph is sensitive to the position of the "WaveDoZ#"
// key, so we have to position it after the corresponding
// "WaveName#".
}
file_string = replace(file_string,
'"WaveDoZ' + w_value + '", TRUE\n',
"");
file_string = replace(file_string,
'"WaveName\\d", "' + ch_dual + '"\n',
'"WaveName' + w_value + '", "' + ch1 + '"\n' +
do_z_1 +
'"WaveName' + new_w_value + '", "' + ch2 + '"\n' +
do_z_2);
}
File.saveString(file_string, outfile);
return toString(w_value);
}
/**
* Replace illegal characters from WaveName value for .nd file.
*
* @param string channel name in MetaMorph
* @return string of safe channel name
*/
function nd_safe_wavename(string) {
return replace(string, "[-/\]", "_");
}
/**
* Filter file list with RegEx.
*
* @param directory path where files are located
* @param regexp desired match expression
* @return array file list
*/
function match_files(directory, regexp) {
all_file_list = getFileList(directory);
match_file_list = newArray();
for (i = 0; i < all_file_list.length; i++) {
file = all_file_list[i];
if (matches(file, regexp))
match_file_list = Array.concat(match_file_list, file);
}
return match_file_list;
}
// Emacs needs these comments to recognize the ImageJ macro format.
//
// Local Variables:
// mode: java
// c-basic-offset: 3
// tab-width: 3
// indent-tabs-mode: nil
// End:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment