diff --git a/modules/highgui/CMakeLists.txt b/modules/highgui/CMakeLists.txt index 91a9a1bb45..1b73dbcbcc 100644 --- a/modules/highgui/CMakeLists.txt +++ b/modules/highgui/CMakeLists.txt @@ -1,5 +1,9 @@ set(the_description "High-level GUI") -ocv_add_module(highgui opencv_imgproc opencv_imgcodecs OPTIONAL opencv_videoio WRAP python) +if(ANDROID) + ocv_add_module(highgui opencv_imgproc opencv_imgcodecs OPTIONAL opencv_videoio WRAP python) +else() + ocv_add_module(highgui opencv_imgproc opencv_imgcodecs OPTIONAL opencv_videoio WRAP python java) +endif() # ---------------------------------------------------------------------------- # CMake file for highgui. See root CMakeLists.txt diff --git a/modules/highgui/misc/java/src/java/highgui+HighGui.java b/modules/highgui/misc/java/src/java/highgui+HighGui.java new file mode 100644 index 0000000000..87a0ec127a --- /dev/null +++ b/modules/highgui/misc/java/src/java/highgui+HighGui.java @@ -0,0 +1,193 @@ +package org.opencv.highgui; + +import org.opencv.core.Mat; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * This class was designed for use in Java applications + * to recreate the OpenCV HighGui functionalities. + */ +public final class HighGui { + + // Constants for namedWindow + public final static int WINDOW_NORMAL = ImageWindow.WINDOW_NORMAL; + public final static int WINDOW_AUTOSIZE = ImageWindow.WINDOW_AUTOSIZE; + + // Control Variables + public static int n_closed_windows = 0; + public static int pressedKey = -1; + public static CountDownLatch latch = new CountDownLatch(1); + + // Windows Map + public static Map windows = new HashMap(); + + public static void namedWindow(String winname) { + namedWindow(winname, HighGui.WINDOW_AUTOSIZE); + } + + public static void namedWindow(String winname, int flag) { + ImageWindow newWin = new ImageWindow(winname, flag); + if (windows.get(winname) == null) windows.put(winname, newWin); + } + + public static void imshow(String winname, Mat img) { + if (img.empty()) { + System.err.println("Error: Empty image in imshow"); + System.exit(-1); + } else { + ImageWindow tmpWindow = windows.get(winname); + if (tmpWindow == null) { + ImageWindow newWin = new ImageWindow(winname, img); + windows.put(winname, newWin); + } else { + tmpWindow.setMat(img); + } + } + } + + public static Image toBufferedImage(Mat m) { + int type = BufferedImage.TYPE_BYTE_GRAY; + + if (m.channels() > 1) { + type = BufferedImage.TYPE_3BYTE_BGR; + } + + int bufferSize = m.channels() * m.cols() * m.rows(); + byte[] b = new byte[bufferSize]; + m.get(0, 0, b); // get all the pixels + BufferedImage image = new BufferedImage(m.cols(), m.rows(), type); + + final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); + System.arraycopy(b, 0, targetPixels, 0, b.length); + + return image; + } + + public static JFrame createJFrame(String title, int flag) { + JFrame frame = new JFrame(title); + + frame.addWindowListener(new java.awt.event.WindowAdapter() { + @Override + public void windowClosing(java.awt.event.WindowEvent windowEvent) { + n_closed_windows++; + if (n_closed_windows == windows.size()) latch.countDown(); + } + }); + + frame.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + } + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + pressedKey = e.getKeyCode(); + latch.countDown(); + } + }); + + if (flag == WINDOW_AUTOSIZE) frame.setResizable(false); + + return frame; + } + + public static void waitKey(){ + waitKey(0); + } + + public static int waitKey(int delay) { + // Reset control values + latch = new CountDownLatch(1); + n_closed_windows = 0; + pressedKey = -1; + + // If there are no windows to be shown return + if (windows.isEmpty()) { + System.err.println("Error: waitKey must be used after an imshow"); + System.exit(-1); + } + + // Remove the unused windows + Iterator> iter = windows.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + ImageWindow win = entry.getValue(); + if (win.alreadyUsed) { + iter.remove(); + win.frame.dispose(); + } + } + + // (if) Create (else) Update frame + for (ImageWindow win : windows.values()) { + + if (win.img != null) { + + ImageIcon icon = new ImageIcon(toBufferedImage(win.img)); + + if (win.lbl == null) { + JFrame frame = createJFrame(win.name, win.flag); + JLabel lbl = new JLabel(icon); + win.setFrameLabelVisible(frame, lbl); + } else { + win.lbl.setIcon(icon); + } + } else { + System.err.println("Error: no imshow associated with" + " namedWindow: \"" + win.name + "\""); + System.exit(-1); + } + } + + try { + if (delay == 0) { + latch.await(); + } else { + latch.await(delay, TimeUnit.MILLISECONDS); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // Set all windows as already used + for (ImageWindow win : windows.values()) + win.alreadyUsed = true; + + return pressedKey; + } + + public static void destroyWindow(String winname) { + ImageWindow tmpWin = windows.get(winname); + if (tmpWin != null) windows.remove(winname); + } + + public static void destroyAllWindows() { + windows.clear(); + } + + public static void resizeWindow(String winname, int width, int height) { + ImageWindow tmpWin = windows.get(winname); + if (tmpWin != null) tmpWin.setNewDimension(width, height); + } + + public static void moveWindow(String winname, int x, int y) { + ImageWindow tmpWin = windows.get(winname); + if (tmpWin != null) tmpWin.setNewPosition(x, y); + } +} diff --git a/modules/highgui/misc/java/src/java/highgui+ImageWindow.java b/modules/highgui/misc/java/src/java/highgui+ImageWindow.java new file mode 100644 index 0000000000..a8f572f4ae --- /dev/null +++ b/modules/highgui/misc/java/src/java/highgui+ImageWindow.java @@ -0,0 +1,132 @@ +package org.opencv.highgui; + +import org.opencv.core.Mat; +import org.opencv.core.Size; +import org.opencv.imgproc.Imgproc; + +import javax.swing.*; +import java.awt.*; + +/** + * This class was designed to create and manipulate + * the Windows to be used by the HighGui class. + */ +public final class ImageWindow { + + public final static int WINDOW_NORMAL = 0; + public final static int WINDOW_AUTOSIZE = 1; + + public String name; + public Mat img = null; + public Boolean alreadyUsed = false; + public Boolean imgToBeResized = false; + public Boolean windowToBeResized = false; + public Boolean positionToBeChanged = false; + public JFrame frame = null; + public JLabel lbl = null; + public int flag; + public int x = -1; + public int y = -1; + public int width = -1; + public int height = -1; + + public ImageWindow(String name, Mat img) { + this.name = name; + this.img = img; + this.flag = WINDOW_NORMAL; + } + + public ImageWindow(String name, int flag) { + this.name = name; + this.flag = flag; + } + + public static Size keepAspectRatioSize(int original_width, int original_height, int bound_width, int bound_height) { + + int new_width = original_width; + int new_height = original_height; + + if (original_width > bound_width) { + new_width = bound_width; + new_height = (new_width * original_height) / original_width; + } + + if (new_height > bound_height) { + new_height = bound_height; + new_width = (new_height * original_width) / original_height; + } + + return new Size(new_width, new_height); + } + + public void setMat(Mat img) { + + this.img = img; + this.alreadyUsed = false; + + if (imgToBeResized) { + resizeImage(); + imgToBeResized = false; + } + + } + + public void setFrameLabelVisible(JFrame frame, JLabel lbl) { + this.frame = frame; + this.lbl = lbl; + + if (windowToBeResized) { + lbl.setPreferredSize(new Dimension(width, height)); + windowToBeResized = false; + } + + if (positionToBeChanged) { + frame.setLocation(x, y); + positionToBeChanged = false; + } + + frame.add(lbl); + frame.pack(); + frame.setVisible(true); + } + + public void setNewDimension(int width, int height) { + + if (this.width != width || this.height != height) { + this.width = width; + this.height = height; + + if (img != null) { + resizeImage(); + } else { + imgToBeResized = true; + } + + if (lbl != null) { + lbl.setPreferredSize(new Dimension(width, height)); + } else { + windowToBeResized = true; + } + } + } + + public void setNewPosition(int x, int y) { + if (this.x != x || this.y != y) { + this.x = x; + this.y = y; + + if (frame != null) { + frame.setLocation(x, y); + } else { + positionToBeChanged = true; + } + } + } + + private void resizeImage() { + if (flag == WINDOW_NORMAL) { + Size tmpSize = keepAspectRatioSize(img.width(), img.height(), width, height); + Imgproc.resize(img, img, tmpSize); + } + } +}