java - 检测图像中子图像的所有出现

标签 java image opencv image-processing template-matching

最近,我一直在开发一个小型辅助项目,需要能够在另一个图像中找到子图像的 X 和 Y 坐标。图像可能具有不同的分辨率,但总体而言图像分辨率将相似并且颜色应该相同。我查看了 OpenCV,但似乎 OpenCV 只返回一个匹配项。我需要在 super 图像中找到子图像的所有出现/实例。我已经有了要搜索的所有子图像,所以我只需要一种方法来找到 super 图像中子图像的坐标。

这是我的意思的一个例子:

如果我们有 red_circle.png:

Red Circle Subimage

shapes.png:

Shapes Super-Image

我需要获取图片中各种形状 (shapes) 中所有 红色圆圈 (red_circle.png; subimage) 的 X 和 Y 坐标.png; super 图像)。

理想情况下,我希望能够做这样的事情:

/* code to read in red_cirlce.png and shapes.png as BufferedImages */
ArrayList<Point> instancesOfRedCircle = new ArrayList<>();
findAllSubimageInstances( shapesObj, // Super-Image
                          redCircleObj, // Subimage
                          instancesOfRedCircle // ArrayList to put points in
                        );

有没有人知道这样做的方法(例如,库、函数等)?

最佳答案

那天晚上我睡不着..

enter image description here

我已经用 java 编写了教程中的代码,并围绕它构建了一个小图形用户界面。

package opencv.test;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;

import javax.imageio.ImageIO;

import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Scalar;
import org.opencv.features2d.DMatch;
import org.opencv.features2d.DescriptorExtractor;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.features2d.FeatureDetector;
import org.opencv.features2d.Features2d;
import org.opencv.highgui.Highgui;

public class MatchDetection {

    public static BufferedImage detectMatches(File file, File file2) {

        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

        Mat img_1 = Highgui.imread(file.getAbsolutePath(), Highgui.CV_LOAD_IMAGE_GRAYSCALE);
        Mat img_2 = Highgui.imread(file2.getAbsolutePath(), Highgui.CV_LOAD_IMAGE_GRAYSCALE);

        if (img_1.empty() || img_2.empty()) {
            System.out.println(" --(!) Error reading images ");
            return null;
        }

        // -- Step 1: Detect the keypoints using SURF Detector
        //I am not sure where to use it
        int minHessian = 400;

        FeatureDetector detector = FeatureDetector.create(FeatureDetector.SURF);

        MatOfKeyPoint keypoints_1 = new MatOfKeyPoint();
        MatOfKeyPoint keypoints_2 = new MatOfKeyPoint();

        detector.detect(img_1, keypoints_1);
        detector.detect(img_2, keypoints_2);

        // -- Step 2: Calculate descriptors (feature vectors)
        DescriptorExtractor extractor = DescriptorExtractor
                .create(DescriptorExtractor.SURF);

        Mat descriptors_1 = new Mat();
        Mat descriptors_2 = new Mat();

        extractor.compute(img_1, keypoints_1, descriptors_1);
        extractor.compute(img_2, keypoints_2, descriptors_2);

        // -- Step 3: Matching descriptor vectors using FLANN matcher
        DescriptorMatcher matcher = DescriptorMatcher
                .create(DescriptorExtractor.SURF);
        MatOfDMatch matches = new MatOfDMatch();
        matcher.match(descriptors_1, descriptors_2, matches);
        DMatch[] matchesArr = matches.toArray();

        double max_dist = 0;
        double min_dist = 100;

        // -- Quick calculation of max and min distances between keypoints
        for (int i = 0; i < matchesArr.length; i++) {
            double dist = matchesArr[i].distance;
            if (dist < min_dist)
                min_dist = dist;
            if (dist > max_dist)
                max_dist = dist;
        }

        System.out.printf("-- Max dist : %f \n", max_dist);
        System.out.printf("-- Min dist : %f \n", min_dist);

        // -- Draw only "good" matches (i.e. whose distance is less than
        // 2*min_dist,
        // -- or a small arbitary value ( 0.02 ) in the event that min_dist is
        // very
        // -- small)
        // -- PS.- radiusMatch can also be used here.
        MatOfDMatch good_matches = new MatOfDMatch();

        for (int i = 0; i < matchesArr.length; i++) {
            if (matchesArr[i].distance <= Math.max(2 * min_dist, 0.02)) {
                good_matches.push_back(matches.row(i));
            }
        }

        // -- Draw only "good" matches
        Mat img_matches = new Mat();
        Features2d.drawMatches(img_1, keypoints_1, img_2, keypoints_2,
                good_matches, img_matches);//, Scalar.all(-1), Scalar.all(-1),
                //null, Features2d.NOT_DRAW_SINGLE_POINTS);

        // ----Here i had to Patch around a little----
        MatOfByte matOfByte = new MatOfByte();

        Highgui.imencode(".jpg", img_matches, matOfByte);

        byte[] byteArray = matOfByte.toArray();
        BufferedImage bufImage = null;
        try {

            InputStream in = new ByteArrayInputStream(byteArray);
            bufImage = ImageIO.read(in);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        for (int i = 0; i < (int) good_matches.rows(); i++) {
            System.out.printf(
                    "-- Good Match [%d] Keypoint 1: %d  -- Keypoint 2: %d  \n",
                    i, good_matches.toArray()[i].queryIdx,
                    good_matches.toArray()[i].trainIdx);
        }

        return bufImage;

    }
}

和图形用户界面

package opencv.test;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;

public class OpenCVMyGui {

    private JFrame frame;
    ImageResultPanel panel_bot;
    ImageChoosePanel panel_left;
    ImageChoosePanel panel_right;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    OpenCVMyGui window = new OpenCVMyGui();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the application.
     */
    public OpenCVMyGui() {
        initialize();
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
        frame = new JFrame();
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        GridBagLayout gridBagLayout = new GridBagLayout();
        gridBagLayout.columnWidths = new int[]{0, 0, 0};
        gridBagLayout.rowHeights = new int[]{0, 0, 0};
        gridBagLayout.columnWeights = new double[]{1.0, 1.0, Double.MIN_VALUE};
        gridBagLayout.rowWeights = new double[]{1.0, 1.0, Double.MIN_VALUE};
        frame.getContentPane().setLayout(gridBagLayout);

        panel_left = new ImageChoosePanel();
        GridBagConstraints gbc_panel_2 = new GridBagConstraints();
        gbc_panel_2.insets = new Insets(0, 0, 5, 5);
        gbc_panel_2.fill = GridBagConstraints.BOTH;
        gbc_panel_2.gridx = 0;
        gbc_panel_2.gridy = 0;
        frame.getContentPane().add(panel_left, gbc_panel_2);

        panel_right = new ImageChoosePanel();
        GridBagConstraints gbc_panel_1 = new GridBagConstraints();
        gbc_panel_1.insets = new Insets(0, 0, 5, 0);
        gbc_panel_1.fill = GridBagConstraints.BOTH;
        gbc_panel_1.gridx = 1;
        gbc_panel_1.gridy = 0;
        frame.getContentPane().add(panel_right, gbc_panel_1);

        panel_bot = new ImageResultPanel(this);
        GridBagConstraints gbc_panel = new GridBagConstraints();
        gbc_panel.gridwidth = 2;
        gbc_panel.fill = GridBagConstraints.BOTH;
        gbc_panel.gridx = 0;
        gbc_panel.gridy = 1;
        frame.getContentPane().add(panel_bot, gbc_panel);
    }

    private class ImageChoosePanel extends JPanel {

        /**
         * 
         */
        private static final long serialVersionUID = 2207576827793103205L;
        public BufferedImage image;
        public File file;

        public ImageChoosePanel() {
            setFocusable(true);
            addMouseListener(new MouseListener() {

                @Override
                public void mouseReleased(MouseEvent e) {
                }

                @Override
                public void mousePressed(MouseEvent e) {
                }

                @Override
                public void mouseExited(MouseEvent e) {
                }

                @Override
                public void mouseEntered(MouseEvent e) {
                }

                @Override
                public void mouseClicked(MouseEvent e) {
                    JFileChooser chooser = new JFileChooser();
                    chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
                    chooser.setFileFilter(new FileNameExtensionFilter("Images",
                            "jpg", "png")); // maybe more? dont know what OpenCV
                                            // likes
                    chooser.showOpenDialog(ImageChoosePanel.this);
                    ImageChoosePanel icp = ((ImageChoosePanel) e.getSource());
                    icp.file = chooser.getSelectedFile();
                    try {
                        image = ImageIO.read(icp.file);
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                }
            });
        }

        @Override
        public void paint(Graphics arg0) {

            if (image != null) {
                arg0.drawImage(image, 0, 0, null);
            } else{ 
                arg0.fillRect(0, 0, getWidth(), getHeight());
            }
        }
    }

    private class ImageResultPanel extends JPanel {

        /**
         * 
         */
        private static final long serialVersionUID = 8948107638933808175L;
        public BufferedImage image;
        OpenCVMyGui gui;

        public ImageResultPanel(OpenCVMyGui gui) {
            this.gui = gui;
            setFocusable(true);
            addMouseListener(new MouseListener() {

                @Override
                public void mouseReleased(MouseEvent arg0) {
                }

                @Override
                public void mousePressed(MouseEvent arg0) {
                }

                @Override
                public void mouseExited(MouseEvent arg0) {
                }

                @Override
                public void mouseEntered(MouseEvent arg0) {
                }

                @Override
                public void mouseClicked(MouseEvent arg0) {
                    try {
                        OpenCVMyGui gui = ((ImageResultPanel) arg0.getSource()).gui;
                        image = MatchDetection.detectMatches(
                                gui.panel_right.file, gui.panel_left.file);
                    } catch (Exception e2) {
                        e2.printStackTrace();
                    }
                }
            });
        }

        @Override
        public void paint(Graphics arg0) {
            if (image != null) {
                arg0.drawImage(image, 0, 0, null);
            }else{
                arg0.fillRect(0, 0, getWidth(), getHeight());
            }
        }
    }

}

你应该明确地使用这些算法......但结果 atm 应该可以帮助你实现你的目标。

我可能会再读一遍。明天。

关于java - 检测图像中子图像的所有出现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29356639/

相关文章:

python - 通过python算法将Square Hald clut转换为Classic Hald clut

java - Rythm:根据请求创建新引擎或在现有引擎实例上设置语言/区域设置

Javascript 交换 2 个图像

javascript - 如何显示数组中的图像? JavaScript

php - 如何使用 PHP 从 mySQL 数据库获取图像并调整其尺寸?

opencv - 用于OpenCV问题的Video4Linux Y12像素格式

python-2.7 - 使用 ORB 检查重复图像

java - mysql中数据库的创建和删除

java - 如果我的 int 返回方法没有找到数值解,我会返回什么?

java - 如何在需要时从文件中提取元素?