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