/** * */ package analyze; import java.awt.*; import java.awt.geom.*; import javax.swing.*; import util.Defaults; //FrontEnd Imports import io.*; import cortexrules.*; import model.*; /** * @author John Krantz * */ public class PlotOutput extends JPanel { static final long serialVersionUID = 0; // constants to identify type of analysis // when the current cell being analyzed in a simple cell public final static int ANA_TYPE = 0; public final static int POSPEAK = 1; public final static int POSNEGPEAK = 2; public final static int PEAKANDACT = 3; public final static int MAX_ACT = 4; public final static int MIN_ACT = 5; public final static int MEAN_ACT = 6; public final static int SINGLE_ORIENT = 7; public final static int CHANGE_DIR = 8; public final static int CHANGE_AND_ACT = 9; public final static int CHANGE_AND_ACT2 = 10; public final static int CHANGE_AND_ACT3 = 11; public final static int ASYM_CHANGE_ACT = 12; public final static int ASYM_CHANGE_ACT2 = 13; public final static int MIN_DIR_TYPE = CHANGE_DIR; private int type = POSPEAK; public final static String [] ANANAMES = {"Gabor Display Type", "Positive Peaks Only", "Peak Positive or Negative", "Peaks and Activity","Max Activity", "Min Activity","Mean Activity", "Single Orientation","Change Direction", "Activity then Change Direction", "Change Direction then Activity", "Change Direction AND Activity", "Act/Asymmtrical Change Dir", "Asymmetrical Change Dir/Act"}; // Cell to model private int currentCell = Defaults.PLOT_CELL; // if plotting only one orientation, private int currentOrient = Defaults.DFLT_ORIENT; // DOG channel to plot private int currentChannel = Defaults.DOG_CHANNEL; // data arrays private double [][][] cellResp; // input array // with the activity of all cells private int numOrient = Defaults.NUM_ORIENT; private double [][] peakOrients; // peak orientations // to plot private double [][] rangeAct; // array which // contains the range of activity from the array private Rectangle2D [][] rfArray; // array of plotting // rectangles // data analysis objects and parameters private double threshold = 100; // Activity must exceed this value for there to be a peak orientation private boolean threshAuto = false; // if true, tresholds below are determined automatically private OptimizeThresholds ot = new OptimizeThresholds(); private int optType = OptimizeThresholds.FIT_TO_RANGE; public final static int FIT_TO_RANGE = OptimizeThresholds.FIT_TO_RANGE; public final static int COMPETATIVE = OptimizeThresholds.COMPETATIVE; public final static String [] OPT_TYPE_NAMES = OptimizeThresholds.OPT_TYPE_NAMES; private double difToRangeWgt = 0.3; private boolean weightLines = true; private boolean weightFromThresh = true; private boolean weightAngle = false; private double rangeThresh = 100; private double changeThresh = 0.1; private int range = 1; // Range for finding minima and maxima // analysis values private Summarize sum = new Summarize(); // drawing flags private boolean showGrid = true; // scaling parameter private double scale = 1.0; // allow image to be changes in size // 1.0 means that the image is the best fit to the screen. private Point start = new Point (0,0); // upper left corner of the drawing private boolean center = true; // set if want to center the image // will always center if image is smaller than screen. // Assistant Plotting objects private PlotLines plotLines = new PlotLines(); private PlotActivity plotAct = new PlotActivity(); private PlotChangeDir plotChange = new PlotChangeDir(); // DOG plotting object private PlotDOG plotDOG = new PlotDOG(); // model output for the cortex that can then be // further analyzed private double [][][] cortex; // on first index position 0 is angles // position 1 is activity mean or weight of angle // then x and then y public static final double NO_ANGLE = 500; public static final int ANGLE = 0; public static final int ACTIVITY = 1; public static final double BREAK = -999; public static final int NEG_OFFSET = -360; // // offset for angles from negative peaks or minimal // cortex modeling objects private boolean drawSegs = false; private Rules rules = new Rules(); public static final String [] RULE_DESC = Rules.RULE_DESC; private SaveFile save; /** * */ public PlotOutput() { super(); peakOrients = null; } public void paint(Graphics g){ Graphics2D g2d = (Graphics2D)g; // clear the screen white as a background g2d.setPaint(Color.black); int width = getWidth(); int height = this.getHeight(); g2d.fill(new Rectangle(0,0,width,height)); if (peakOrients != null){ // determine limits for weighting lines double maxPos = sum.getMaxAct(); double maxNeg = sum.getMaxNegAct(); // initialize the array of plotting rectangles rfArray = new Rectangle2D[cellResp.length][cellResp[0].length]; // determine box size, want steps // the same size in x and y so need smallest int yStep = height/peakOrients.length; int xStep = width/peakOrients[0].length; // use the smaller int step = yStep; if (xStep < yStep) step = xStep; // scale the step step = (int)Math.round((double)step*scale); // determine the upper left corner. if appropriate double top = start.y; double left = start.x; if (center | scale <= 1.0){ top = (double)height/2.0-((double)step* (double)peakOrients.length/2.0); left = (double)width/2.0-((double)step* (double)peakOrients[0].length/2.0); start.x = (int)left; start.y = (int)top; } start.setLocation(new Point((int)left,(int)top)); // outline the drawing area in light blue g2d.setPaint(new Color(128,128,255)); g2d.draw(new Rectangle2D.Double(left,top, step*peakOrients[0].length, step*peakOrients.length)); // setting for change in directions plotChange.setWeightLines(weightLines); plotChange.setWeightAngles(weightAngle); // now draw the lines if (currentCell == ModelConst.DOG_CELL){ // plot dog cells // create the RF array // and extract the 2d retinal response array double [][] output = new double[cellResp.length] [cellResp[0].length]; for (int y = 0; y < peakOrients.length; y ++){ for (int x = 0; x < peakOrients[0].length; x ++){ output[y][x]=cellResp[y][x][currentChannel]; rfArray[y][x] = new Rectangle2D.Double( left+x*step,top+y*step, step,step); } } plotDOG.plotDOG(g2d, rfArray, output); } else { // plot simple cells // declare the model output array cortex = new double[2][cellResp[0].length] [cellResp.length+1]; for (int y = 0; y < peakOrients.length; y ++){ for (int x = 0; x < peakOrients[0].length; x ++){ // only draw if orientation is above threshold // draw the grid cell if wanted rfArray[y][x] = new Rectangle2D.Double( left+x*step,top+y*step, step,step); // if the thresholds are determined automatically // do it here if (threshAuto){ double thresh [] = new double [3]; switch (optType){ case OptimizeThresholds.FIT_TO_RANGE : thresh = ot.fitToRange(sum.getPointSummary(y,x), 1, 100, 0.1); break; case OptimizeThresholds.COMPETATIVE : if (x >=range & x < rfArray[0].length-range & y >= range & y < rfArray.length-range){ thresh = ot.competRangeDiff(sum, x, y, maxPos,maxNeg, difToRangeWgt); } } if (thresh[OptimizeThresholds.THRESHOLD] != -1){ threshold = thresh[OptimizeThresholds.THRESHOLD]; } rangeThresh = thresh[OptimizeThresholds.RANGE_THRESHOLD]; changeThresh = thresh[OptimizeThresholds.DIFF_THRESHOLD]; } // determine if value is below threshold double val = sum.getPointSummary(y,x) [Summarize.MAX]; double nVal = sum.getPointSummary(y,x) [Summarize.MIN]; if (Math.abs(nVal) > val){ val = Math.abs(nVal); } // place a no angle code as default cortex[ANGLE][x][y] = NO_ANGLE; if (val >= threshold){ // now plot // first send to the plot activity // options double [] hold = new double [2]; switch (type) { case MAX_ACT : // plot various activity leves case MIN_ACT : case MEAN_ACT : hold[ACTIVITY] = plotAct.plotActivity(type, g2d, rfArray[y][x], sum.getPointSummary(y, x), maxPos, maxNeg); hold[ANGLE] = NO_ANGLE; break; case SINGLE_ORIENT: if (currentOrient >=cellResp[0][0].length){ currentOrient = 0; } hold[ACTIVITY] = plotAct.plotActivity(g2d, rfArray[y][x], cellResp[y][x][currentOrient], maxPos, maxNeg); hold[ANGLE] = NO_ANGLE; break; case CHANGE_DIR : // plot change directions hold = plotChange.plotActivity(g2d, rfArray, cellResp, sum, y, x, range, maxPos, maxNeg, changeThresh); break; case PEAKANDACT : if (rangeAct[y][x] <=rangeThresh){ hold[ACTIVITY]= plotAct.plotActivity(PlotOutput.MEAN_ACT, g2d, rfArray[y][x], sum.getPointSummary(y, x), maxPos, maxNeg); hold[ANGLE]=NO_ANGLE; } // end if range to small else { if (peakOrients[y][x] >=0){ hold = plotLines.plotLines(g2d, rfArray[y][x], sum.getPointSummary(y, x)[Summarize.MAX], peakOrients[y][x], maxPos, maxNeg, weightLines, threshold, weightFromThresh); } // end positive activity else { hold = plotLines.plotLines(g2d, rfArray[y][x], sum.getPointSummary(y, x)[Summarize.MIN], peakOrients[y][x], maxPos, maxNeg, weightLines, threshold, weightFromThresh); } // end negative activity } break; // eng PEAKANDACT case CHANGE_AND_ACT : case ASYM_CHANGE_ACT : if (type == CHANGE_AND_ACT){ plotChange.setAllowAsymmtrical(false); } else { plotChange.setAllowAsymmtrical(true); } if (rangeAct[y][x] <=rangeThresh){ hold[ACTIVITY]= plotAct.plotActivity(PlotOutput.MEAN_ACT, g2d, rfArray[y][x], sum.getPointSummary(y, x), maxPos, maxNeg); hold[ANGLE]=NO_ANGLE; } // end if range to small else { hold= plotChange.plotActivity(g2d, rfArray, cellResp, sum, y, x, range, maxPos, maxNeg, changeThresh); cortex[ACTIVITY][x][y] = hold[ACTIVITY]; cortex[ANGLE][x][y] = hold[ANGLE]; } break; // end CHANGE_AND_ACT case CHANGE_AND_ACT2 : case ASYM_CHANGE_ACT2 : if (type == CHANGE_AND_ACT2){ plotChange.setAllowAsymmtrical(false); } else { plotChange.setAllowAsymmtrical(true); } hold= plotChange.plotActivity(g2d, rfArray, cellResp, sum, y, x, range, maxPos, maxNeg, changeThresh); if (!plotChange.getPosPeak() & !plotChange.getNegPeak() & rangeAct[y][x] <=rangeThresh){ hold[ACTIVITY]= plotAct.plotActivity(PlotOutput.MEAN_ACT, g2d, rfArray[y][x], sum.getPointSummary(y, x), maxPos, maxNeg); hold[ANGLE]=NO_ANGLE; } break; // end CHANGE_AND_ACT case CHANGE_AND_ACT3 : if (type == CHANGE_AND_ACT3){ plotChange.setAllowAsymmtrical(false); } else { plotChange.setAllowAsymmtrical(true); } hold[ACTIVITY]= plotAct.plotActivity(PlotOutput.MEAN_ACT, g2d, rfArray[y][x], sum.getPointSummary(y, x), maxPos, maxNeg); hold= plotChange.plotActivity(g2d, rfArray, cellResp, sum, y, x, range, maxPos, maxNeg, changeThresh); break; // end CHANGE_AND_ACT3 case POSPEAK: hold= plotLines.plotLines(g2d, rfArray[y][x], sum.getPointSummary(y, x)[Summarize.MAX], peakOrients[y][x], maxPos, maxNeg, weightLines, threshold, weightFromThresh); break; // end pospeak case POSNEGPEAK : if (peakOrients[y][x] >=0){ hold= plotLines.plotLines(g2d, rfArray[y][x], sum.getPointSummary(y, x)[Summarize.MAX], peakOrients[y][x], maxPos, maxNeg, weightLines, threshold, weightFromThresh); } // end positive activity else { hold= plotLines.plotLines(g2d, rfArray[y][x], sum.getPointSummary(y, x)[Summarize.MIN], peakOrients[y][x], maxPos, maxNeg, weightLines, threshold, weightFromThresh); } // end negative activity break; // end posnegpeak } // end switch on plot type. cortex[ACTIVITY][x][y] = hold[ACTIVITY]; cortex[ANGLE][x][y] = hold[ANGLE]; }// end if val > threshold } // end for x } // end for y } // end plot simple cells if (showGrid){ g2d.setPaint(Color.darkGray); g2d.setStroke(new BasicStroke(1.0f)); for (int y = 0; y < rfArray.length; y ++){ for (int x = 0; x < rfArray[0].length; x ++){ g2d.draw(rfArray[y][x]); } } } // end if showGrid // do any extra text or indicators on screen // Angle if just plotting one orientation if (type == SINGLE_ORIENT & currentCell == ModelConst.SIMPLE_CELL){ g2d.setPaint(Color.YELLOW); String or = ""+(double)currentOrient* 180.0/Defaults.NUM_OR_DBL; g2d.setFont(new Font("SansSerif",Font.BOLD,20)); FontMetrics fm = g2d.getFontMetrics(); g2d.drawString("Orientation:",5,50); g2d.drawString(or, 10, 50+fm.getHeight()); } // now draw the cortex processed rules if (drawSegs & currentCell == ModelConst.SIMPLE_CELL){ float sW = (float)rfArray[0][0].getWidth()*0.8f; g2d.setStroke(new BasicStroke(sW)); GeneralPath [][] objects = rules.getAllShapes( cortex, rfArray); int totalObj = 0; if (objects != null){ for (int j = 0; j < 2; j ++){ if (objects[j] != null){ totalObj += objects[j].length; for (int i = 0; i < objects[j].length; i ++){ // set the paint for each color float clr = (float)i/(float)objects[j].length; if (j == Rules.POS){ g2d.setPaint(new Color(clr,1.0f-clr/2.0f,0.2f,0.4f)); } if (j == Rules.NEG){ g2d.setPaint(new Color(1.0f,0.3f,clr,0.4f)); } if (objects[j][i] != null) g2d.draw(objects[j][i]); } } } // debug ? g2d.setPaint(Color.orange); g2d.setFont(new Font("SansSerif",Font.BOLD,24)); g2d.drawString(""+totalObj+" objects",50,100); } else { // debug? g2d.setPaint(Color.orange); g2d.setFont(new Font("SansSerif",Font.BOLD,24)); g2d.drawString("0 objects",50,100); } } } // if there is a data array done. } // end paint // plot methods /** * Do a simple finding of the orientation with * peak activity */ public Rectangle2D[][] plotSimplePeaks(){ type = POSPEAK; getPeaksAndAct(); // now repaint the screen with this data repaint(); return getRFArray(); } /** * Find the orientation of the greatest activity, * excitatory (positive) or inhibitory (negative) * in graph positive will be plotted in black and * inhibitory will be plotted in red */ public Rectangle2D [][] plotPosNegPeaks(){ type = POSNEGPEAK; getPeaksAndAct(); // now repaint the screen with this data repaint(); return getRFArray(); } /** * Find the orientation of the greatest activity, * excitatory (positive) or inhibitory (negative) * in graph positive will be plotted in black and * inhibitory will be plotted in red also if * range at point is lower than threshold, only the * intensity will be indicated */ public Rectangle2D [][] plotSummary(){ type = PEAKANDACT; getPeaksAndAct(); repaint(); // now repaint the screen with this data return getRFArray(); } /** * Set the type of plot and then do it. * @param int typePlot the type of plot to do. */ public Rectangle2D [][] plot(int typePlot){ type = typePlot; getPeaksAndAct(); repaint(); // now repaint the screen with this data return getRFArray(); } /** * get the peaks and range arrays filled for each * condition. * */ private void getPeaksAndAct(){ peakOrients = new double [cellResp.length] [cellResp[0].length]; rangeAct = new double[cellResp.length] [cellResp[0].length]; sum.setColumns(cellResp); for (int y = 0; y < cellResp.length; y ++){ for (int x = 0; x < cellResp[0].length; x ++){ // get the range for the point rangeAct[y][x] = sum.getPointSummary(y,x)[Summarize.RANGE]; // first find the maximum firing rate at this // location on the cortex double max = 0; for (int c = 0; c < cellResp[0][0].length; c ++){ if (max < Math.abs(cellResp[y][x][c])) max = Math.abs(cellResp[y][x][c]); } double orientVal = 0; // find the orientation for (int c = 0; c < cellResp[0][0].length; c ++){ if (max == cellResp[y][x][c]){ orientVal = c; } if (max == -cellResp[y][x][c] & type > POSPEAK){ orientVal =(c+NEG_OFFSET/10); } } // convert to angle in degrees and // add to plot array peakOrients[y][x] = orientVal * 180.0 / (double)numOrient; } } } /** * Save the image plotted data for the simple cell, * the first array is the orientation plotted * the second is some measure of activity * @param directory * @param summary * @return */ public String SaveCortexData(String directory, String summary){ save = null; System.gc(); save = new SaveFile(); // add dividers for (int i = 0; i < cortex[0].length; i ++){ cortex[ANGLE][i][cortex[0][0].length-1] = BREAK; cortex[ACTIVITY][i][cortex[0][0].length-1] = BREAK; } save.setFlipVertical(false); directory = save.saveAs(cortex, summary, directory,".ctx"); return directory; } /** * Save the processed data array as a space delmited file * @param directory the default directory * @param summary what type of data to be saved * @return the directory saved it */ public String saveProcessedData(String directory, String summary){ save = null; System.gc(); save = new SaveFile(); save.setFlipVertical(false); String ext = ".gbr"; if (currentCell == ModelConst.DOG_CELL){ directory = save.saveDOG(cellResp, summary, directory); } else { directory = save.saveGabor(cellResp, summary, directory, ext); } return directory; } // set methods /** * Set the column data * @param double [][][] cols the column data */ public void setCellData(double [][][] cells){ if (cellResp != null){ if (cells.length != cellResp.length | cells[0].length != cellResp[0].length){ rfArray = null; } } cellResp = cells; } public void setCurrentModelCell(int cellType){ currentCell = cellType; } /** * Allow the thresholds to be determined automatically and dynamically. * * @param boolean b true to set automatically */ public void setThresholdsAutomatically(boolean b){ threshAuto = b; } /** * Set the noise threshold. All activity below this value * is ignored * @param double thresh the limit */ public void setThreshold(double thresh){ threshold = (thresh >= 0 ? thresh : threshold); } /** * Set the range threshold. All activity below this value * is not thought to give adequat orientation information * @param double thresh the limit */ public void setRangeThreshold(double thresh){ rangeThresh = (thresh >= 0 ? thresh : rangeThresh); } /** * Set the Change threshold. All activity below this value * is not thought be a real change in direction * @param double thresh the limit */ public void setChangeThreshold(double thresh){ changeThresh = (thresh >= 0 ? thresh : changeThresh); } /** * Set the Change Range. How far around location to determine * the maxima and minima * @param int rnge the range */ public void setChangeRange(int rnge){ range = (rnge > 0 ? rnge : range); } /** * Determine if the grid is to be shown and repaint afterwards * @param boolean b flag, true to show. */ public void setShowGrid(boolean b){ showGrid = b; repaint(); } /** * flag to indicate if line is drawn using weights * @param boolean b true to weight lines */ public void setWeightLines(boolean b){ weightLines = b; } /** * flag to indicate where the weighting starts from * true = weight starts from threshold * false = weight starts from 0 * @param boolean b */ public void setWeightFromThresh(boolean b){ weightFromThresh = b; } /** * flag to indicate if the drawn angle is determined from * the weighted threshold. * @param boolean b flag, true to use weighted sum. */ public void setWeightAngles(boolean b){ weightAngle = b; } /** * Change the drawing scale so that people can zoom * in to a region of the output * @param double sc - the scale relative to the best * fit to the screen (1.0). */ public void setDrawScale(double sc){ scale = (sc > 0 ? sc : scale); } /** * set true to center an image. *@param boolean b flag true to center the image. */ public void setCenter(boolean b){ center = b; } public void moveFigure(int xMove, int yMove){ start.x += xMove; start.y += yMove; center = false; } public void setOptimizeThresholdType(int opt_type){ optType = opt_type; } public void setDifToRangeWeight(double weight){ difToRangeWgt = weight; } public void setDrawSegmentedObjects(boolean b){ drawSegs = b; } public void setSegmetRule(int r){ rules.setRule(r); } public void setCurrentDOGChannel(int ch){ currentChannel = ch; } public void setNumOrientation(int orients){ numOrient = orients; } public void setCurrentOrientation(int orient){ currentOrient = orient >= 0 & orient < Defaults.NUM_ORIENT ? orient: currentOrient; } // get methods /** * Return the plotting array to allow summarizing * of point * @return Rectangle2D [][] the receptive field array */ public Rectangle2D[][] getRFArray(){ return rfArray; } /** * Returns the type of plotting being done for * the simple cell arrays * @return int the analysis type */ public int getSimplePlotType() { return type; } /** * returns the current drawing scale of the image * @return double drawing scale. */ public double getDrawScale() { return scale; } /** * returns the current minimum activity level that can register * @return double threshold */ public double getThreshold() { return threshold; } /** * returns the current threshold below which the average activity * is plotted * @return double rangeThresh */ public double getRangeThresh() { return rangeThresh; } /** * returns the threshold for a change to be counted as a * change in direction * @return double changeThresh */ public double getChangeThresh() { return changeThresh; } public boolean getShowGrid() { return showGrid; } public boolean getWeightLines() { return weightLines; } public boolean getWeightFromThresh() {return weightFromThresh; } public boolean getWeightAngles() { return weightAngle; } public boolean getThresholdsAutomatically() { return threshAuto; } public int getThreshOptType() { return optType; } public double getDifToRangeWeight() { return difToRangeWgt; } public double [][][] getCortexData() { return cortex; } public int getCurrentOrientation() { return currentOrient; } }