/* * Copyright (c) 2014 Paul Scherrer Institute. All rights reserved. */ import ch.psi.pshell.core.Controller; import java.io.File; import java.io.IOException; import java.nio.file.Paths; import javax.swing.DefaultComboBoxModel; import ch.psi.pshell.ui.Panel; import ch.psi.pshell.imaging.ImageListener; import ch.psi.utils.State; import ch.psi.utils.IO; import ch.psi.utils.swing.SwingUtils; import ch.psi.utils.swing.ConfigDialog; import ch.psi.utils.swing.StandardDialog; import ch.psi.utils.swing.TextEditor; import ch.psi.pshell.epics.PsiCamera; import ch.psi.pshell.core.JsonSerializer; import ch.psi.pshell.device.Device; import ch.psi.pshell.epics.ArraySource; import ch.psi.pshell.epics.ChannelDouble; import ch.psi.pshell.epics.ChannelDoubleArray; import ch.psi.pshell.epics.ChannelInteger; import ch.psi.pshell.epics.ChannelIntegerArray; import ch.psi.pshell.ui.App; import ch.psi.pshell.imaging.Data; import ch.psi.pshell.imaging.Overlay; import ch.psi.pshell.imaging.Overlays; import ch.psi.pshell.imaging.Pen; import ch.psi.pshell.imaging.Renderer; import ch.psi.pshell.imaging.Renderer.Profile; import ch.psi.pshell.imaging.RendererListener; import ch.psi.pshell.imaging.Source; import ch.psi.pshell.imaging.Utils; import ch.psi.pshell.scripting.InterpreterResult; import ch.psi.pshell.scripting.ScriptManager; import ch.psi.utils.Arr; import ch.psi.utils.ArrayProperties; import ch.psi.utils.Convert; import ch.psi.utils.swing.Editor.EditorDialog; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Frame; import java.awt.image.BufferedImage; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.IntStream; import javax.script.ScriptException; import static javax.swing.WindowConstants.DISPOSE_ON_CLOSE; import org.apache.commons.math3.analysis.function.Gaussian; import org.apache.commons.math3.fitting.GaussianCurveFitter; import org.apache.commons.math3.fitting.WeightedObservedPoint; /** * */ public class Cameras extends Panel { public Cameras() { initComponents(); renderer.setPersistenceFile(Paths.get(getController().getSetup().getContextPath(), "Renderer_Cameras.bin")); setPersistedComponents(new Component[]{checkCamtool}); comboCameras.setEnabled(false); if (App.hasArgument("ct")) { checkCamtool.setSelected(! App.getArgumentValue("ct").equals("0")); } } final String configFolder = "/afs/psi.ch/intranet/SF/Applications/config/camtool"; File[] cameraConfigFiles = new File[0]; ArraySource camera; String cameraName; String cameraConfigJson; CameraConfig config; //Overridable callbacks @Override public void onInitialize(int runCount) { comboCameras.setEnabled(false); if (App.hasArgument("s")){ renderer.setDevice((Source)getDevice("image")); renderer.setAutoScroll(true); ((Source)getDevice("image")).addListener(new ImageListener() { @Override public void onImage(Object o, BufferedImage bi, Data data) { if (bi == null) { fitOv = null; } else { Overlay[] profile = renderer.getProfileOverlays(); profile = ((profile != null) && (profile.length==4)) ? getFitOverlays(bi) : null; renderer.updateOverlays(profile, fitOv); fitOv = profile; } } @Override public void onError(Object o, Exception excptn) { } } ); } else { cameraConfigFiles = IO.listFiles(configFolder, new String[]{"json"}); Arrays.sort(cameraConfigFiles, (a, b) -> a.compareTo(b)); DefaultComboBoxModel model = new DefaultComboBoxModel(); for (File file : cameraConfigFiles) { String prefix = IO.getPrefix(file); if (!prefix.startsWith("#")) { model.addElement(prefix); } } comboCameras.setModel(model); comboCameras.setEnabled(true); comboCameras.setSelectedItem(-1); if (model.getSize() > 0) { try { //setCamera((String)comboCameras.getSelectedItem()); if (App.hasArgument("cam")) { comboCameras.setSelectedItem(App.getArgumentValue("cam")); } } catch (Exception ex) { ex.printStackTrace(); } } } startTimer(2000); } @Override public void onStateChange(State state, State former) { } @Override public void onExecutedFile(String fileName, Object result) { } //Callback to perform update - in event thread @Override protected void doUpdate() { } public static class CameraConfig { public HashMap kwargs; public ArrayList args; public ArrayList links; public String type; public HashMap state; public CameraConfig image_source; public HashMap subcomponents; public ArrayList getCalibration() { return (ArrayList) state.get("calibration"); } public Double getCalibrationHeight() { return (Double) state.get("calibration_height"); } public Double getCalibrationWidth() { return (Double) state.get("calibration_width"); } public Double getCalibrationHorizontalAngle() { return (Double) state.get("calibration_horizontal_angle"); } public Double getCalibrationVerticalAngle() { return (Double) state.get("calibration_vertical_angle"); } public Boolean getMirrorX() { return (Boolean) state.get("mirror_x"); } public Boolean getMirrorY() { return (Boolean) state.get("mirror_y"); } public Integer getRotate() { return (Integer) state.get("rotate"); } public ArrayList getOrigin() { return (ArrayList) state.get("origin"); } public ArrayList getRoi() { return (ArrayList) state.get("roi"); } public Boolean getRoiEnable() { if ((state.get("roi_enable") == null) || (state.get("roi") == null)) { return false; } return (Boolean) state.get("roi_enable"); } public ArrayList getUnitSize() { return (ArrayList) state.get("unit_size"); } public Integer getRun() { return (Integer) state.get("run"); } } Overlay[] fitOv; void setCamera(String cameraName) throws IOException, InterruptedException { System.out.println("Setting camera: " + cameraName); if (camera != null) { camera.close(); camera = null; renderer.setDevice(null); renderer.clear(); } try { Path configFile = Paths.get(configFolder, cameraName + ".json"); cameraConfigJson = new String(Files.readAllBytes(configFile)); this.cameraName = cameraName; //SwingUtils.showMessage(null, "", json); try { if (checkCamtool.isSelected()) { camera = new Camtool("CurrentCamera", cameraName, false, true); } else { camera = new PsiCamera("CurrentCamera", cameraName); config = (CameraConfig) JsonSerializer.decode(cameraConfigJson, CameraConfig.class); camera.getConfig().flipHorizontally = config.getMirrorX(); camera.getConfig().flipVertically = config.getMirrorY(); camera.getConfig().rotation = config.getRotate(); camera.getConfig().rotationCrop = true; camera.getConfig().roiX = config.getRoiEnable() ? config.getRoi().get(0) : 0; camera.getConfig().roiY = config.getRoiEnable() ? config.getRoi().get(1) : 0; camera.getConfig().roiWidth = config.getRoiEnable() ? config.getRoi().get(2) : -1; camera.getConfig().roiHeight = config.getRoiEnable() ? config.getRoi().get(3) : -1; //camera.getConfig().spatialCalOffsetX = (config.getOrigin()!=null) ? config.getOrigin().get(0) : Double.NaN; //camera.getConfig().spatialCalOffsetY = (config.getOrigin()!=null) ? config.getOrigin().get(1) : Double.NaN; //camera.getConfig().spatialCalScaleX = (config.getUnitSize()!=null) && (config.getOrigin().get(0)!=0) ? 1.0/(config.getOrigin().get(0)*1000) : Double.NaN; //camera.getConfig().spatialCalScaleY= (config.getUnitSize()!=null) && (config.getOrigin().get(1)!=0) ? 1.0/(config.getOrigin().get(1)*1000) : Double.NaN; ArrayList calibration = config.getCalibration(); if ((calibration != null) && (calibration.size() != 4)) { calibration = null; } camera.getConfig().spatialCalOffsetX = (calibration != null) ? -calibration.get(2) : Double.NaN; camera.getConfig().spatialCalOffsetY = (calibration != null) ? -calibration.get(3) : Double.NaN; camera.getConfig().spatialCalScaleX = (calibration != null) && (calibration.get(0) != 0) ? 1.0 / (calibration.get(0)) : Double.NaN; camera.getConfig().spatialCalScaleY = (calibration != null) && (calibration.get(1) != 0) ? 1.0 / (calibration.get(1)) : Double.NaN; } } catch (Exception ex) { config = null; showException(ex); } camera.initialize(); if (camera instanceof Camtool){ double[] origin = ((Camtool)camera).origin.read(); if (origin.length>=2){ camera.getConfig().spatialCalOffsetX = origin[0]; camera.getConfig().spatialCalOffsetY = origin[1]; } } camera.getConfig().save(); camera.setPolling(-2000); camera.setMonitored(false); renderer.setDevice(camera); renderer.setShowReticle(true); renderer.setAutoScroll(true); camera.addListener(new ImageListener() { @Override public void onImage(Object o, BufferedImage bi, Data data) { if (bi == null) { renderer.removeOverlays(fitOv); fitOv = null; } else { if (renderer.getReticle() != null) { renderer.getReticle().setSize(new Dimension(bi.getWidth(), bi.getHeight())); } Overlay[] profile = renderer.getProfileOverlays(); profile = ((profile != null) && (profile.length==4)) ? getFitOverlays(bi) : null; renderer.updateOverlays(profile, fitOv); fitOv = profile; } //camera.removeListener(this); } @Override public void onError(Object o, Exception excptn) { } }); } finally { onTimer(); } } @Override protected void onTimer() { textState.setText((camera == null) ? "" : camera.getState().toString()); buttonConfig.setEnabled(camera != null); if (App.hasArgument("s")){ System.out.println("Refresh"); try { ((Source)getDevice("image")).initialize(); } catch (IOException ex) { Logger.getLogger(Cameras.class.getName()).log(Level.SEVERE, null, ex); } catch (InterruptedException ex) { Logger.getLogger(Cameras.class.getName()).log(Level.SEVERE, null, ex); } } } Pen fitPen = new Pen(new Color(0, 192, 0), 2); Overlay[] getFitOverlays(BufferedImage img){ Overlays.Polyline hpoly = null; Overlays.Polyline vpoly = null; int factor = 2; Profile profile = renderer.getProfile(); if ((profile != Profile.None) && (img != null)) { img = Utils.grayscale(img); if (profile.hasVertical()) { try { int[] sum = Utils.integrateVertically(img); int[] x = Arr.indexesInt(sum.length); double[] gaussian = fitGaussian(sum, x); if (gaussian!=null){ System.out.println("Norm: " + gaussian[0] + " Mean: " + gaussian[1] + " Sigma: " + gaussian[2]); double[] fit = getFitFunction(gaussian, x); int[] y = new int[x.length]; for (int i = 0; i < x.length; i++) { y[i] = (int) (img.getHeight() - 1 - (((double) fit[i]) / 255 / factor)); } vpoly = new Overlays.Polyline(fitPen, x, y); } else { System.out.println("Fit failure"); } } catch (Exception ex) { ex.printStackTrace(); } } if (profile.hasHorizontal()) { try { int[] sum = Utils.integrateHorizontally(img); int[] x = Arr.indexesInt(sum.length); double[] gaussian = fitGaussian(sum, x); if (gaussian!=null){ double[] fit = getFitFunction(gaussian, x); int[] y = new int[x.length]; for (int i = 0; i < x.length; i++) { y[i] = (int) (((double) fit[i]) / 255 / factor); } hpoly = new Overlays.Polyline(fitPen, y, x); } } catch (Exception ex) { ex.printStackTrace(); } } return new Overlay[]{hpoly, vpoly}; } return null; } double[] fitGaussianScript(int[] y, int[] x){ ScriptManager sm = Controller.getInstance().getScriptManager(); ArrayProperties pY = ArrayProperties.get(y); sm.setVar("y", y); sm.setVar("x", x); //InterpreterResult r = sm.eval("r = fit(y, x)"); System.out.println("-> " + pY.maxIndex); InterpreterResult r = sm.eval("r = fit_gaussians(y, x, [" + pY.maxIndex+ ",])"); if (r.exception != null){ r.exception .printStackTrace(); } else { List ret = (List) sm.getVar("r"); System.out.println(ret.size()); if ((ret!=null) && (ret.size()==1)&& (ret.get(0) instanceof List) && (((List)(ret.get(0))).size()==3)){ double norm = (Double) ((List)ret.get(0)).get(0); double mean = (Double) ((List)ret.get(0)).get(1); double sigma = (Double) ((List)ret.get(0)).get(2); System.out.println("! " + norm + " " + mean + " " + sigma) ; return new double[]{norm, mean, sigma}; } } return null; } double[]fitGaussian(int[] y, int[] x){ try{ ArrayProperties pY = ArrayProperties.get(y); GaussianCurveFitter fitter = GaussianCurveFitter.create().withStartPoint(new double[]{(pY.max-pY.min)/2,x[pY.maxIndex],1.0}).withMaxIterations(1000); ArrayListvalues = new ArrayList<>(); for (int i=0; i< y.length; i++){ values.add(new WeightedObservedPoint(1.0, x[i], y[i])); } return fitter.fit(values); } catch (Exception ex){ return null; } } double[] getFitFunction(double[] pars, int[] x){ double[] fit = new double[x.length]; Gaussian g = new Gaussian(pars[0], pars[1], pars[2]); for (int i=0; i< x.length; i++){ fit[i] = g.value(x[i]); } return fit; } ///////// public class Camtool extends ArraySource { final String prefix; final String dataPrefix; final boolean latch; final public ChannelInteger channelRun; final public ChannelInteger channelLatch; final public ChannelDouble channelTimestamp; final public ChannelDoubleArray origin; final public ChannelDouble posX, posY; final public ChannelDoubleArray profileX, profileY; final public ChannelIntegerArray shape; final public ChannelInteger bgEnable, bgCapture, bgCaptureRemain; //final public CamToolPosX posMeanX; //final public CamToolPosY posMeanY; //final public CamToolVarX posVarX; //final public CamToolVarY posVarY; public Camtool(String name, String prefix) { this(name, prefix, false, true); } public Camtool(String name, String prefix, boolean latch, boolean roi) { super(name, prefix + (latch ? ":latch" : ":pipeline") + (roi ? ".roi.output" : ".image")); this.prefix = prefix + ":"; this.latch = latch; dataPrefix = this.prefix + (latch ? "latch" : "pipeline") + "."; channelRun = new ChannelInteger(name + " run", this.prefix + "camera.run"); channelLatch = new ChannelInteger(name + " latch", this.prefix + "latch.capture"); channelTimestamp = new ChannelDouble(name + " timestamp", dataPrefix + "timestamp"); channelTimestamp.setMonitored(true); //posX = new ChannelDouble(name + " com x", dataPrefix + "x_stats.com"); //posY = new ChannelDouble(name + " com y", dataPrefix + "y_stats.com"); posX = new ChannelDouble(name + " com x", dataPrefix + "x_stats.com_egu"); posY = new ChannelDouble(name + " com y", dataPrefix + "y_stats.com_egu"); profileX = new ChannelDoubleArray(name + " profile x", dataPrefix + "profile.x"); profileY = new ChannelDoubleArray(name + " profile y", dataPrefix + "profile.y"); shape = new ChannelIntegerArray(name + " shape", dataPrefix + (roi ? "roi.output.shape" : "image.shape")); origin = new ChannelDoubleArray(name + " origin X", roi ? (dataPrefix + "roi.origin_out") : (prefix+"origin")); bgEnable = new ChannelInteger(name + " bg enable", this.prefix + "pipeline.bg.enabled"); bgCapture = new ChannelInteger(name + " bg capture", this.prefix + "pipeline.bg.capture"); bgCaptureRemain = new ChannelInteger(name + " bg capture remain", this.prefix + "pipeline.bg.capture_remain"); //posMeanX = new CamToolPosX(); //posMeanY = new CamToolPosY(); //posVarX = new CamToolVarX(); //posVarY = new CamToolVarY(); } @Override public void doSetMonitored(boolean value) { super.doSetMonitored(value); getDevice().setMonitored(value); } @Override public void doUpdate() throws IOException, InterruptedException { super.doUpdate(); getDevice().update(); } void safeInitialize(Device dev, int timeout) throws IOException, InterruptedException{ for (int retries = 0; retries < 10; retries ++){ try{ dev.initialize(); break; } catch (IOException ex){ if (retries==9){ throw ex; } Thread.sleep(timeout / 10); } } } @Override protected void doInitialize() throws IOException, InterruptedException { try { safeInitialize (channelRun,3000); channelLatch.initialize(); if (latch){ start(); latch(); } safeInitialize (channelTimestamp,2000); posX.initialize(); posY.initialize(); profileX.initialize(); profileY.initialize(); shape.initialize(); origin.initialize(); bgEnable.initialize(); bgCapture.initialize(); bgCaptureRemain.initialize(); int[] s = shape.read(); getConfig().imageHeight = s[0]; getConfig().imageWidth = s[1]; getConfig().save(); getDevice().setSize(s[0] * s[1]); super.doInitialize(); } catch (InterruptedException ex) { throw ex; } catch (Exception ex) { throw new IOException(ex); } } int numImages = 1; public int getNumImages() { return numImages; } public void setNumImages(int value) { numImages = value; } double grabTimeout = 3.0; public double getGrabTimeout() { return grabTimeout; } public void setGrabTimeou(double value) { grabTimeout = value; } public void capture() throws IOException, InterruptedException { } public void start() throws IOException, InterruptedException { channelRun.write(-1); } public void stop() throws IOException, InterruptedException { channelRun.write(0); } public void grabSingle() throws IOException, InterruptedException { channelRun.write(1); } public void latch() throws IOException, InterruptedException { System.out.println("LATCH"); channelLatch.write(1); System.out.println("Ret:" + channelLatch.read()); } public void enableBackground(boolean value) throws IOException, InterruptedException { bgEnable.write(value ? 1 : 0); } public void captureBackground(int images) throws IOException, InterruptedException { start(); bgCapture.write(images); Thread.sleep(200); while (bgCaptureRemain.read() > 0) { Thread.sleep(10); } } } //////// @SuppressWarnings("unchecked") // //GEN-BEGIN:initComponents private void initComponents() { renderer = new ch.psi.pshell.imaging.Renderer(); jLabel1 = new javax.swing.JLabel(); comboCameras = new javax.swing.JComboBox(); jLabel2 = new javax.swing.JLabel(); textState = new javax.swing.JTextField(); buttonConfig = new javax.swing.JButton(); buttonSetup = new javax.swing.JButton(); checkCamtool = new javax.swing.JCheckBox(); jLabel1.setText("Camera:"); comboCameras.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { comboCamerasActionPerformed(evt); } }); jLabel2.setText("State:"); textState.setEditable(false); textState.setHorizontalAlignment(javax.swing.JTextField.CENTER); textState.setDisabledTextColor(new java.awt.Color(0, 0, 0)); textState.setEnabled(false); buttonConfig.setText("Config"); buttonConfig.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { buttonConfigActionPerformed(evt); } }); buttonSetup.setText("Setup"); buttonSetup.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { buttonSetupActionPerformed(evt); } }); checkCamtool.setSelected(true); checkCamtool.setText("Camtool"); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(renderer, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addComponent(checkCamtool) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(comboCameras, 0, 218, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(buttonConfig) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(buttonSetup) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(textState, javax.swing.GroupLayout.PREFERRED_SIZE, 120, javax.swing.GroupLayout.PREFERRED_SIZE))) .addContainerGap()) ); layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {buttonConfig, buttonSetup}); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel1) .addComponent(comboCameras, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel2) .addComponent(textState, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(buttonConfig) .addComponent(buttonSetup) .addComponent(checkCamtool)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(renderer, javax.swing.GroupLayout.DEFAULT_SIZE, 348, Short.MAX_VALUE) .addGap(6, 6, 6)) ); layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {comboCameras, textState}); }// //GEN-END:initComponents private void comboCamerasActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboCamerasActionPerformed try { comboCameras.setEnabled(false); new Thread(new Runnable() { @Override public void run() { try{ setCamera((String) comboCameras.getSelectedItem()); } catch(Exception ex){ ex.printStackTrace(); } finally{ comboCameras.setEnabled(true); } } }).start(); } catch (Exception ex) { showException(ex); } }//GEN-LAST:event_comboCamerasActionPerformed private void buttonConfigActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonConfigActionPerformed try { if (camera != null) { TextEditor editor = new TextEditor(); editor.setText(cameraConfigJson); editor.setReadOnly(true); editor.setTitle(cameraName); EditorDialog dlg = editor.getDialog(getTopLevel(), false); dlg.setSize(480, 640); dlg.setVisible(true); SwingUtils.centerComponent(this, dlg); } } catch (Exception ex) { showException(ex); } }//GEN-LAST:event_buttonConfigActionPerformed private void buttonSetupActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonSetupActionPerformed try { if (camera != null) { this.showDeviceConfigDialog(camera, false); } } catch (Exception ex) { showException(ex); } }//GEN-LAST:event_buttonSetupActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton buttonConfig; private javax.swing.JButton buttonSetup; private javax.swing.JCheckBox checkCamtool; private javax.swing.JComboBox comboCameras; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private ch.psi.pshell.imaging.Renderer renderer; private javax.swing.JTextField textState; // End of variables declaration//GEN-END:variables }