Last active
December 15, 2015 01:19
-
-
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
This file contains 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
/** | |
* 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