package IBA_J.Spectra; import IBA_J.GeneratedMap.GeneratedMap; import IBA_J.ConvertListFiles.ADC.ADC; import IBA_J.MainFrame.MainFrame; import ij.*; import IBA_J.Prefs.PrefsManager; import java.util.ArrayList; import java.util.Arrays; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.File; import IBA_J.resources.lib.XYPlotSp; /** * Class Spectra responsible for generating a spectra object using * an ADC object, and optionally the boundary energy values. */ public final class Spectra { private ADC adc; private double[] yields; private float minEnergy=0; private int minChannel=0; private Float energySlope=null;//value of energy between two channels private int mapSizeX=0; // useful if Spectra generated from an ImageGenerated private int mapSizeY=0; // useful if Spectra generated from an ImageGenerated private String directory=null;// useful for saving and restoring private String filename =null; //if spectra is open from a file or a parent spectra private boolean isFromSprectraHeritated=false; //to know if the spectra has been produced from a spectra or a file (if file : level=0) private static MainFrame parentWindow; private ArrayList<GeneratedMap> producedMaps; /** * This is the basic constructor when you want to create a Spectra from an ADC * @param adc the ADC source to build the spectra * @param filename name of the file (ex : "name".pixe (or an other extension) contains the ADC information) */ public Spectra(ADC adc,String filename) { this.producedMaps = new ArrayList<>(); this.adc=adc; this.filename=filename; yields=adc.getSpectra(); int i = yields.length-1; while (yields[i]<1){ i--; } yields=Arrays.copyOfRange(yields, 0, i+1); int nEvents=adc.getNEvents(); //x=0 or y=0 is ignored with this code for(int j=0;j<nEvents;j++){ if(adc.getX(j)<0 || adc.getY(j)<0 || adc.getY(j)>255 || adc.getX(j)>255){ adc.removeEvent(j); nEvents--; } } } // constructor with an ADC, a float value for lower energy delimiter and // a boolean to retrieve the energy value /** * Use this constructor if the Spectra does not start at channel 0 * @param adc * @param filename * @param energyMin * @param startAtEnergMin if true the first channel is adjusted */ public Spectra(ADC adc, String filename, float energyMin, boolean startAtEnergMin) {//to use if spectra don't begin at channel 0 this(adc,filename); this.producedMaps = new ArrayList<>(); if (startAtEnergMin){ this.minChannel=getIndex(energyMin,false); yields=Arrays.copyOfRange(yields, minChannel, yields.length); } this.minEnergy=energyMin; } /** * Use this constructor if the Spectra is generated from an ImageGenerated * @param adc * @param filename * @param generatedMap */ public Spectra(ADC adc,String filename,GeneratedMap generatedMap) { this(adc,filename); this.producedMaps = new ArrayList<>(); //mapSizeX=generatedMap.getWidth()-1; //mapSizeY=generatedMap.getHeight()-1; mapSizeX=generatedMap.getWidth(); mapSizeY=generatedMap.getHeight(); } /** * Use this constructor if the Spectra is generated from an ImageGenerated and does not start at channel 0 * @param adc * @param filename * @param gMap * @param energyMin * @param isStartingAtEnergyMin */ public Spectra(ADC adc,String filename,GeneratedMap gMap, float energyMin, boolean isStartingAtEnergyMin) { this(adc,filename,energyMin,isStartingAtEnergyMin); this.producedMaps = new ArrayList<>(); //mapSizeX=gMap.getWidth()-1; //mapSizeY=gMap.getHeight()-1; mapSizeX=gMap.getWidth(); mapSizeY=gMap.getHeight(); } @Override protected void finalize() throws Throwable { System.gc(); super.finalize(); } // getters & setters public ADC getADC (){ return adc; } public String getDirectory(){ return directory; } public String getPath(){ if (!isFromSprectraHeritated) return filename; return filename; } public String getPath(String roiName){ if (!isFromSprectraHeritated) return filename; return filename+"-"+roiName; } public String getFilename(){ File f=new File(getPath()); if (!isFromSprectraHeritated) return f.getName(); return f.getName()+"-"+String.valueOf(1); } public int getMinChannel(){ return minChannel; } public boolean isSaved(){ return directory!=null; } public boolean getHeritage(){ return isFromSprectraHeritated; } public void setFilename(String filename){ this.filename=filename; } public void setHeritage(boolean isFromSpectraHeritated){ this.isFromSprectraHeritated=isFromSpectraHeritated; } public void setParentWindow(MainFrame parentWindow){ Spectra.parentWindow=parentWindow; parentWindow.addSpectra(this); } public MainFrame getParentWindow(){ return parentWindow; } public static MainFrame getParentWindowS(){ return parentWindow; } // this method returns true if energy vaue is within boundries /** * Use this method to check if an energy exists in the Spectra * @param energy the energy to check * @return true if the energy exists. */ public boolean isAvailable(float energy){ if (energy<minEnergy) return false; float energyMax; if (energySlope==null){ energyMax = minEnergy + (yields.length-1) * 1; } else { energyMax = minEnergy + (yields.length-1) * energySlope; } return energy <= energyMax; } // this method sets the energyMin and energyMax public void energyScaling(float minEnergy, float energySlope){ this.minEnergy=minEnergy; this.energySlope=energySlope; } public boolean isScaled(){ return energySlope!=null; } /** * Use this method to get the energies of the Spectra * @return An array containing all the energies of the Spectra */ public float[] getEnergies(){ float[] energies= new float[yields.length]; energies[0]=minEnergy; float step; if (energySlope==null){step=1;} else{step=energySlope;} for (int i=1;i<energies.length;i++){ energies[i]=energies[i-1]+step; } return energies; } public double[] convertFloatsToDoubles(float[] input){ if (input == null){ return null; } double[] output = new double[input.length]; for (int i = 0; i < input.length; i++){ output[i] = (double) input[i]; } return output; } public XYPlotSp plotSpectra(String titleWindow, String titleGraph){ return plotSpectra(titleWindow, titleGraph,10); } /** * This method creates a new plot using XYPlotSp * @param titleWindow title of the windows where the Spectra will be draw * @param titleGraph title of the component where the Spectra will be draw * @param nROI the number of selected ROI * @return a frame of the class XYPlotSp, use showVisible to affich */ public XYPlotSp plotSpectra(String titleWindow, String titleGraph, int nROI){ PrefsManager prefs=new PrefsManager(); if(nROI<=0)nROI=Integer.valueOf(prefs.ijGetValue("IBA.nROI",""+10)); double[] xEnergies = convertFloatsToDoubles(getEnergies()); XYPlotSp plot1=new XYPlotSp(this,titleWindow,titleGraph,xEnergies,yields,nROI); return plot1; } /** * @param energyToSearch * @param isLowerBounded * @return the index corresponding to the energy which is closer to energyToSearch in function of rightIncluded */ public int getIndex(float energyToSearch, boolean isLowerBounded){ float[] energies=getEnergies(); if (isLowerBounded){ int i=0; while(energies[i]<energyToSearch){ i++; } return i; } else { int i=energies.length-1; while(energies[i]>energyToSearch){ i--; } return i; } } /** * Use this method to search the max "x" or "y". It will help to define the resolution of an ImageGenerated * @param direction give the element to search : "x", "y" or "E"/"e" are accepted (all other values will be considered as "E") * @return the max value of the given element (valToSearch) */ public int searchMax(String direction){ int scan_direction; switch (direction) { case "x": scan_direction=0; break; case "y": scan_direction=1; break; default: return -1; } int max=0; for (int i=0;i<adc.getNEvents();i++){ int[] event=adc.getEvent(i); if(event[scan_direction]>max){max=event[scan_direction];} } return max; } /** * generate a chemical Map from an energy window as a new ImageGenerated using this Spectra * @param minEnergy float value as min channel * @param maxEnergy fload value as max channel * @return a chemical element map */ public GeneratedMap elementMap(float minEnergy,float maxEnergy){ if (mapSizeX==0) mapSizeX=searchMax("x")+1; if (mapSizeY==0) mapSizeY=searchMax("y")+1; int minIndex= getIndex(minEnergy,false)+minChannel; int maxIndex= getIndex(maxEnergy,true)+minChannel; //double[] countPerPixel= new double[(mapSizeX+1)*(mapSizeY+1)]; double[] countPerPixel= new double[(mapSizeX)*(mapSizeY)]; for (int i=0;i<adc.getNEvents();i++){ int[] event=adc.getEvent(i); try{ if (event[2]>=minIndex && event[2]<=maxIndex){ //countPerPixel[event[0]-1+(event[1]-1)*(mapSizeX+1)]+=1; countPerPixel[event[0]+(event[1])*(mapSizeX)]+=1; } } catch(Exception e){} } GeneratedMap img= new GeneratedMap(this,countPerPixel,minEnergy,maxEnergy,mapSizeX,mapSizeY); producedMaps.add(img); return img; } /** * same as generatePicture(start,end) but read the ADC only once for several ImageGenerateds * @param rois array containing the two bounding values for each ImageGenerated * @return a table containing all calculated maps */ public GeneratedMap[] elementMaps(float[][] rois){ if (mapSizeX==0) //mapSizeX=searchMax("x")-1;//-1 because here x starts at 1 and for the picture x starts at 0 mapSizeX=searchMax("x")+1; if (mapSizeY==0) //mapSizeY=searchMax("y")-1; mapSizeY=searchMax("y")+1; int[][] roiIndex = new int[rois.length][2]; for(int i=0; i<rois.length;i++){ roiIndex[i][0]= getIndex(rois[i][0],false)+minChannel; roiIndex[i][1]= getIndex(rois[i][1],true)+minChannel; } //double[][] countPerPixel= new double[rois.length][(mapSizeX+1)*(mapSizeY+1)]; double[][] countPerPixel= new double[rois.length][(mapSizeX)*(mapSizeY)]; for (int i=0;i<adc.getNEvents();i++){ int[] event=adc.getEvent(i); for (int j=0; j<rois.length;j++){ int minRoiIndex = roiIndex[j][0]; int maxRoiIndex = roiIndex[j][1]; try{ if (event[2]>=minRoiIndex && event[2]<=maxRoiIndex){ //countPerPixel[j][event[0]-1+(event[1]-1)*(mapSizeX+1)]+=1; countPerPixel[j][event[0]+(event[1])*(mapSizeX)]+=1; } } catch (Exception e){ IJ.log("** Warning** Event XYE "+event[0] + " " + event[1] + " " + event[2] + " in map " + j +" removed / " + e.toString()); int index=event[0]+(event[1])*(mapSizeX+1); IJ.log("index " + index + "sizeX " + mapSizeX); } } } GeneratedMap[] arrayOfImgGen = new GeneratedMap[rois.length]; for (int i=0; i<rois.length;i++){ float start = rois[i][0]; float end = rois[i][1]; arrayOfImgGen[i]= new GeneratedMap(this,countPerPixel[i],start,end,mapSizeX,mapSizeY); producedMaps.add(arrayOfImgGen[i]); } return arrayOfImgGen; } public String getNameToSave(){ return getPath()+".spct.spj"; } /** * the save method stores spectra data to a file in the given directory * @param directory where the Spectra will be save */ public void save(String directory){ if (isSaved() && this.directory.equals(directory)) return; this.directory=directory; String nameToSave=getNameToSave(); DataOutputStream file = null; try { file = new DataOutputStream(new BufferedOutputStream( new FileOutputStream(directory+nameToSave))); file.writeFloat(minEnergy); file.writeInt(minChannel); if (energySlope==null){ file.writeFloat(-1); } else{ file.writeFloat(energySlope); } file.writeInt(mapSizeX); file.writeInt(mapSizeY); adc.saveXYEListFile(file); } catch(FileNotFoundException e){ IJ.log(tr("**Error** "+ e.toString())); } catch(IOException e2){ IJ.log(tr("**Error** "+ e2.toString())); } try{ if (file!=null) { file.close(); } } catch(IOException e){ IJ.log(tr("**Error** "+ e.toString())); } } public String tr(String strToTranslate){ return MainFrame.tr(strToTranslate); } }