import java.io.*; import java.util.*; import java.awt.*; import ij.*; import ij.plugin.*; import ij.process.*; import ij.io.*; import ij.measure.*; import ij.text.*; import ij.gui.*; // Jim Hull, The University of Washington Chemical Engineering // Authored 01/04 // Modified on 04/2007 by Philippe Carl, Life science project engineer Veeco Instruments GmbH // E-mail: pcarl@veeco.de // Modified on 10/2008 by Xin Zhang, Materials Science and Engineering, University of Maryland at College Park // E-mail: zhangx@umd.edu public class ExtendedOpen_NV implements PlugIn{ FileInfo nFileInfo; FileOpener nFileOpener; private int bufferCount=0; String buf = ""; BufferedReader fileInputStream; File file; private String strDate; private String strEquipmentDescript; private String strController; private String strMicroscope; private String strExtender; private String strAspectRatio; private String strFrameDirection; int dataLength = 0; int lines = 0; int samplesPerLine = 0; double scanSize = 0d; //unit in nm double scanRate = 0d; //unit in Hz double tipVelocity = 0d; //unit in um/s double heightSensitivity = 0d; //unit in nm/V double phaseSensitivity = 0d; double amplitudeSensitivity = 0d; //containers of double Vector vZOffset = new Vector(); Vector vZScale = new Vector(); Vector vZMagnify= new Vector(); //containers of int Vector vDataLength = new Vector(); Vector vDataOffset = new Vector(); //containers of String Vector vNote = new Vector(); Vector vDataType = new Vector(); Vector vLineDirection = new Vector(); private int colonIndex, unitSkip; public void run(String arg) { if (arg.equals("")) { OpenDialog od = new OpenDialog("Opening nanoscope iii file...", "", ""); String fileName = od.getFileName(); if (fileName==null) return; String directory = od.getDirectory(); IJ.showStatus("Opening: " + directory + fileName); IJ.log("\nInformation for " + fileName); file = new File(directory + fileName); } else file = new File(arg); vZOffset.removeAllElements(); vZScale.removeAllElements(); vZMagnify.removeAllElements(); vDataLength.removeAllElements(); vDataOffset.removeAllElements(); vNote.removeAllElements(); vDataType.removeAllElements(); vLineDirection.removeAllElements(); openNV(file); } //end run public void openNV(File fileName){ //Create the file input stream try{ fileInputStream = new BufferedReader(new FileReader(fileName)); IJ.showStatus("Opening Buffered Reader"); } catch (FileNotFoundException exception){ IJ.showStatus("Buffered Reader Exception"); } //Read the Header readHeader(fileInputStream); //Close the input stream try{ fileInputStream.close(); IJ.showStatus("Closing Buffered Reader"); } catch (IOException exception){ IJ.showStatus("Buffered Reader Exception"); } //Set up the images for(int i = 0; i < bufferCount; i++) { //Set Parameters and open each buffer nFileInfo = new FileInfo(); nFileInfo.fileType = nFileInfo.GRAY16_SIGNED; nFileInfo.fileName = fileName.toString(); nFileInfo.width = samplesPerLine; nFileInfo.height = lines; nFileInfo.offset = ( ( Integer )vDataOffset.elementAt( i ) ).intValue(); nFileInfo.intelByteOrder = true; //all nanoscope softwares are intel only //Open the Image nFileOpener = new FileOpener( nFileInfo ); ImagePlus imp = nFileOpener.open( true ); ImageProcessor ipr = imp.getProcessor(); Calibration cal = imp.getCalibration(); cal.info = "Type:"+(String)vLineDirection.elementAt(i)+";"; IJ.log( (String)vDataType.elementAt(i) ); if ( ((String)vDataType.elementAt(i)).equals( "Height" ) ){ ipr.multiply( heightSensitivity * ((Double)vZScale.elementAt( i )).doubleValue()/65536); IJ.log("heigh factor:"+heightSensitivity * ((Double)vZScale.elementAt( i )).doubleValue()/65536); /* * Process the height image * this change the raw number into the real number in nm, * !!!! This is for Version 4.3 of nanoscope software and later only !!!! * information acquired from helpfile of nanoscope software section B5 * heightSensitivity :Sens. Zscan nm/V * buffProps[ i ][ 3 ]: "Soft scale" of the LSB data (raw data number) in V/LSB * real height(nm) = raw data (LSB) * Sens. Zscan (nm/V) * Soft scale (V/LSB)/ 65536 * LSB is explained in help file section B.5.4 * I don't know what to do with phase or amplitude image yet!!! But same calculation is applied. */ cal.info = cal.info + "Sensitivity:" + heightSensitivity + ";"; } else if ( ((String)vDataType.elementAt(i)).equals( "Phase" ) ){ ipr.multiply( phaseSensitivity * ((Double)vZScale.elementAt( i )).doubleValue()/65536); /* * Same calculation method of the height image */ cal.info = cal.info + "Sensitivity:" + phaseSensitivity + ";"; } else if ( ((String)vDataType.elementAt(i)).equals( "Amplitude" ) ){ ipr.multiply( amplitudeSensitivity * ((Double)vZScale.elementAt( i )).doubleValue()/65536); /* * Same calculation method of the height image */ cal.info = cal.info + "Sensitivity:" + amplitudeSensitivity + ";"; } cal.pixelWidth = scanSize/samplesPerLine; //real distance over pixels cal.pixelHeight = scanSize/lines; cal.pixelDepth = cal.pixelWidth; /* FIX ME! * What does this pixel depth mean? */ cal.setUnit("nm"); imp.setCalibration( cal ); showInfoWindow( fileName.toString(),i); } //end for } //end openNV public void readHeader(BufferedReader in){ //Scan through header for file info while(!buf.endsWith("File list end")){ //IJ.log("buffer count " + new Integer(bufferCount).toString()); try{ buf = new String(in.readLine()); } catch (IOException exception){ IJ.showStatus("IO Exception"); } buf=buf.substring(1); colonIndex=buf.indexOf(":"); unitSkip=buf.length()-2; if( 0 == bufferCount ){ if (buf.startsWith("Date:") ){ strDate = buf.substring(colonIndex + 1).trim(); IJ.log("Date: " + strDate); continue; } if ( buf.startsWith( "Data length:" ) ){ dataLength = new Integer(buf.substring(colonIndex + 1).trim()).intValue(); IJ.log("Header Length " + dataLength); continue; } if (buf.startsWith("Description:") ){ strEquipmentDescript = buf.substring(colonIndex + 1); IJ.log("Description: " + strEquipmentDescript ); continue; } if (buf.startsWith("Controller:") ){ strController = buf.substring(colonIndex + 1); IJ.log("Controller: " + strController ); continue; } if (buf.startsWith("Microscope:") ){ strMicroscope = buf.substring(colonIndex + 1); IJ.log("Microscope: " + strMicroscope ); continue; } if (buf.startsWith("Extender:") ){ strExtender = buf.substring(colonIndex + 1); IJ.log("Extender: " + strExtender ); continue; } //Get the number if pixels if ( buf.startsWith( "Samps/line:" ) ){ samplesPerLine = new Integer(buf.substring(colonIndex + 1).trim()).intValue(); IJ.log("Samps/line: " + samplesPerLine); continue; } if ( buf.startsWith( "Lines:" ) ){ lines = new Integer(buf.substring(colonIndex + 1).trim()).intValue(); IJ.log("Lines: " + lines); continue; } if ( buf.startsWith( "Scan size:" ) ){ scanSize = new Double(buf.substring(colonIndex + 1, unitSkip).trim()).doubleValue(); IJ.log("Scan Size: " + scanSize + "nm"); continue; } if ( buf.startsWith( "Scan rate:" ) ){ scanRate = new Double(buf.substring(colonIndex + 1, unitSkip).trim()).doubleValue(); IJ.log("Scan rate: " + scanRate + "Hz"); continue; } if (buf.startsWith("Tip velocity:") ){ tipVelocity = new Double(buf.substring(colonIndex + 1, unitSkip).trim()).doubleValue(); IJ.log("Tip velocity: " + tipVelocity + "um/s "); continue; } if (buf.startsWith("@Sens. Zscan: V") ){ heightSensitivity = new Double(buf.substring(buf.indexOf("V") + 1, unitSkip - 2 ).trim()).doubleValue(); IJ.log("Z scan sensitivity: " + heightSensitivity + "nm/V"); continue; } if (buf.startsWith("@Sens. Phase: V") ){ phaseSensitivity = new Double(buf.substring(buf.indexOf("V") + 1 ).trim()).doubleValue(); IJ.log("Phase sensitivity: " + phaseSensitivity ); continue; } if (buf.startsWith("@Sens. Amplitude: V") ){ amplitudeSensitivity = new Double(buf.substring(buf.indexOf("V") + 1 ).trim()).doubleValue(); IJ.log("Amplitude sensitivity: " + amplitudeSensitivity ); continue; } if (buf.startsWith("Aspect ratio:") ){ strAspectRatio = buf.substring(colonIndex + 1).trim(); IJ.log("Aspect ratio: " + strAspectRatio ); continue; } }//end if ( bufferCount == 0 ) if (buf.indexOf("Ciao image list") > -1 ) bufferCount ++; //This is the beginning of each image description //Ciao means "control input and output" if ( bufferCount > 0 ){ if ( buf.startsWith( "Note:" ) ){ vNote.addElement(buf.substring(colonIndex + 1).trim()); IJ.log("Note: " + (String)vNote.elementAt( bufferCount - 1 )); continue; } if (buf.startsWith("Frame direction:") ){ strFrameDirection = buf.substring(colonIndex + 1).trim(); IJ.log("Frame direction: " + strFrameDirection ); continue; } /* Frame direction should be the same for all images. * Don't know why they put one in every image. * This can be fixed in future, if they are different. */ if (buf.startsWith("@2:Image Data:") ){ vDataType.addElement(buf.substring(buf.indexOf('\"') + 1, buf.lastIndexOf('\"'))); IJ.log("Image data type: " + (String)vDataType.lastElement()); continue; } if (buf.startsWith("Line direction:") ){ vLineDirection.addElement(buf.substring(colonIndex + 1 ).trim()); IJ.log("Line direction: " + (String)vLineDirection.lastElement()); continue; } if (buf.startsWith("Data offset:")){ vDataOffset.addElement( new Integer(buf.substring(colonIndex + 1).trim()) ); IJ.log("Data length: " + ((Integer)vDataOffset.lastElement()).intValue()); continue; } if (buf.startsWith("Data length:") ){ vDataLength.addElement( new Integer(buf.substring(colonIndex + 1).trim()) ); IJ.log("Data length: " + ((Integer)vDataLength.lastElement()).intValue()); continue; } if (buf.startsWith("@Z magnify: C [2:Z scale]") ){ vZMagnify.addElement( new Double(buf.substring(buf.indexOf("]") + 1, unitSkip ).trim()) ); IJ.log("Magnify: " + ((Double)vZMagnify.lastElement()).doubleValue() ); continue; } if (buf.startsWith("@2:Z scale:") ){ vZScale.addElement( new Double(buf.substring(buf.indexOf(")") + 1, unitSkip ).trim()) ); IJ.log("Z scale: " + ((Double)vZScale.lastElement()).doubleValue() ); continue; } if (buf.startsWith("@2:Z offset:") ){ vZOffset.addElement( new Double(buf.substring(buf.indexOf(")") + 1, unitSkip ).trim()) ); IJ.log("Z offset: " + ((Double)vZOffset.lastElement()).doubleValue() ); continue; } }//end if ( bufferCount > 0 ) } //end for } //end readHeader public void showInfoWindow( String strTitle, int n ){ TextWindow tWindow = new TextWindow( strTitle + "-" + n + "-info", "", 300, 300 ); //ImageWindow iw = ip.getWindow(); TextPanel tP = tWindow.getTextPanel(); tP.setTitle( "Information" ); tP.setSize( 200, 400 ); tP.setColumnHeadings("Name\tValue"); tP.append( "File:\t" + strTitle ); tP.append( "Date:\t" + strDate); tP.append( "Lines:\t" + lines); tP.append( "Samples/line:\t" + samplesPerLine); tP.append( "Scan size (nm):\t" + scanSize); tP.append( "Scan rate (Hz):\t" + scanRate); tP.append( "Tip velocity (um/s):\t" + tipVelocity); tP.append( "Note:\t" + (String)vNote.elementAt(n)); tP.append( "Frame direction:\t" + strFrameDirection); tP.append( "Data type:\t" + (String)vDataType.elementAt(n)); tP.append( "Line direction:\t" + (String)vLineDirection.elementAt(n)); tP.append( "Aspect ratio:\t" + strAspectRatio); if ( ((String)vDataType.elementAt( n ) ).equals( "Height" ) ){ float zLimit = (float)(heightSensitivity * ((Double)vZMagnify.elementAt( n ) ).doubleValue() * ((Double)vZScale.elementAt( n ) ).doubleValue() ); tP.append("Display height range (nm):\t" + zLimit ); /* The limit for display in height image is * zLimit = Z Sensitivity * Z Magnify * Z scale */ //imps[ n ].setDisplayRange( -zLimit/2 , zLimit/2 ); } else if ( ((String)vDataType.elementAt( n ) ).equals( "Phase" ) ){ float pLimit = (float)( ((Double)vZMagnify.elementAt( n ) ).doubleValue() * ((Double)vZScale.elementAt( n ) ).doubleValue() ); tP.append("Display phase range:\t" + pLimit ); /* The limit for display in phase image is * zLimit = Z Magnify * Z scale */ //imps[ n ].setDisplayRange( -655.36 * pLimit/2 , 655.36 * pLimit/2 ); } /* FIX ME * Need amplitude image, but I never got one... I don't know what it looks like. */ }//end showInfoWindow } //end class