/* Simple macro to convert a tilt-pair stack to red-blue anaglyph v210909: 1st working version v210910c: Adds flicker 3D GIF animation save and compression using gifsicle if available v210913: Adds multiple gifsicle location possibilities and SIFT alignment v210914: Added gifsicle location preferences v210921: Added generic app-finding functions v210924: Added post alignment crop option, fixed options dialog mismatch, added save location to prefs v211006: Fixed workingImageID issue. v211007: Smarter resizing options. v211007 Added prealignment. v211011: Tries to more effectively transfer titles and calibrations. v211012: More logical dialog order. v211018: Updated findAppPath function v211025: Updated stripKnownExtensionFromString function v211104: Updated stripKnownExtensionFromString function 211112 + 220616 + 230505(f3) + 060723: Again v211213 fixed appPath function f5: Replaced getDir with getDirectory for ImageJ 1.54g10 */ macro "Tilt-pair to red-blue anaglyph" { macroL = "Tilt_Pair_to_Red-Blue_Anaglyph_v211214-f5.ijm"; depth = bitDepth(); nTitle = stripKnownExtensionFromString(getTitle()); sliceN = nSlices; imageN = nImages; workingImageID = getImageID; if (imageN>1){ imgIDs = newArray(nImages); imgTs = newArray(nImages); for (i=0; i2) exit("2-slice stack needed for this version of the anaglyph macro"); if (sliceN==1){ if (imageN<2) exit("Sorry, macro needs 2 open tilts"); else stackEm = true; } getVoxelSize(imgPxW, imgPxH, imgPxD, imgU); Dialog.create(macroL); if (stackEm){ Dialog.addCheckbox("Stack two tilt images as selected below?",true); Dialog.addChoice("Select control image:",imgTs,imgTs[0]); Dialog.addChoice("Select tilted image:",imgTs,imgTs[1]); } else Dialog.addCheckbox("Is first slice control image?",true); lrOptions = newArray("left","right","not sure"); Dialog.addRadioButtonGroup("Control image is:_________", lrOptions, 1,3,"left"); if (depth==16) Dialog.addCheckbox("Create composite \(retains 16-bit\)",false); if (imgU!="pixels") Dialog.addCheckbox("Retain scale calibration for all images?",true); Dialog.addCheckbox("Apply initial translation alignment and manual crop before other processes?",true); Dialog.addCheckbox("Apply bleach correction to balance image histograms?",true); if (alignImages){ if (stackRegCheck) rrr = "SR options use StackReg"; if (SIFTCheck){ sss = "SIFT uses Linear SIFT"; if (stackRegCheck) aaa = " and "; } radioLabel = "Align images \("+rrr+aaa+sss+"\)?"; Dialog.addRadioButtonGroup(radioLabel,regOptions,3,3,"No"); sOs = newArray("initial_gaussian_blur","steps_per_scale_octave","minimum_imag,e_size","maximum_image_size","feature_descriptor_size","feature_descriptor_orientation_bins"," closest\/next_closest_ratio","maximal_alignment_error","inlier_ratio"); sOSs = newArray("interpolate","show_info","show_transformation_matrix"); sVNs = newArray(1.60,3,64,1024,4,8,0.92,25,0.05); sVSsOptions = newArray(true,false,false); sOSsOptions = ""; sOSsOptionsM = ""; for(i=0;i125){ rFactorsDefault[j] = rFactorsBase[i]; rFactorsDefaultW[j] = newWidth; rFactorsDefaultH[j] = round(imageHeight * rFactorsBase[i]); j++; } } // rFactors = newArray(0.5,1/3,0.25,0.1); Dialog.addCheckbox("Duplicate full size with lossy compression?",true); Dialog.addNumber("Lossiness:",80,0,3,"% Gifsicle default is 20 but 80 seems to work well for these"); Dialog.addCheckbox("Add scaled and lossy \(set above\) compressed copies of animations?",true); Dialog.addChoice("Resize method:",rMethods,"lanczos3"); Dialog.setInsets(0, 120, 10); Dialog.addMessage("Scaling using lanczos3 is generally more accurate but more CPU-intensive than Catmull-Rom/bicubic in ImageJ"); for(i=0; i0 && rF<1) rFactors = Array.concat(rFactors,rF); } if (!gifsicles && !lossyFull) gifsicle = false; } if (anim3D){ if (!endsWith(animSaveDirectory,fs)) animSaveDirectory += fs; if (!File.isDirectory(animSaveDirectory)){ if (File.exists(animSaveDirectory)) exit("Selected anim directory is a file not a directory"); else { File.makeDirectory(animSaveDirectory); print("New animation directory created:" + animSaveDirectory); } } } /* End options dialog */ if (preAlign){ run("Linear Stack Alignment with SIFT", "initial_gaussian_blur=1.60 steps_per_scale_octave=3 minimum_image_size=64 maximum_image_size=1024 feature_descriptor_size=4 feature_descriptor_orientation_bins=8 closest/next_closest_ratio=0.92 maximal_alignment_error=25 inlier_ratio=0.05 expected_transformation=Translation interpolate"); setBatchMode("exit & display"); setTool("rectangle"); run("Select Bounding Box (guess background color)"); title="Crop stack after alignment"; msg = "1. Select the area that you want to crop to.\n2. Click on OK"; waitForUser(title, msg); run("Crop"); run("Select None"); nTitle += "_crp"; rename(nTitle); if (retainScale) setVoxelSize(imgPxW, imgPxH, imgPxD, imgU); setBatchMode(true); } if (bC) { run("Bleach Correction", "correction=[Histogram Matching]"); nTitle += "_blch"; rename(nTitle); if (retainScale) setVoxelSize(imgPxW, imgPxH, imgPxD, imgU); processed = true; workingImageID = getImageID; } if (endsWith(alignImageOption,"-SIFT")){ transformation = substring(alignImageOption,0,indexOf(alignImageOption,"-SIFT")); if (tweakSIFT){ Dialog.create("SIFT settings"); for(i=0;i0) gSCommands += "--lossy="+lossG+" "; gSSep = " -o "; if(!File.exists(aGIFPath)) print("Saved animation not found:\n",aGIFPath); else call("ij.Prefs.set", "asc.lastsaved.gifanim.path", aGIFPath); endIndex = lastIndexOf(aGIFPath,"_anim3D.gif"); if (endIndex>0) filePathG1 = substring(aGIFPath,0,endIndex); else { endIndex = lastIndexOf(aGIFPath,".gif"); if (endIndex>0) filePathG1 = substring(aGIFPath,0,endIndex); else filePathG1 = aGIFPath; } // print("filePathG1: "+filePathG1); if (lossyFull) { newFileName = filePathG1+"_loss"+lossG+"_anim3D.gif"; // print("Saving lossy GIF to:\n",newFileName); showStatus("Saving lossy GIF to:\n",newFileName); exec(gSPath+gSCommands+aGIFPath+gSSep+newFileName); } gSCommands += "--resize-method "+resizeMethod+" "; // print(gSCommands); if (gifsicles){ for(i=0; i0){ rFname = replace(rFactors[i],".","p"); if (lengthOf(rFname)>5) rFname = substring(rFname,0,5); newFileName = filePathG1+"_loss"+lossG+"scale"+rFname+"_anim3D.gif"; // print("Saving resized lossy GIF to:\n", newFileName); gSCommands += "--scale "+rFactors[i]+" "; gsCommand = gSPath+gSCommands+aGIFPath+gSSep+newFileName; // print(gsCommand); exec(gSPath+gSCommands+aGIFPath+gSSep+newFileName); } } } } } } selectImage(workingImageID); if (LR=="right") run("Reverse"); else if (LR=="not sure"){ try1 = getImageID; run("Duplicate...", "title="+nTitle+"_RL duplicate"); run("Reverse"); addCopyOfSliceToStack (2); if (depth==16){ if (compo) run("Make Composite", "display=Composite"); else run("Stack to RGB"); } else run("Stack to RGB"); rename(nTitle+"_RL_ana3D"); selectImage(try1); } addCopyOfSliceToStack (2); if (depth==16){ if (compo) run("Make Composite", "display=Composite"); else run("Stack to RGB"); } else run("Stack to RGB"); rename(nTitle+"_LR_ana3D"); closeImageByTitle("DUP_Stack"); closeImageByTitle("Stack"); closeImageByTitle(nTitle+"_RL"); setBatchMode("exit & display"); beep(); wait(50); beep(); wait(50); beep(); call("java.lang.System.gc"); showStatus("Anaglyph 3D image created"); } /* ( 8(|) ( 8(|) ASC Functions @@@@@:-) @@@@@:-) */ function addCopyOfSliceToStack (slice){ /*`Duplicates slice to adjacent position whereas a combination of duplicated and concatenate adds the duplicate slice to the end or beginning only v210909: 1st version */ setSlice(slice); run("Select All"); run("Copy"); run("Select None"); run("Add Slice"); run("Paste"); run("Select None"); } function checkForPluginNameContains(pluginNamePart) { /* v180831 1st version to check for partial names so avoid versioning problems ... v220722 Uses File.separator and adds .class v230912 This version is case insensitive and does NOT require restoreExit. NOTE: underlines are NOT converted to spaces in names */ pluginCheck = false; pluginNamePart = toLowerCase(pluginNamePart); fS = File.separator; pluginDir = getDirectory("plugins"); if (pluginDir == "") IJ.log("Failure to find any plugins!"); else { pluginFolderList = getFileList(pluginDir); subFolderList = newArray(); pluginList = newArray(); for (l=0; l=0) pluginCheck = true; } /* If not in the root try the subfolders */ if (!pluginCheck) { for (i=0; i=0) pluginCheck = true; } } } } return pluginCheck; } function closeImageByTitle(windowTitle) { /* Cannot be used with tables */ /* v181002 reselects original image at end if open v200925 uses "while" instead of "if" so that it can also remove duplicates */ oIID = getImageID(); while (isOpen(windowTitle)) { selectWindow(windowTitle); close(); } if (isOpen(oIID)) selectImage(oIID); } function findAppPath(appName,appEx,defaultPath) { /* v210921 1st version: appName is assumed to be the app folder name, appEx is the executable, default is the default return value v211018: assumes specific executable path stored in prefs Prints message rather than exits when app not found v211213: fixed defaultPath error v211214: Adds additional location as packaged within a Fiji/IJ distribution v220121: Changed fS line v230803: Replaced getDir with getDirectory for ImageJ 1.54g10 */ functionL = "findAppPath_v230803"; fS = File.separator; ijPath = getDirectory("imagej"); appsPath = substring(ijPath,0,lengthOf(ijPath)-1); appsPath = substring(appsPath,0,lastIndexOf(appsPath,fS)); appFound = false; prefsName = "asc.external.paths." + toLowerCase(appName) + "." + appEx; appPath = call("ij.Prefs.get", prefsName, defaultPath); appLoc = ""+fS+appName+fS+appEx; cProg = "C:"+fS+"Program Files"; defAppPaths = newArray(cProg+fS+"Utilities"+appLoc,cProg+" \(x86\)"+fS+"Utilities"+appLoc,cProg+appLoc,cProg+" \(x86\)"+appLoc,appsPath+appLoc,ijPath+"Apps"+appLoc); if (!File.exists(appPath)) { for(i=0;i0){ protectedPath = substring(string,0,protectedPathEnd); string = substring(string,protectedPathEnd); } unusefulCombos = newArray("-", "_"," "); for (i=0; i=0) string = replace(string,combo,unusefulCombos[i]); } } if (lastIndexOf(string, ".")>0 || lastIndexOf(string, "_lzw")>0) { knownExts = newArray(".avi", ".csv", ".bmp", ".dsx", ".gif", ".jpg", ".jpeg", ".jp2", ".png", ".tif", ".txt", ".xlsx"); knownExts = Array.concat(knownExts,knownExts,"_transp","_lzw"); kEL = knownExts.length; for (i=0; i0){ preChan = substring(string,0,iChanLabels); postChan = substring(string,iChanLabels); while (indexOf(preChan,knownExts[i])>0){ preChan = replace(preChan,knownExts[i],""); string = preChan + postChan; } } } while (endsWith(string,knownExts[i])) string = "" + substring(string, 0, lastIndexOf(string, knownExts[i])); } } unwantedSuffixes = newArray(" ", "_","-"); for (i=0; i0){ if(!endsWith(protectedPath,fS)) protectedPath += fS; string = protectedPath + string; } return string; }