From 667f16f3929421abf4195e72d4cc7c81e815b52f Mon Sep 17 00:00:00 2001 From: Amandine Tournay <amandine.tournay@pasteur.fr> Date: Tue, 9 Mar 2021 18:54:57 +0100 Subject: [PATCH] Added project --- .gitignore | 6 + pom.xml | 110 ++++ .../java/plugins/adufour/opencv/OpenCV.java | 575 ++++++++++++++++++ .../plugins/adufour/opencv/OpenCVCapture.java | 160 +++++ 4 files changed, 851 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/plugins/adufour/opencv/OpenCV.java create mode 100644 src/main/java/plugins/adufour/opencv/OpenCVCapture.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d47f98 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.idea/ +target/ +.settings/ +*.iml +.project +.classpath \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..4f214df --- /dev/null +++ b/pom.xml @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <!-- Inherited Icy Parent POM --> + <parent> + <groupId>org.bioimageanalysis.icy</groupId> + <artifactId>parent-pom-plugin</artifactId> + <version>1.0.3</version> + </parent> + + <!-- Project Information --> + <artifactId>opencv</artifactId> + <version>4.5.1</version> + + <packaging>jar</packaging> + + <name>OpenCV</name> + <description>OpenCV (Open Computer Vision) library for Icy. see more at http://opencv.org</description> + <url>http://icy.bioimageanalysis.org/plugin/opencv/</url> + <inceptionYear>2020</inceptionYear> + + <organization> + <name>Institut Pasteur</name> + <url>https://pasteur.fr</url> + </organization> + + <licenses> + <license> + <name>GNU GPLv3</name> + <url>https://www.gnu.org/licenses/gpl-3.0.en.html</url> + <distribution>repo</distribution> + </license> + </licenses> + + <developers> + <developer> + <id>sdallongeville</id> + <name>Stéphane Dallongeville</name> + <url>https://research.pasteur.fr/fr/member/stephane-dallongeville/</url> + <roles> + <role>founder</role> + <role>lead</role> + <role>architect</role> + <role>developer</role> + <role>debugger</role> + <role>tester</role> + <role>maintainer</role> + <role>support</role> + </roles> + </developer> + </developers> + + <!-- Project properties --> + <properties> + + </properties> + + <!-- Project build configuration --> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <version>3.1.1</version> + <executions> + <execution> + <id>${project.artifactId}-fetch</id> + <phase>generate-sources</phase> + <goals> + <goal>unpack-dependencies</goal> + </goals> + <configuration> + <includeArtifactIds>opencv</includeArtifactIds> + <outputDirectory>${project.build.outputDirectory}</outputDirectory> + <stripVersion>true</stripVersion> + <excludeTransitive>true</excludeTransitive> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <!-- List of project's dependencies --> + <dependencies> + <!-- The core of Icy --> + <dependency> + <groupId>org.bioimageanalysis.icy</groupId> + <artifactId>icy-kernel</artifactId> + </dependency> + + <dependency> + <groupId>org.openpnp</groupId> + <artifactId>opencv</artifactId> + <version>4.5.1-0</version> + </dependency> + </dependencies> + + <!-- Icy Maven repository (to find parent POM) --> + <repositories> + <repository> + <id>icy</id> + <name>Icy's Nexus</name> + <url>https://icy-nexus.pasteur.fr/repository/Icy/</url> + </repository> + </repositories> +</project> \ No newline at end of file diff --git a/src/main/java/plugins/adufour/opencv/OpenCV.java b/src/main/java/plugins/adufour/opencv/OpenCV.java new file mode 100644 index 0000000..5ec6023 --- /dev/null +++ b/src/main/java/plugins/adufour/opencv/OpenCV.java @@ -0,0 +1,575 @@ +package plugins.adufour.opencv; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import javax.imageio.ImageIO; + +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.Size; +import org.opencv.imgproc.Imgproc; +import org.opencv.videoio.VideoCapture; +import org.opencv.videoio.Videoio; + +import icy.common.Version; +import icy.image.IcyBufferedImage; +import icy.main.Icy; +import icy.math.FPSMeter; +import icy.plugin.abstract_.Plugin; +import icy.plugin.interface_.PluginLibrary; +import icy.sequence.Sequence; +import icy.sequence.SequenceAdapter; +import icy.system.IcyExceptionHandler; +import icy.system.IcyHandledException; +import icy.system.SystemUtil; +import icy.type.DataType; + +/** + * <a href=http://opencv.org>OpenCV</a> library for Icy. This class contains additional test + * functions as well as tools to convert between OpenCV and Icy image formats. + * + * @author Alexandre Dufour + */ +public class OpenCV extends Plugin implements PluginLibrary +{ + public static final Version OpenCV_Version = new Version(Core.VERSION_MAJOR, Core.VERSION_MINOR, + Core.VERSION_REVISION); + + /** + * A local buffer used to speed up repetitive conversions between OpenCV and Icy + */ + private static Object buffer = new Object[0]; + + private static final ExecutorService service = Executors.newFixedThreadPool(SystemUtil.getNumberOfCPUs()); + + static + { + String loadingMessage = "Loading OpenCV " + OpenCV_Version + "..."; + String successMessage = "OpenCV " + OpenCV_Version + " successfully loaded."; + + try + { + // First check whether OpenCV is already installed + System.out.println(loadingMessage); + prepareLibrary(OpenCV.class, Core.NATIVE_LIBRARY_NAME); + nu.pattern.OpenCV.loadShared(); + System.out.println(successMessage); + } + catch (UnsatisfiedLinkError lib1) + { + IcyExceptionHandler.handleException(lib1, true); + System.out.println("Trying alternate method"); + + try + { + // alternate method + loadLibrary(OpenCV.class, Core.NATIVE_LIBRARY_NAME); + } + catch (UnsatisfiedLinkError lib2) + { + IcyExceptionHandler.handleException(lib2, true); + System.out.println("Last chance..."); + + try + { + // last chance... + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + } + catch (UnsatisfiedLinkError lib3) + { + IcyExceptionHandler.handleException(lib3, true); + } + } + } + } + + /** + * Initializes OpenCV by loading the native libraries for the current operating system. This + * method should be called once before any OpenCV class is used + */ + public static void initialize() + { + // There is no need to actually initialize anything. + // The static block above will be executed as soon as this method is called. + } + + /** + * Converts the specified OpenCV {@link Mat} into an {@link IcyBufferedImage} + * + * @param mat + * the OpenCV {@link Mat} to convert + * @return an {@link IcyBufferedImage} + */ + public static IcyBufferedImage convertToIcy(Mat mat) + { + IcyBufferedImage output = new IcyBufferedImage(mat.width(), mat.height(), mat.channels(), getIcyDataType(mat)); + convertToIcy(mat, output); + return output; + } + + /** + * Converts the data contained inside the specified OpenCV {@link Mat}rix into the specified + * {@link IcyBufferedImage} object.<br> + * This method requires that the output image is initialized and has the correct dimensions and + * data type (if unsure, use {@link #convertToIcy(Mat)} instead). + * + * @param mat Mat + * @param output IcyBufferedImage + * throws {@link NullPointerException} if either parameter is null + */ + public static void convertToIcy(Mat mat, final IcyBufferedImage output) + { + final int width = mat.cols(); + final int height = mat.rows(); + final int nChannels = mat.channels(); + int bufferSize = width * height * nChannels; + + // handle the easy case (single-channel) first + if (nChannels == 1) + { + try + { + // Retrieve the frame data in a generic way + // This is equivalent to calling e.g. Mat.get(0, 0, (byte[]) buffer); + Object dataBuffer = output.getDataXY(0); + Class<?> dataType = dataBuffer.getClass(); + Mat.class.getMethod("get", int.class, int.class, dataType).invoke(mat, 0, 0, dataBuffer); + + return; + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + // Make sure the buffer has the proper type and size + if (Array.getLength(buffer) != bufferSize) + buffer = null; + final DataType dataType = getIcyDataType(mat); + Class<?> type = dataType.toPrimitiveClass(); + if (buffer == null || buffer.getClass().getComponentType() != type) + buffer = Array.newInstance(type, bufferSize); + + try + { + // Retrieve the frame data in a generic way + // This is equivalent to calling e.g. Mat.get(0, 0, (byte[]) buffer); + Mat.class.getMethod("get", int.class, int.class, buffer.getClass()).invoke(mat, 0, 0, buffer); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + + ArrayList<Future<?>> tasks = new ArrayList<Future<?>>(nChannels); + + for (int c = 0; c < nChannels; c++) + { + final int channel = c; + + tasks.add(service.submit(new Runnable() + { + @Override + public void run() + { + switch (dataType) + { + case BYTE: + case UBYTE: + { + // OpenCV reads BGR by default => revert this to conventional RGB + final byte[] icyChannel = output.getDataXYAsByte(nChannels - channel - 1); + for (int out = 0, in = channel; out < icyChannel.length; out++, in += nChannels) + icyChannel[out] = ((byte[]) buffer)[in]; + break; + } + case SHORT: + case USHORT: + { + // OpenCV reads BGR by default => revert this to conventional RGB + final short[] icyChannel = output.getDataXYAsShort(nChannels - channel - 1); + for (int out = 0, in = channel; out < icyChannel.length; out++, in += nChannels) + icyChannel[out] = ((short[]) buffer)[in]; + break; + } + case INT: + { + // OpenCV reads BGR by default => revert this to conventional RGB + final int[] icyChannel = output.getDataXYAsInt(nChannels - channel - 1); + for (int out = 0, in = channel; out < icyChannel.length; out++, in += nChannels) + icyChannel[out] = ((int[]) buffer)[in]; + break; + } + case FLOAT: + { + // OpenCV reads BGR by default => revert this to conventional RGB + final float[] icyChannel = output.getDataXYAsFloat(nChannels - channel - 1); + for (int out = 0, in = channel; out < icyChannel.length; out++, in += nChannels) + icyChannel[out] = ((float[]) buffer)[in]; + break; + } + case DOUBLE: + { + // OpenCV reads BGR by default => revert this to conventional RGB + final double[] icyChannel = output.getDataXYAsDouble(nChannels - channel - 1); + for (int out = 0, in = channel; out < icyChannel.length; out++, in += nChannels) + icyChannel[out] = ((double[]) buffer)[in]; + break; + } + default: + { + // OpenCV reads BGR by default => revert this to conventional RGB + final Object icyChannel = output.getDataXY(nChannels - channel - 1); + int size = Array.getLength(icyChannel); + for (int out = 0, in = channel; out < size; out++, in += nChannels) + System.arraycopy(buffer, in, icyChannel, out, 1); + } + } + } + })); + } + + try + { + for (Future<?> task : tasks) + task.get(); + } + catch (InterruptedException e) + { + Thread.currentThread().interrupt(); + } + catch (ExecutionException e) + { + e.printStackTrace(); + } + } + + /** + * Converts the specified {@link IcyBufferedImage} into an OpenCV {@link Mat} + * + * @param img + * the {@link IcyBufferedImage} to convert + * @return an OpenCV {@link Mat} + */ + public static Mat convertToOpenCV(IcyBufferedImage img) + { + int width = img.getWidth(); + int height = img.getHeight(); + int sizeC = img.getSizeC(); + int bufferSize = width * height * sizeC; + + Mat mat = new Mat(new Size(width, height), getCVDataType(img)); + + if (sizeC == 1) + { + buffer = img.getDataXY(0); + } + else + { + // Make sure the buffer has the proper type and size + if (Array.getLength(buffer) != bufferSize) + buffer = null; + Class<?> type = img.getDataType_().toPrimitiveClass(); + if (buffer == null || buffer.getClass().getComponentType() != type) + buffer = Array.newInstance(type, bufferSize); + + for (int c = 0; c < sizeC; c++) + { + // OpenCV Mat elements are interleaved... + Object in = img.getDataXY(sizeC - c - 1); + for (int j = 0, offIN = 0, offOUT = c; j < height; j++) + for (int i = 0; i < width; i++, offIN++, offOUT += sizeC) + System.arraycopy(in, offIN, buffer, offOUT, 1); + } + } + + try + { + // Retrieve the frame data in a generic way + // This is equivalent to calling e.g. Mat.put(0, 0, (byte[]) buffer); + Mat.class.getMethod("put", int.class, int.class, buffer.getClass()).invoke(mat, 0, 0, buffer); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + + return mat; + } + + /** + * @param img Icy Buffered Image + * @return the OpenCV data type corresponding to the specified image (see the {@link CvType} + * .CV_* constants) + */ + public static int getCVDataType(IcyBufferedImage img) + { + switch (img.getDataType_()) + { + case BYTE: + return CvType.CV_8SC(img.getSizeC()); + case UBYTE: + return CvType.CV_8UC(img.getSizeC()); + case SHORT: + return CvType.CV_16SC(img.getSizeC()); + case USHORT: + return CvType.CV_16UC(img.getSizeC()); + case UINT: // TODO ? + case INT: + return CvType.CV_32SC(img.getSizeC()); + case FLOAT: + return CvType.CV_32FC(img.getSizeC()); + case DOUBLE: + return CvType.CV_64FC(img.getSizeC()); + default: + throw new UnsupportedOperationException("OpenCV does not support type " + img.getDataType_()); + } + } + + /** + * @param mat Mat + * @return the {@link DataType} corresponding to the specified OpenCV {@link Mat}rix + */ + public static DataType getIcyDataType(Mat mat) + { + switch (CvType.depth(mat.type())) + { + case CvType.CV_8S: + return DataType.BYTE; + case CvType.CV_8U: + return DataType.UBYTE; + case CvType.CV_16S: + return DataType.SHORT; + case CvType.CV_16U: + return DataType.USHORT; + case CvType.CV_32S: + return DataType.INT; + case CvType.CV_32F: + return DataType.FLOAT; + case CvType.CV_64F: + return DataType.DOUBLE; + default: + return null; + } + } + + /** + * OpenCV test that starts the first available webcam and displays its live feed inside a + * viewer. This method will run indefinitely unless the viewer is closed or its calling thread + * is interrupted (see {@link Thread#interrupt()}) + */ + public static void liveWebcam() + { + liveWebcam(-1, -1, -1); + } + + /** + * OpenCV test that starts the first available webcam and displays its live feed inside a + * viewer. This method will run indefinitely unless the viewer is closed or its calling thread + * is interrupted (see {@link Thread#interrupt()}) + * + * @param width int + * @param height int + * @param fps int + */ + public static void liveWebcam(final int width, final int height, final int fps) + { + // Connect to the camera + VideoCapture vc = new VideoCapture(0); + if (width != -1) + vc.set(Videoio.CAP_PROP_FRAME_WIDTH, width); + if (height != -1) + vc.set(Videoio.CAP_PROP_FRAME_HEIGHT, height); + if (fps != -1) + vc.set(Videoio.CAP_PROP_FPS, fps); + + liveWebcam(vc); + } + + /** + * OpenCV test that starts the first available webcam and displays its live feed inside a + * viewer. This method will run indefinitely unless the viewer is closed or its calling thread + * is interrupted (see {@link Thread#interrupt()}) + */ + public static void liveWebcam(final VideoCapture camera) + { + new Thread(new Runnable() + { + @Override + public void run() + { + // We need to know which thread to interrupt to clean the camera + final Thread currentThread = Thread.currentThread(); + + // Read the first image to initialize the meta-data + Mat mat = new Mat(); + camera.read(mat); + IcyBufferedImage image = convertToIcy(mat); + + // Create the sequence + final Sequence s = new Sequence("Live webcam", image); + Icy.getMainInterface().addSequence(s); + + // don't update the channel bounds on update + image.setAutoUpdateChannelBounds(false); + s.setAutoUpdateChannelBounds(false); + + // Closing the sequence should stop the camera + s.addListener(new SequenceAdapter() + { + @Override + public void sequenceClosed(Sequence sequence) + { + s.removeListener(this); + currentThread.interrupt(); + } + }); + + try + { + FPSMeter fps = new FPSMeter(); + long grabTime = 0; + long readTime = 0; + long conversionTime = 0; + long dataUpdateTime = 0; + double nbIterations = 0.0; + + // loop until the thread is interrupted + while (!currentThread.isInterrupted()) + { + long t0 = System.nanoTime(); + camera.grab(); + long t1 = System.nanoTime(); + camera.retrieve(mat); + long t2 = System.nanoTime(); + convertToIcy(mat, image); + long t3 = System.nanoTime(); + image.dataChanged(); + long t4 = System.nanoTime(); + fps.update(); + + nbIterations++; + grabTime += (t1 - t0) / 1000000; + readTime += (t2 - t1) / 1000000; + conversionTime += (t3 - t2) / 1000000; + dataUpdateTime += (t4 - t3) / 1000000; + } + System.out.println("Capture frame-rate: " + fps.getFPS() + " fps"); + System.out.println("Camera grab time: " + grabTime / nbIterations + " ms"); + System.out.println("Camera read time: " + readTime / nbIterations + " ms"); + System.out.println("Conversion time: " + conversionTime / nbIterations + " ms"); + System.out.println("Data update time: " + dataUpdateTime / nbIterations + " ms"); + } + finally + { + // close the camera + camera.release(); + } + } + }).start(); + } + + /** + * OpenCV test that takes the active image and performs a Sobel filter in X and Y and combines + * the result into a new image + */ + public static void testSobel() + { + new Thread(new Runnable() + { + @Override + public void run() + { + Sequence s = Icy.getMainInterface().getActiveSequence(); + + if (s == null) + throw new IcyHandledException("Open an image first!"); + + Mat mat = convertToOpenCV(Icy.getMainInterface().getActiveImage()); + + Mat x = new Mat(); + Mat y = new Mat(); + + Imgproc.Sobel(mat, x, -1, 1, 0); + Imgproc.Sobel(mat, y, -1, 0, 1); + + org.opencv.core.Core.addWeighted(x, 0.5, y, 0.5, 1.0, mat); + + s = new Sequence("OpenCV: Sobel filter applied to " + s.getName(), convertToIcy(mat)); + + Icy.getMainInterface().addSequence(s); + } + }).start(); + } + + /** + * <h2>WARNING: This method only works on the Raspberry Pi!!</h2> Takes an image of the + * specified width and height and stores it in a BufferedImage object. The resulting image is + * NOT saved anywhere in the Pi's memory. The image's encoding will be the same as the + * RPiCamera's encoding setting (JPEG by default).<br> + * <br> + * Usage Example:<br> + * + * <pre> + * BufferedImage buffImg = piCamera.takeBufferedStill(500, 500); + * </pre> + * + * @param width + * An int specifying width of image to take. + * @param height + * An int specifying height of image to take. + * @return A BufferedImage containing the image. + * @throws IOException + * this exception is thrown if: + * <ul> + * <li>The system is not a Raspberry Pi (RPi),</li> + * <li>The RPi is not equipped with a camera board,</li> + * <li>The <code>raspistill</code> utility is not installed,</li> + * <li>Something went wrong when reading the image from the camera</li> + * </ul> + */ + public static BufferedImage rpi_raspistillToBufferedImage(int width, int height) throws IOException + { + List<String> command = new ArrayList<String>(); + command.add("raspistill"); + command.add("-o"); + command.add("-v"); + command.add("-w"); + command.add("" + width); + command.add("-h"); + command.add("" + height); + ProcessBuilder pb = new ProcessBuilder(command); + + // System.out.println("Executed this command:\n\t" + command.toString()); + // pb.redirectErrorStream(true); + // pb.redirectOutput( + // new File(System.getProperty("user.home") + File.separator + + // "Desktop" + File.separator + "RPiCamera.out")); + + Process p = pb.start(); + BufferedImage bi = ImageIO.read(p.getInputStream()); + // -------------------------------------------------------------------------- + // This code can be used to specify an ImageReader - perhaps for a specific + // type of image - in place of the previous line: + // + // ImageInputStream iis = ImageIO.createImageInputStream(p.getInputStream()); + // Iterator<?> imgReaders = ImageIO.getImageReadersByFormatName("png"); + // ImageReader reader = (ImageReader) imgReaders.next(); + // reader.setInput(iis, true); //May need to set this to false... + // ImageReadParam param = reader.getDefaultReadParam(); + // BufferedImage bi = reader.read(0, param); + // -------------------------------------------------------------------------- + return bi; + } + +} diff --git a/src/main/java/plugins/adufour/opencv/OpenCVCapture.java b/src/main/java/plugins/adufour/opencv/OpenCVCapture.java new file mode 100644 index 0000000..0e3f234 --- /dev/null +++ b/src/main/java/plugins/adufour/opencv/OpenCVCapture.java @@ -0,0 +1,160 @@ +package plugins.adufour.opencv; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JSeparator; + +import org.opencv.core.Mat; +import org.opencv.videoio.VideoCapture; +import org.opencv.videoio.Videoio; + +import icy.image.IcyBufferedImage; +import icy.main.Icy; +import icy.math.FPSMeter; +import icy.sequence.Sequence; +import icy.sequence.SequenceAdapter; +import plugins.adufour.ezplug.EzPlug; +import plugins.adufour.ezplug.EzStoppable; +import plugins.adufour.ezplug.EzVar; +import plugins.adufour.ezplug.EzVarInteger; +import plugins.adufour.ezplug.EzVarListener; +import plugins.adufour.vars.util.VarException; + +public class OpenCVCapture extends EzPlug implements EzStoppable, EzVarListener<Integer> +{ + List<EzVarInteger> parameters = new ArrayList<EzVarInteger>(); + + VideoCapture camera = null; + + @Override + protected void initialize() + { + OpenCV.initialize(); + + for (Field f : Videoio.class.getDeclaredFields()) + { + String name = f.getName(); + if (!name.startsWith("CAP_PROP_")) continue; + if (name.contains("_DC1394")) continue; + if (name.contains("_GIGA")) continue; + if (name.contains("_GPHOTO2")) continue; + if (name.contains("_GSTREAMER")) continue; + if (name.contains("_INTELPERC")) continue; + if (name.contains("_IOS")) continue; + if (name.contains("_OPENNI")) continue; + if (name.contains("_POS")) continue; + if (name.contains("_PVAPI")) continue; + if (name.contains("_XI")) continue; + + name = name.substring(9).toLowerCase(); + EzVarInteger setting = new EzVarInteger(name); + setting.setValue(Integer.valueOf(-1)); + setting.addVarChangeListener(this); + parameters.add(setting); + } + + int cpt = 0; + for (EzVarInteger setting : parameters) + { + addEzComponent(setting); + cpt++; + if (cpt % 20 == 0) addComponent(new JSeparator(JSeparator.VERTICAL)); + } + } + + @Override + protected void execute() + { + camera = new VideoCapture(0); + + for (EzVarInteger setting : parameters) + { + if (setting.getValue() == -1) continue; + + String propName = "CAP_PROP_" + setting.name.toUpperCase(); + try + { + int propID = Videoio.class.getDeclaredField(propName).getInt(null); + camera.set(propID, setting.getValue()); + } + catch (Exception e) + { + throw new VarException(setting.getVariable(), e.getMessage()); + } + } + + // Read the first image to initialize the meta-data + Mat mat = new Mat(); + camera.read(mat); + IcyBufferedImage image = OpenCV.convertToIcy(mat); + + // Create the sequence + final Sequence s = new Sequence("Live webcam", image); + Icy.getMainInterface().addSequence(s); + + // don't update the channel bounds on update + image.setAutoUpdateChannelBounds(false); + s.setAutoUpdateChannelBounds(false); + + // Closing the sequence should stop the camera + final Thread thread = Thread.currentThread(); + s.addListener(new SequenceAdapter() + { + @Override + public void sequenceClosed(Sequence sequence) + { + s.removeListener(this); + thread.interrupt(); + } + }); + + FPSMeter fps = new FPSMeter(); + + try + { + while (camera.read(mat) && !Thread.currentThread().isInterrupted()) + { + OpenCV.convertToIcy(mat, image); + image.dataChanged(); + fps.update(); + getUI().setProgressBarMessage("Acquiring at " + fps.getFPS() + "fps"); + } + } + catch (Exception e) + { + + } + finally + { + camera.release(); + } + } + + @Override + public void clean() + { + for (EzVarInteger setting : parameters) + setting.removeVarChangeListener(this); + parameters.clear(); + } + + @Override + public void variableChanged(EzVar<Integer> source, Integer newValue) + { + if (camera == null) return; + + String propName = "CAP_PROP_" + source.name.toUpperCase(); + try + { + int propID = Videoio.class.getDeclaredField(propName).getInt(null); + camera.set(propID, newValue); + } + catch (Exception e) + { + throw new VarException(source.getVariable(), e.getMessage()); + } + } + +} -- GitLab