diff --git a/BIOP_VSI_reader.ijm b/BIOP_VSI_reader.ijm index 3503df4..da1a2c6 100644 --- a/BIOP_VSI_reader.ijm +++ b/BIOP_VSI_reader.ijm @@ -1,1351 +1,1353 @@ // Action Bar description file : BIOP_VSI_reader // By Olivier Burri & Romain Guiet // EPFL BIOP 2014 /* * DESCRIPTION: Simple Action Bar to open and make sense of the Olympus OlyVIA Slide Scanner * The macro works as follows * Uses Jerome Mutterer's Action Bar Plugin as the interface. * Takes advantage of the print "Update" Function as of IJ 1.38m to keep data in memory. * Uses LOCI BioFormats to read and parse the data from the VSI reader * * MOTIVATION: Images produced by the OlyVIA Scanner are extremely large (>48'000px) * The inherent limitation of Java (or IJ) for images is about 48'000px * Because the .vsi files have preview thumbnails, we want to use those in order to * navigate the data and open only the part of the image we're interested in. * A Simple navigator is provided for convenience. * * DISCLAIMER: This is a work in progress and some bugs are bound to pop up, don't hesitate to contact us in case you have problems * at olivier.burri at epfl.ch * Bug 18.05.2012: Navigator: Not obvious when macro finished working. Find a way to stop the position update more easily. */ // Action Bar settings sep = File.separator; // Install the BIOP Library call("BIOP_LibInstaller.installLibrary", "BIOP"+sep+"BIOPLib.ijm"); runFrom = "jar:file:BIOP/BIOP_VSI_reader.jar!/BIOP_VSI_reader.ijm"; ////////////////////////////////////////////////////////////////////////////////////////////// // The line below is for debugging. Place this VSI file in the ActionBar folder within Plugins ////////////////////////////////////////////////////////////////////////////////////////////// //runFrom = "/plugins/ActionBar/BIOP_VSI_reader.ijm"; print("Running From: "+runFrom); run("Action Bar", runFrom ); exit(); //Start of ActionBar /* * Must be set for the function library to work */ function toolName() { return "VSI Reader"; } /* * Flag to set for debugging */ function isDebug() { return false; } /* * Debug print. If a debug flag is set, then print, otherwise do nothing. */ function dprint(text) { deb = isDebug(); if (deb) { print(text); } } /* * isVSI just checks at the file extension */ function isVSI(filename) { if(endsWith(filename, ".vsi")) { return true; } return false; } /* * Setting and recovering the ID of the current fuile */ function getID() { id = getData("Series ID"); return id; } function setID(id) { setData("Series ID",id); dir = substring(id, 0,lastIndexOf(id,File.separator)+1); setData("Image Folder",dir); } /* * Extracts the name of the image from its directory+file string */ function getImageName() { name = getData("Series ID"); name = substring(name, lastIndexOf(name, File.separator)+1,lengthOf(name)); return name; } // Extracts the directory function getImageDirectory() { name = getData("Series ID"); dir = substring(name, 0, lastIndexOf(name, File.separator)+1); return dir; } /* parseSeriesData(fileID) * Serves for the analysis of the series, using the following rules: * All pyramidal series are ordered from largest to smallest size (512x512). * The intermediate sizes seem incomplete... * */ function parseSeriesData(fileID) { dprint("Parsing Series Data for "+fileID); offset = log(getThumbnailSize())/log(2) -9 ; run("Bio-Formats Macro Extensions"); setID(fileID); Ext.setId(fileID); // Get the total number of images in the .VSI file. Ext.getSeriesCount(seriesCount); setData("totSeries", seriesCount); dprint("Total number of series in VSI file: "+seriesCount); // Now we count the data fullSeries = 0; s = 0; lastIndex = 0; dprint("Finding how many thumbnails ("+512+"x"+512+") images there are..."); while (s < seriesCount) { Ext.setSeries(s); Ext.getSizeX(sizeX); Ext.getSizeY(sizeY); //print(s,":", sizeX, sizeY); dprint("Series "+s+ " is "+sizeX+" x "+sizeY+ "px"); // If it's thumbSize; if(sizeX == 512 && sizeY == 512) { // get the one after //This is a series fullSeries++; // Last index tells us where we stopped finding new series lastIndex = s+1; dprint("Series Found."+fullSeries+" Total series. Last index of series: "+lastIndex); // skip the next one } s+=1; //increment search } setData("Last Index",lastIndex); /* * now from lastIndex to the end we should be able to discover some things about the image * first one is the overview (always there) * second is the label( if defined) * third is the 2x preview (always there) * fourth is the mask (maybe) * * A trick, calculate from the last series, going back up how many 'micro' thumbnails there are, this gives seriesNum */ oldnSeries = getNSeriesFromThumbs(); // Now: Magic! unaccountedSeries = fullSeries - oldnSeries; dprint("Series unaccounted for: "+unaccountedSeries); // Logically there should be 1 or two // if 1, then it's the 2X preview // if 2, then it's the label and the 2X preview // if 0, then no 2x preview.... hasPreview = true; hasLabel = false; if (unaccountedSeries == 2 ) { hasLabel = true; } else if (unaccountedSeries == 1) { hasLabel = false; } else if (unaccountedSeries == 0) { // No 2x Preview!!!!! hasPreview = false; } setBool("Has label", hasLabel); setBool("Has Preview", hasPreview); // Now we should know everything. // check if it has the mask // See what remains mask = seriesCount - lastIndex - 2*oldnSeries - 1 - hasLabel - hasPreview; // This value should be 0 or 1 //print("mask (",mask,")= ", seriesCount ,"-", lastIndex, "-", 2*oldnSeries, "- 2 -", hasLabel); if(mask == 1) { hasMask = true; } else { hasMask = false; } setBool("Has mask", hasMask); setData("nSeries", oldnSeries); setData("Mode", "Normal"); return 1; } function getLabelPos() { hasLabel = getBool("Has label"); if(hasLabel) { lIdx = getData("LastIndex"); return lIdx+1; } else { return -1; } } function getOverviewPos() { lIdx = parseInt(getData("Last Index")); return lIdx; } function get2xPreviewPos() { hasLabel = getBool("Has label"); hasPreview = getBool("Has Preview"); if (hasPreview) { if(hasLabel) { pos = getRawSeriesPos(2); } else { pos = getRawSeriesPos(1); } } else { pos = -1; } return pos; } function getMaskPos() { hasMask = getBool("Has mask"); nS = parseInt(getData("nSeries")); totS = parseInt(getData("totSeries")); pos = -1; if(hasMask) { pos = (totS-2*nS-1); } return pos; } /* * This function looks at images based on the nanoscopic thumbnails residing in the VSI file */ function getNSeriesFromThumbs() { run("Bio-Formats Macro Extensions"); fileID = getID(); Ext.setId(fileID); serN = parseInt(getData("totSeries")); oldSeries = 0; // Run this baby backwards dprint("Running backwards to find number of thumbnails (No labels or overviews)"); for (i=serN-1; i>=0; i--) { Ext.setSeries(i); Ext.getSizeX(sizeX); Ext.getSizeY(sizeY); if(sizeX < 100 && sizeY < 100) oldSeries++; } dprint("Found "+oldSeries+" thumbnails smaller than 100px"); return oldSeries; } function getRawSeriesPos(series) { serN = parseInt(getData("totSeries")); nextSer = 0; nSer=1; //print("Searching for Series ", series); for (i=0; i sizeX) { neworiW = sizeX - oriX; print("Size of image exceeds image dimensions, cropping:"); print("Original Width: "+oriW+" , New Width: "+neworiW); oriW = neworiW; } if (oriY + oriH > sizeY) { neworiH = sizeY - oriY; print("Size of image exceeds image dimensions, cropping:"); print("Original Height: "+oriH+" , New Height: "+neworiH); oriH = neworiH; } // Append rescaling factor to image name if (rescaling != 1) { rescaleText = " Scaled "+rescaling; } else { rescaleText = ""; } // Make sure that the chosen size is not larger than what imageJ can handle if( oriW > 42000 || oriH > 42000) { showMessage("Your selection is too large ("+oriX+", "+oriY+")\n Please select a smaller area"); } else if(matches(name,".*Thumbnail.*")) { openLociSubStack("Series #"+serInd+" at ("+oriX+", "+oriY+")"+rescaleText, (serPos), oriX, oriY, oriW , oriH); } else { showMessage("Please Select a Thumbnail Image!"); } } /* * openLociStack opens the stack at the given index. This index is the raw index of the image as interpreted by LOCI * It starts at 1 and should be used in conjuction with the "getSeriesIndex... series of functions to ensure it works properly * Based on an example by Wayne Rasband on the LOCI Website */ function openLociStack(name, index) { run("Bio-Formats Macro Extensions"); id = getID(); Ext.setId(id); // Place yourself at the desired series Index (starts at 0); Ext.setSeries(index); // Get ther dimensions Ext.getSizeC(sizeC); Ext.getSizeZ(sizeZ); Ext.getSizeT(sizeT); Ext.getSizeX(sizeX); Ext.getSizeY(sizeY); Ext.getImageCount(n); //print(index); //Start opening the series setBatchMode(true); for (i=0; i1) { Stack.setDimensions(sizeC, sizeZ, sizeT); if (sizeC>1) { if (sizeC==3&&sizeC==nSlices) mode = "Composite"; else mode = "Color"; run("Make Composite", "display="+mode); } setOption("OpenAsHyperStack", true); } setBatchMode(false); run("Select None"); } /* * Same as above, but opens a sub-region of the image */ function openLociSubStack(name, index, posX, posY, w, h) { run("Bio-Formats Macro Extensions"); id = getID(); Ext.setId(id); Ext.setSeries(index); Ext.getSizeC(sizeC); Ext.getSizeZ(sizeZ); Ext.getSizeT(sizeT); Ext.getImageCount(n); setBatchMode(true); for (i=0; i1) { cMode = getData("Camera Mode"); Stack.setDimensions(sizeC, sizeZ, sizeT); if (sizeC>1) { if (sizeC==3&&sizeC==nSlices) mode = "Composite"; else mode = "Color"; run("Make Composite", "display="+mode); } setOption("OpenAsHyperStack", true); if (cMode == "Brightfield") { for (i=0; i tol) { return round(value)+1; } else { return round(value); } } function countRoiFiles() { roiDir = getRoiFolder("Open"); roiFiles = getFileList(roiDir); c=0; for(i=0; i