<commit_before>/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.micromanager.projector;

import ij.IJ;
import ij.WindowManager;
import ij.gui.ImageCanvas;
import ij.gui.ImageWindow;
import ij.gui.PointRoi;
import ij.gui.Roi;
import ij.plugin.filter.GaussianBlur;
import ij.plugin.frame.RoiManager;
import ij.process.ImageProcessor;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import javax.swing.JOptionPane;
import mmcorej.CMMCore;
import mmcorej.Configuration;
import mmcorej.TaggedImage;
import org.micromanager.api.AcquisitionEngine;
import org.micromanager.api.ScriptInterface;
import org.micromanager.utils.ImageUtils;
import org.micromanager.utils.JavaUtils;
import org.micromanager.utils.MathFunctions;
import org.micromanager.utils.ReportingUtils;

/**
 *
 * @author arthur
 */
public class ProjectorController {

   private String slm;
   private CMMCore mmc;
   private final ScriptInterface gui;
   private boolean imageOn_ = false;
   final private ProjectionDevice dev;
   private MouseListener pointAndShootMouseListener;
   private double pointAndShootInterval;
   private Roi[] individualRois_ = {};
   private int reps_ = 1;
   private long interval_us_ = 500000;
   private Map mapping_ = null;
   private String mappingNode_ = null;
   private String targetingChannel_;
       
   public ProjectorController(ScriptInterface app) {
      gui = app;
      mmc = app.getMMCore();
      String slm = mmc.getSLMDevice();
      String galvo = mmc.getGalvoDevice();
      
      if (slm.length() > 0) {
         dev = new SLM(mmc, 5);
      } else if (galvo.length() > 0) {
         dev = new Galvo(mmc);
      } else {
         dev = null;
      }

      loadMapping();
      pointAndShootMouseListener = setupPointAndShootMouseListener();
   }

   public boolean isSLM() {
       return (dev instanceof SLM);
   }
   
   
   public Point transform(Map<Polygon, AffineTransform> mapping, Point pt) {
       Set<Polygon> set = mapping.keySet();
       for (Polygon poly:set) {
           if (poly.contains(pt)) {
               return toIntPoint((Point2D.Double) mapping.get(poly).transform(toDoublePoint(pt), null));
           } 
       }
       return null;
   }
   
   public void calibrate() {
      final boolean liveModeRunning = gui.isLiveModeOn();
      gui.enableLiveMode(false);
      Thread th = new Thread("Projector calibration thread") {
         public void run() {
            Roi originalROI = IJ.getImage().getRoi();
            gui.snapSingleImage();
            
            AffineTransform firstApproxAffine = getFirstApproxTransform();
            
            HashMap<Polygon, AffineTransform> mapping = (HashMap<Polygon, AffineTransform>) getMapping(firstApproxAffine);
            //LocalWeightedMean lwm = multipleAffineTransforms(mapping_);
            //AffineTransform affineTransform = MathFunctions.generateAffineTransformFromPointPairs(mapping_);
            dev.turnOff();
            try {
               Thread.sleep(500);
            } catch (InterruptedException ex) {
               ReportingUtils.logError(ex);
            }
            saveMapping((HashMap<Polygon, AffineTransform>) mapping);
            //saveAffineTransform(affineTransform);
            gui.enableLiveMode(liveModeRunning);
            JOptionPane.showMessageDialog(IJ.getImage().getWindow(), "Calibration finished.");
            IJ.getImage().setRoi(originalROI);
         }
      };
      th.start();
   }

   private HashMap<Polygon, AffineTransform> loadMapping() {
       String nodeStr = getCalibrationNode().toString();
       if (mappingNode_ == null || !nodeStr.contentEquals(mappingNode_)) {
           mappingNode_ = nodeStr;
           mapping_ = (HashMap<Polygon, AffineTransform>) JavaUtils.getObjectFromPrefs(getCalibrationNode(), dev.getName(), new HashMap<Polygon, AffineTransform>());
       }
       return (HashMap<Polygon, AffineTransform>) mapping_;
   }
   
   private void saveMapping(HashMap<Polygon, AffineTransform> mapping) {
       JavaUtils.putObjectInPrefs(getCalibrationNode(), dev.getName(), mapping);
       mapping_ = mapping;
       mappingNode_ = getCalibrationNode().toString();
   }
   
   
   private Preferences getCalibrationNode() {
       return Preferences.userNodeForPackage(ProjectorPlugin.class)
               .node("calibration")
               .node(dev.getChannel())
               .node(mmc.getCameraDevice());
   }
   
   public void saveAffineTransform(AffineTransform affineTransform) {
       JavaUtils.putObjectInPrefs(getCalibrationNode(), dev.getName(), affineTransform);
   }

      public AffineTransform loadAffineTransform() {
      AffineTransform transform = (AffineTransform) JavaUtils.getObjectFromPrefs(getCalibrationNode(), dev.getName(), null);
      if (transform == null) {
         ReportingUtils.showError("The galvo has not been calibrated for the current settings.");
         return null;
      } else {
         return transform;
      }
   }
      
   public Point measureSpot(Point dmdPt) {
      try {
         mmc.snapImage();
         ImageProcessor proc1 = ImageUtils.makeProcessor(mmc.getTaggedImage());

         displaySpot(dmdPt.x, dmdPt.y, 500000);
         Thread.sleep(300);

         mmc.snapImage();
         TaggedImage taggedImage2 = mmc.getTaggedImage();
         ImageProcessor proc2 = ImageUtils.makeProcessor(taggedImage2);
         gui.displayImage(taggedImage2);

         Point maxPt = findPeak(ImageUtils.subtractImageProcessors(proc2, proc1));
         IJ.getImage().setRoi(new PointRoi(maxPt.x, maxPt.y));
         mmc.sleep(500);
         return maxPt;
      } catch (Exception e) {
         ReportingUtils.showError(e);
         return null;
      }
   }

   private Point findPeak(ImageProcessor proc) {
      ImageProcessor blurImage = ((ImageProcessor) proc.duplicate());
      blurImage.setRoi((Roi) null);
      GaussianBlur blur = new GaussianBlur();
      blur.blurGaussian(blurImage, 10, 10, 0.01);
      //gui.displayImage(blurImage.getPixels());
      Point x = ImageUtils.findMaxPixel(blurImage);
      x.translate(1, 1);
      return x;
   }
   
   public void mapSpot(Map spotMap, Point ptSLM) {
      Point2D.Double ptSLMDouble = new Point2D.Double(ptSLM.x, ptSLM.y);
      Point ptCam = measureSpot(ptSLM);
      Point2D.Double ptCamDouble = new Point2D.Double(ptCam.x, ptCam.y);
      spotMap.put(ptCamDouble, ptSLMDouble);
   }

   public void mapSpot(Map spotMap, Point2D.Double ptSLM) {
      mapSpot(spotMap, new Point((int) ptSLM.x, (int) ptSLM.y));
   }

   public AffineTransform getFirstApproxTransform() {
      double x = dev.getWidth() / 2;
      double y = dev.getHeight() / 2;

      int s = 50;
      Map spotMap = new HashMap();

      mapSpot(spotMap, new Point2D.Double(x, y));
      mapSpot(spotMap, new Point2D.Double(x, y + s));
      mapSpot(spotMap, new Point2D.Double(x + s, y));
      mapSpot(spotMap, new Point2D.Double(x, y - s));
      mapSpot(spotMap, new Point2D.Double(x - s, y));

      return MathFunctions.generateAffineTransformFromPointPairs(spotMap);
   }

   
   public static Point2D.Double clipPoint(Point2D.Double pt, Rectangle2D.Double rect) {
      return new Point2D.Double(
              MathFunctions.clip(pt.x, rect.x, rect.x + rect.width),
              MathFunctions.clip(pt.y, rect.y, rect.y + rect.height));
   }
   
   public static Point2D.Double transformAndClip(double x, double y, AffineTransform transform, Rectangle2D.Double clipRect) {
      return clipPoint((Point2D.Double) transform.transform(new Point2D.Double(x,y), null), clipRect);
   }
   
   public static void addVertex(Polygon poly, Point p) {
       poly.addPoint(p.x, p.y);
   }
   
   public static Point toIntPoint(Point2D.Double pt) {
       return new Point((int) pt.x, (int) pt.y);
   }
   
   public static Point2D.Double toDoublePoint(Point pt) {
       return new Point2D.Double(pt.x, pt.y);
   }
   
   public Map getMapping(AffineTransform firstApprox) {
      int devWidth = (int) dev.getWidth()-1;
      int devHeight = (int) dev.getHeight()-1;
     Point2D.Double camCorner1 = (Point2D.Double) firstApprox.transform(new Point2D.Double(0,0), null);
     Point2D.Double camCorner2 = (Point2D.Double) firstApprox.transform(new Point2D.Double((int )mmc.getImageWidth(), (int) mmc.getImageHeight()), null);
     int camLeft = Math.min((int) camCorner1.x, (int) camCorner2.x);
     int camRight = Math.max((int) camCorner1.x, (int) camCorner2.x);
     int camTop = Math.min((int) camCorner1.y, (int) camCorner2.y);
     int camBottom = Math.max((int) camCorner1.y, (int) camCorner2.y);
     int left = Math.max(camLeft, 0);
     int right = Math.min(camRight,devWidth);
     int top = Math.max(camTop, 0);
     int bottom = Math.min(camBottom,devHeight);
     int width = right-left;
     int height = bottom-top;
   
     
      int n = 8;
      Point2D.Double dmdPoint[][] = new Point2D.Double[1+n][1+n];
      Point2D.Double resultPoint[][] = new Point2D.Double[1+n][1+n];
      for (int i = 0; i <= n; ++i) {
        for (int j = 0; j <= n; ++j) {
           dmdPoint[i][j] = new Point2D.Double((int) left + i*width/n,(int) top + j*height/n);
           resultPoint[i][j] = toDoublePoint(measureSpot(toIntPoint(dmdPoint[i][j])));
        }
      }
      
      Map bigMap = new HashMap();
      for (int i=0; i<=n-1; ++i) {
          for (int j=0; j<=n-1; ++j) {
              Polygon poly = new Polygon();
              addVertex(poly, toIntPoint(resultPoint[i][j]));
              addVertex(poly, toIntPoint(resultPoint[i][j+1]));
              addVertex(poly, toIntPoint(resultPoint[i+1][j+1]));
              addVertex(poly, toIntPoint(resultPoint[i+1][j]));
              
              Map map = new HashMap();
              map.put(resultPoint[i][j], dmdPoint[i][j]);
              map.put(resultPoint[i][j+1], dmdPoint[i][j+1]);
              map.put(resultPoint[i+1][j], dmdPoint[i+1][j]);
              map.put(resultPoint[i+1][j+1], dmdPoint[i+1][j+1]);
              
              AffineTransform transform = MathFunctions.generateAffineTransformFromPointPairs(map);
              bigMap.put(poly, transform);
          } 
      }<commit_msg>use a different number to avoid clashes with firstapprox for now<commit_after>/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.micromanager.projector;

import ij.IJ;
import ij.WindowManager;
import ij.gui.ImageCanvas;
import ij.gui.ImageWindow;
import ij.gui.PointRoi;
import ij.gui.Roi;
import ij.plugin.filter.GaussianBlur;
import ij.plugin.frame.RoiManager;
import ij.process.ImageProcessor;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import javax.swing.JOptionPane;
import mmcorej.CMMCore;
import mmcorej.Configuration;
import mmcorej.TaggedImage;
import org.micromanager.api.AcquisitionEngine;
import org.micromanager.api.ScriptInterface;
import org.micromanager.utils.ImageUtils;
import org.micromanager.utils.JavaUtils;
import org.micromanager.utils.MathFunctions;
import org.micromanager.utils.ReportingUtils;

/**
 *
 * @author arthur
 */
public class ProjectorController {

   private String slm;
   private CMMCore mmc;
   private final ScriptInterface gui;
   private boolean imageOn_ = false;
   final private ProjectionDevice dev;
   private MouseListener pointAndShootMouseListener;
   private double pointAndShootInterval;
   private Roi[] individualRois_ = {};
   private int reps_ = 1;
   private long interval_us_ = 500000;
   private Map mapping_ = null;
   private String mappingNode_ = null;
   private String targetingChannel_;
       
   public ProjectorController(ScriptInterface app) {
      gui = app;
      mmc = app.getMMCore();
      String slm = mmc.getSLMDevice();
      String galvo = mmc.getGalvoDevice();
      
      if (slm.length() > 0) {
         dev = new SLM(mmc, 5);
      } else if (galvo.length() > 0) {
         dev = new Galvo(mmc);
      } else {
         dev = null;
      }

      loadMapping();
      pointAndShootMouseListener = setupPointAndShootMouseListener();
   }

   public boolean isSLM() {
       return (dev instanceof SLM);
   }
   
   
   public Point transform(Map<Polygon, AffineTransform> mapping, Point pt) {
       Set<Polygon> set = mapping.keySet();
       for (Polygon poly:set) {
           if (poly.contains(pt)) {
               return toIntPoint((Point2D.Double) mapping.get(poly).transform(toDoublePoint(pt), null));
           } 
       }
       return null;
   }
   
   public void calibrate() {
      final boolean liveModeRunning = gui.isLiveModeOn();
      gui.enableLiveMode(false);
      Thread th = new Thread("Projector calibration thread") {
         public void run() {
            Roi originalROI = IJ.getImage().getRoi();
            gui.snapSingleImage();
            
            AffineTransform firstApproxAffine = getFirstApproxTransform();
            
            HashMap<Polygon, AffineTransform> mapping = (HashMap<Polygon, AffineTransform>) getMapping(firstApproxAffine);
            //LocalWeightedMean lwm = multipleAffineTransforms(mapping_);
            //AffineTransform affineTransform = MathFunctions.generateAffineTransformFromPointPairs(mapping_);
            dev.turnOff();
            try {
               Thread.sleep(500);
            } catch (InterruptedException ex) {
               ReportingUtils.logError(ex);
            }
            saveMapping((HashMap<Polygon, AffineTransform>) mapping);
            //saveAffineTransform(affineTransform);
            gui.enableLiveMode(liveModeRunning);
            JOptionPane.showMessageDialog(IJ.getImage().getWindow(), "Calibration finished.");
            IJ.getImage().setRoi(originalROI);
         }
      };
      th.start();
   }

   private HashMap<Polygon, AffineTransform> loadMapping() {
       String nodeStr = getCalibrationNode().toString();
       if (mappingNode_ == null || !nodeStr.contentEquals(mappingNode_)) {
           mappingNode_ = nodeStr;
           mapping_ = (HashMap<Polygon, AffineTransform>) JavaUtils.getObjectFromPrefs(getCalibrationNode(), dev.getName(), new HashMap<Polygon, AffineTransform>());
       }
       return (HashMap<Polygon, AffineTransform>) mapping_;
   }
   
   private void saveMapping(HashMap<Polygon, AffineTransform> mapping) {
       JavaUtils.putObjectInPrefs(getCalibrationNode(), dev.getName(), mapping);
       mapping_ = mapping;
       mappingNode_ = getCalibrationNode().toString();
   }
   
   
   private Preferences getCalibrationNode() {
       return Preferences.userNodeForPackage(ProjectorPlugin.class)
               .node("calibration")
               .node(dev.getChannel())
               .node(mmc.getCameraDevice());
   }
   
   public void saveAffineTransform(AffineTransform affineTransform) {
       JavaUtils.putObjectInPrefs(getCalibrationNode(), dev.getName(), affineTransform);
   }

      public AffineTransform loadAffineTransform() {
      AffineTransform transform = (AffineTransform) JavaUtils.getObjectFromPrefs(getCalibrationNode(), dev.getName(), null);
      if (transform == null) {
         ReportingUtils.showError("The galvo has not been calibrated for the current settings.");
         return null;
      } else {
         return transform;
      }
   }
      
   public Point measureSpot(Point dmdPt) {
      try {
         mmc.snapImage();
         ImageProcessor proc1 = ImageUtils.makeProcessor(mmc.getTaggedImage());

         displaySpot(dmdPt.x, dmdPt.y, 500000);
         Thread.sleep(300);

         mmc.snapImage();
         TaggedImage taggedImage2 = mmc.getTaggedImage();
         ImageProcessor proc2 = ImageUtils.makeProcessor(taggedImage2);
         gui.displayImage(taggedImage2);

         Point maxPt = findPeak(ImageUtils.subtractImageProcessors(proc2, proc1));
         IJ.getImage().setRoi(new PointRoi(maxPt.x, maxPt.y));
         mmc.sleep(500);
         return maxPt;
      } catch (Exception e) {
         ReportingUtils.showError(e);
         return null;
      }
   }

   private Point findPeak(ImageProcessor proc) {
      ImageProcessor blurImage = ((ImageProcessor) proc.duplicate());
      blurImage.setRoi((Roi) null);
      GaussianBlur blur = new GaussianBlur();
      blur.blurGaussian(blurImage, 10, 10, 0.01);
      //gui.displayImage(blurImage.getPixels());
      Point x = ImageUtils.findMaxPixel(blurImage);
      x.translate(1, 1);
      return x;
   }
   
   public void mapSpot(Map spotMap, Point ptSLM) {
      Point2D.Double ptSLMDouble = new Point2D.Double(ptSLM.x, ptSLM.y);
      Point ptCam = measureSpot(ptSLM);
      Point2D.Double ptCamDouble = new Point2D.Double(ptCam.x, ptCam.y);
      spotMap.put(ptCamDouble, ptSLMDouble);
   }

   public void mapSpot(Map spotMap, Point2D.Double ptSLM) {
      mapSpot(spotMap, new Point((int) ptSLM.x, (int) ptSLM.y));
   }

   public AffineTransform getFirstApproxTransform() {
      double x = dev.getWidth() / 2;
      double y = dev.getHeight() / 2;

      int s = 50;
      Map spotMap = new HashMap();

      mapSpot(spotMap, new Point2D.Double(x, y));
      mapSpot(spotMap, new Point2D.Double(x, y + s));
      mapSpot(spotMap, new Point2D.Double(x + s, y));
      mapSpot(spotMap, new Point2D.Double(x, y - s));
      mapSpot(spotMap, new Point2D.Double(x - s, y));

      return MathFunctions.generateAffineTransformFromPointPairs(spotMap);
   }

   
   public static Point2D.Double clipPoint(Point2D.Double pt, Rectangle2D.Double rect) {
      return new Point2D.Double(
              MathFunctions.clip(pt.x, rect.x, rect.x + rect.width),
              MathFunctions.clip(pt.y, rect.y, rect.y + rect.height));
   }
   
   public static Point2D.Double transformAndClip(double x, double y, AffineTransform transform, Rectangle2D.Double clipRect) {
      return clipPoint((Point2D.Double) transform.transform(new Point2D.Double(x,y), null), clipRect);
   }
   
   public static void addVertex(Polygon poly, Point p) {
       poly.addPoint(p.x, p.y);
   }
   
   public static Point toIntPoint(Point2D.Double pt) {
       return new Point((int) pt.x, (int) pt.y);
   }
   
   public static Point2D.Double toDoublePoint(Point pt) {
       return new Point2D.Double(pt.x, pt.y);
   }
   
   public Map getMapping(AffineTransform firstApprox) {
      int devWidth = (int) dev.getWidth()-1;
      int devHeight = (int) dev.getHeight()-1;
     Point2D.Double camCorner1 = (Point2D.Double) firstApprox.transform(new Point2D.Double(0,0), null);
     Point2D.Double camCorner2 = (Point2D.Double) firstApprox.transform(new Point2D.Double((int )mmc.getImageWidth(), (int) mmc.getImageHeight()), null);
     int camLeft = Math.min((int) camCorner1.x, (int) camCorner2.x);
     int camRight = Math.max((int) camCorner1.x, (int) camCorner2.x);
     int camTop = Math.min((int) camCorner1.y, (int) camCorner2.y);
     int camBottom = Math.max((int) camCorner1.y, (int) camCorner2.y);
     int left = Math.max(camLeft, 0);
     int right = Math.min(camRight,devWidth);
     int top = Math.max(camTop, 0);
     int bottom = Math.min(camBottom,devHeight);
     int width = right-left;
     int height = bottom-top;
   
     
      int n = 7;
      Point2D.Double dmdPoint[][] = new Point2D.Double[1+n][1+n];
      Point2D.Double resultPoint[][] = new Point2D.Double[1+n][1+n];
      for (int i = 0; i <= n; ++i) {
        for (int j = 0; j <= n; ++j) {
           dmdPoint[i][j] = new Point2D.Double((int) left + i*width/n,(int) top + j*height/n);
           resultPoint[i][j] = toDoublePoint(measureSpot(toIntPoint(dmdPoint[i][j])));
        }
      }
      
      Map bigMap = new HashMap();
      for (int i=0; i<=n-1; ++i) {
          for (int j=0; j<=n-1; ++j) {
              Polygon poly = new Polygon();
              addVertex(poly, toIntPoint(resultPoint[i][j]));
              addVertex(poly, toIntPoint(resultPoint[i][j+1]));
              addVertex(poly, toIntPoint(resultPoint[i+1][j+1]));
              addVertex(poly, toIntPoint(resultPoint[i+1][j]));
              
              Map map = new HashMap();
              map.put(resultPoint[i][j], dmdPoint[i][j]);
              map.put(resultPoint[i][j+1], dmdPoint[i][j+1]);
              map.put(resultPoint[i+1][j], dmdPoint[i+1][j]);
              map.put(resultPoint[i+1][j+1], dmdPoint[i+1][j+1]);
              
              AffineTransform transform = MathFunctions.generateAffineTransformFromPointPairs(map);
              bigMap.put(poly, transform);
          } 
      }
{'fraction_non_alphanumeric': 0.11997543809928676, 'fraction_numerical': 0.011241793018752066, 'mean_word_length': 2.869859257905319, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '18669', 'n_tokens_mistral': 6777, 'n_tokens_neox': 6576, 'n_words': 1364}