001/*******************************************************************************
002 * Permission to use and distribute this software is granted under the BSD 2-Clause
003 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause).
004 * Copyright (c) 2016-2023 Wilhelm Burger. All rights reserved.
005 * Visit https://imagingbook.com for additional details.
006 ******************************************************************************/
007package Calibration_Plugins_2;
008
009import ij.IJ;
010import ij.ImagePlus;
011import ij.gui.GenericDialog;
012import ij.plugin.PlugIn;
013import imagingbook.calibration.zhang.Camera;
014import imagingbook.calibration.zhang.ViewTransform;
015import imagingbook.calibration.zhang.data.CalibrationImage;
016import imagingbook.calibration.zhang.data.ZhangData;
017import imagingbook.common.color.sets.BasicAwtColor;
018import imagingbook.common.ij.overlay.ColoredStroke;
019import imagingbook.common.ij.overlay.ShapeOverlayAdapter;
020import imagingbook.common.math.Matrix;
021import imagingbook.core.jdoc.JavaDocHelp;
022import imagingbook.core.resource.ImageResource;
023
024import java.awt.Shape;
025import java.awt.geom.Line2D;
026
027import static imagingbook.common.ij.DialogUtils.formatText;
028
029
030/**
031 * This plugin draws the projected X/Y/Z coordinate axes for each of the given camera views.
032 *
033 * @author WB
034 * @version 2022/12/19
035 */
036public class Draw_3D_Axes_Demo implements PlugIn, JavaDocHelp {
037        
038        private static ImageResource resource = CalibrationImage.CalibImageStack;
039        private static BasicAwtColor xColor = BasicAwtColor.Red;
040        private static BasicAwtColor yColor = BasicAwtColor.Green;
041        private static BasicAwtColor zColor = BasicAwtColor.Blue;
042        private static double StrokeWidth = 2.0;
043        private static double AxisLength = 5;
044        private static boolean FlipYaxis = true;
045
046        public void run(String arg0) {
047
048                // create a 3D model:
049                double[] p0 = {0.0, 0.0, 0.0}; // 3D origin
050                double[] p1 = {AxisLength, 0.0, 0.0};   // x-axis
051                double[] p2 = {0.0, AxisLength, 0.0};   // y-axis
052                double[] p3 = {0.0, 0.0, AxisLength};   // z-axis
053                if (FlipYaxis) {
054                        Matrix.multiplyD(-1, p2);
055                }
056
057                // open the test image (stack):
058                ImagePlus testIm = resource.getImagePlus();
059                if (testIm == null) {
060                        IJ.error("Could not open calibration images!");
061                        return;
062                }
063                testIm.show();
064
065                if (!runDialog()) {
066                        return;
067                }
068
069                // get pre-calculated camera intrinsics and view parameters (typically by calibration):
070                Camera camera = ZhangData.getCameraIntrinsics();
071                ViewTransform[] views = ZhangData.getAllViewTransforms();
072                final int M = views.length;
073
074                // project and draw the model into the views:
075                ShapeOverlayAdapter ola = new ShapeOverlayAdapter();
076                ColoredStroke xStroke = new ColoredStroke(StrokeWidth, xColor.getColor());
077                ColoredStroke yStroke = new ColoredStroke(StrokeWidth, yColor.getColor());
078                ColoredStroke zStroke = new ColoredStroke(StrokeWidth, zColor.getColor());
079
080                for (int i = 0; i < M; i++) {
081                        ola.setStackPosition(i + 1);    // stack slice number
082                        ola.addShape(get3DAxisProjection(camera, views[i], p0, p1), xStroke);
083                        ola.addShape(get3DAxisProjection(camera, views[i], p0, p2), yStroke);
084                        ola.addShape(get3DAxisProjection(camera, views[i], p0, p3), zStroke);
085                }
086
087                testIm.setOverlay(ola.getOverlay());
088        }
089
090        private Shape get3DAxisProjection(Camera cam, ViewTransform V, double[] P1, double[] P2) {
091                double[] u1 = cam.project(V, P1);
092                double[] u2 = cam.project(V, P2);
093                return new Line2D.Double(u1[0], u1[1], u2[0], u2[1]);
094        }
095
096        // ------------------------------------------------------------------------------------
097
098        private boolean runDialog() {
099                GenericDialog gd = new GenericDialog(this.getClass().getSimpleName());
100                gd.addHelp(getJavaDocUrl());
101                gd.setInsets(0, 0, 0);
102                gd.addMessage(formatText(40,
103                                "This plugin displays the 5 sample view images and",
104                                "projects the 3D coordinate axis onto the origin of ",
105                                "the calibration model, based on the pre-calculated",
106                                "calibration data. No calibration is performed!"));
107
108                gd.addNumericField("Stroke width", StrokeWidth);
109                gd.addEnumChoice("X-coordinate color", xColor);
110                gd.addEnumChoice("Y-coordinate color", yColor);
111                gd.addEnumChoice("Z-coordinate color", zColor);
112                gd.addNumericField("3D axis length (inches)", AxisLength, 1);
113                gd.addCheckbox("Flip y-axis", FlipYaxis);
114
115                gd.showDialog();
116                if (gd.wasCanceled())
117                        return false;
118
119                StrokeWidth = gd.getNextNumber();
120                xColor = gd.getNextEnumChoice(BasicAwtColor.class);
121                yColor = gd.getNextEnumChoice(BasicAwtColor.class);
122                zColor = gd.getNextEnumChoice(BasicAwtColor.class);
123                AxisLength = gd.getNextNumber();
124                FlipYaxis = gd.getNextBoolean();
125                return true;
126        }
127
128}