OCV_MatchTemplate.java Source code

Java tutorial

Introduction

Here is the source code for OCV_MatchTemplate.java

Source

import ij.IJ;
import ij.ImagePlus;
import ij.WindowManager;
import ij.gui.DialogListener;
import ij.gui.GenericDialog;
import ij.gui.Roi;
import ij.measure.ResultsTable;
import ij.plugin.Macro_Runner;
import ij.plugin.filter.PlugInFilterRunner;
import ij.plugin.frame.RoiManager;
import ij.process.ImageProcessor;
import ij.process.FloatProcessor;
import java.awt.AWTEvent;
import java.util.ArrayList;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;

/*
 * The MIT License
 *
 * Copyright 2016 Takehito Nishida.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * matchTemplate (OpenCV3.1).
 */
public class OCV_MatchTemplate implements ij.plugin.filter.ExtendedPlugInFilter, DialogListener {
    // constant var.
    private final int FLAGS = DOES_8G;
    private final String[] TYPE_STR = new String[] { "1 - TM_SQDIFF_NORMED", "TM_CCORR_NORMED",
            "TM_CCOEFF_NORMED" };
    private final int[] TYPE_VAL = new int[] { Imgproc.TM_SQDIFF_NORMED, Imgproc.TM_CCORR_NORMED,
            Imgproc.TM_CCOEFF_NORMED };

    // static var.
    private static int ind_src = 0;
    private static int ind_tmp = 1;
    private static int ind_type = 1;
    private static float thr_res = (float) 0.5;
    private static boolean enResult = true;
    private static boolean enSearchMax = false;

    // var.
    private String title_src = null;
    private ImagePlus imp_src = null;
    private ImagePlus imp_tmp = null;
    private int[] lst_wid;
    private String[] titles;

    @Override
    public int showDialog(ImagePlus imp, String command, PlugInFilterRunner pfr) {
        GenericDialog gd = new GenericDialog(command.trim() + "...");

        gd.addChoice("src", titles, titles[ind_src]);
        gd.addChoice("template", titles, titles[ind_tmp]);
        gd.addChoice("method", TYPE_STR, TYPE_STR[ind_type]);
        gd.addNumericField("threshold_of_results", thr_res, 4);
        gd.addCheckbox("enable_results_table", enResult);
        gd.addCheckbox("enable_search_max_point", enSearchMax);
        gd.addDialogListener(this);

        gd.showDialog();

        if (gd.wasCanceled()) {
            return DONE;
        } else {
            return FLAGS;
        }
    }

    @Override
    public boolean dialogItemChanged(GenericDialog gd, AWTEvent awte) {
        ind_src = (int) gd.getNextChoiceIndex();
        ind_tmp = (int) gd.getNextChoiceIndex();
        ind_type = (int) gd.getNextChoiceIndex();
        thr_res = (float) gd.getNextNumber();
        enResult = (boolean) gd.getNextBoolean();
        enSearchMax = (boolean) gd.getNextBoolean();

        if (Float.isNaN(thr_res)) {
            IJ.showStatus("ERR : NaN");
            return false;
        }
        if (ind_src == ind_tmp) {
            IJ.showStatus("The same image can not be selected.");
            return false;
        }

        imp_src = WindowManager.getImage(lst_wid[ind_src]);
        imp_tmp = WindowManager.getImage(lst_wid[ind_tmp]);
        title_src = imp_src.getShortTitle();

        if (imp_src.getBitDepth() != 8 || imp_tmp.getBitDepth() != 8) {
            IJ.showStatus("The both images should be 8bit gray");
            return false;
        }
        if (imp_src.getWidth() < imp_tmp.getWidth() || imp_src.getHeight() < imp_tmp.getHeight()) {
            IJ.showStatus("The size of src should be larger than the size of template.");
            return false;
        }

        if (enSearchMax) {
            enResult = true;
        }

        IJ.showStatus("OCV_MatchTemplate");
        return true;
    }

    @Override
    public void setNPasses(int nPasses) {
        // do nothing
    }

    @Override
    public int setup(String arg, ImagePlus imp) {
        if (!OCV__LoadLibrary.isLoad()) {
            IJ.error("Library is not loaded.");
            return DONE;
        }

        if (imp == null) {
            IJ.noImage();
            return DONE;
        } else {
            lst_wid = WindowManager.getIDList();

            if (lst_wid == null || lst_wid.length < 2) {
                IJ.error("At least more than 2 images are needed.");
                return DONE;
            }

            titles = new String[lst_wid.length];

            for (int i = 0; i < lst_wid.length; i++) {
                ImagePlus imp2 = WindowManager.getImage(lst_wid[i]);
                titles[i] = imp2 != null ? imp2.getTitle() : "";
            }

            return FLAGS;
        }
    }

    @Override
    public void run(ImageProcessor ip) {
        // src
        byte[] arr_src = (byte[]) imp_src.getChannelProcessor().getPixels();
        int imw_src = imp_src.getWidth();
        int imh_src = imp_src.getHeight();
        Mat mat_src = new Mat(imh_src, imw_src, CvType.CV_8UC1);
        mat_src.put(0, 0, arr_src);

        // tmp
        byte[] arr_tmp = (byte[]) imp_tmp.getChannelProcessor().getPixels();
        int imw_tmp = imp_tmp.getWidth();
        int imh_tmp = imp_tmp.getHeight();
        Mat mat_tmp = new Mat(imh_tmp, imw_tmp, CvType.CV_8UC1);
        mat_tmp.put(0, 0, arr_tmp);

        // dst
        String title_dst = WindowManager.getUniqueName(title_src + "_MatchTemplate");
        int imw_dst = imw_src - imw_tmp + 1;
        int imh_dst = imh_src - imh_tmp + 1;
        ImagePlus imp_dst = new ImagePlus(title_dst, new FloatProcessor(imw_dst, imh_dst));
        float[] arr_dst = (float[]) imp_dst.getChannelProcessor().getPixels();
        Mat mat_dst = new Mat();

        // run
        Imgproc.matchTemplate(mat_src, mat_tmp, mat_dst, TYPE_VAL[ind_type]);
        mat_dst.get(0, 0, arr_dst);
        imp_dst.show();

        if (TYPE_VAL[ind_type] == Imgproc.TM_SQDIFF_NORMED) {
            substracted_from_one(arr_dst);
        }

        IJ.run(imp_dst, "Enhance Contrast", "saturated=0.35");

        // show data
        if (enResult) {
            if (enSearchMax) {
                showData_enSearchMaxPoint(imp_dst, thr_res, imw_tmp, imh_tmp);
            } else {
                showData(arr_dst, imw_dst, imh_dst, imw_tmp, imh_tmp);
            }
        }
    }

    private void showData(float[] arr_dst, int imw_dst, int imh_dst, int imw_tmp, int imh_tmp) {
        // prepare the ResultsTable
        ResultsTable rt = OCV__LoadLibrary.GetResultsTable(true);

        // prepare the ROI Manager
        RoiManager roiMan = OCV__LoadLibrary.GetRoiManager(true, true);

        // show
        Macro_Runner mr = new Macro_Runner();
        mr.runMacro("setBatchMode(true);", "");
        ArrayList<float[]> res = new ArrayList<float[]>();

        for (int y = 0; y < imh_dst; y++) {
            for (int x = 0; x < imw_dst; x++) {
                if (thr_res <= arr_dst[x + y * imw_dst]) {
                    res.add(new float[] { (float) x, (float) y, arr_dst[x + y * imw_dst] });
                }
            }
        }

        int num_match = res.size();

        for (int i = 0; i < num_match; i++) {
            int bx = (int) res.get(i)[0];
            int by = (int) res.get(i)[1];
            float match = res.get(i)[2];

            Roi roi = new Roi(bx, by, imw_tmp, imh_tmp);
            imp_src.setRoi(roi);

            roiMan.addRoi(roi);
            int idx_last = roiMan.getCount() - 1;
            roiMan.select(idx_last);
            roiMan.runCommand("Rename", String.valueOf(i + 1) + "_" + "Match" + "_" + String.valueOf(match));

            rt.incrementCounter();
            rt.addValue("BX", bx);
            rt.addValue("BY", by);
            rt.addValue("Width", imw_tmp);
            rt.addValue("Height", imh_tmp);
            rt.addValue("Match", match);
            rt.show("Results");
        }

        mr.runMacro("setBatchMode(false);", "");
        roiMan.runCommand("Show All");
    }

    private void showData_enSearchMaxPoint(ImagePlus imp_dst, float thr, int imw_tmp, int imh_tmp) {
        ImagePlus imp_bin = imp_dst.duplicate();
        imp_bin.setTitle("__bin");
        float[] arr_bin = (float[]) imp_bin.getChannelProcessor().getPixels();
        binary_float(arr_bin, thr);

        IJ.run(imp_bin, "8-bit", "");
        IJ.run(imp_bin, "OCV ConnectedComponentsWithStats", "connectivity=8-connected enable_output_labeled_image");
        ImagePlus imp_lab = WindowManager.getImage("__bin_Connect8-1");

        ResultsTable rt = ResultsTable.getResultsTable();
        int col_x = rt.getColumnIndex("BX");
        int col_y = rt.getColumnIndex("BY");
        int col_w = rt.getColumnIndex("Width");
        int col_h = rt.getColumnIndex("Height");
        ArrayList<float[]> arr_point_max = new ArrayList<float[]>();

        for (int i = 0; i < rt.size(); i++) {
            int bx = (int) (rt.getValueAsDouble(col_x, i));
            int by = (int) (rt.getValueAsDouble(col_y, i));
            int w = (int) (rt.getValueAsDouble(col_w, i));
            int h = (int) (rt.getValueAsDouble(col_h, i));
            float[] point_max = new float[3];

            Roi roi_blob = new Roi(bx, by, w, h);

            imp_dst.setRoi(roi_blob);
            ImagePlus imp_dst_roi = imp_dst.duplicate();
            float[] arr__dst_roi = (float[]) imp_dst_roi.getChannelProcessor().getPixels();

            imp_lab.setRoi(roi_blob);
            ImagePlus imp_lab_roi = imp_lab.duplicate();
            IJ.run(imp_lab_roi, "32-bit", "");
            float[] arr_lab_roi = (float[]) imp_lab_roi.getChannelProcessor().getPixels();

            search_max_point(arr__dst_roi, arr_lab_roi, w, i + 1, point_max);
            point_max[0] = point_max[0] + (float) bx;
            point_max[1] = point_max[1] + (float) by;
            arr_point_max.add(point_max);

            imp_dst_roi.close();
            imp_lab_roi.close();
        }

        imp_bin.close();
        imp_lab.close();

        // prepare the ResultsTable
        rt.reset();

        // prepare the ROI Manager
        RoiManager roiMan = OCV__LoadLibrary.GetRoiManager(true, true);

        // show
        Macro_Runner mr = new Macro_Runner();
        mr.runMacro("setBatchMode(true);", "");
        int num_match = arr_point_max.size();

        for (int i = 0; i < num_match; i++) {
            int bx = (int) arr_point_max.get(i)[0];
            int by = (int) arr_point_max.get(i)[1];
            float match = arr_point_max.get(i)[2];

            Roi roi = new Roi(bx, by, imw_tmp, imh_tmp);
            imp_src.setRoi(roi);

            roiMan.addRoi(roi);
            int idx_last = roiMan.getCount() - 1;
            roiMan.select(idx_last);
            roiMan.runCommand("Rename", String.valueOf(i + 1) + "_" + "Match" + "_" + String.valueOf(match));

            rt.incrementCounter();
            rt.addValue("BX", bx);
            rt.addValue("BY", by);
            rt.addValue("Width", imw_tmp);
            rt.addValue("Height", imh_tmp);
            rt.addValue("Match", match);
            rt.show("Results");
        }

        mr.runMacro("setBatchMode(false);", "");
        roiMan.runCommand("Show All");
    }

    private void substracted_from_one(float[] srcdst) {
        int num = srcdst.length;

        for (int i = 0; i < num; i++) {
            srcdst[i] = 1 - srcdst[i];
        }
    }

    private void binary_float(float[] srcdst, float thr) {
        int num = srcdst.length;

        for (int i = 0; i < num; i++) {
            if (thr <= srcdst[i]) {
                srcdst[i] = 255;
            } else {
                srcdst[i] = 0;
            }
        }
    }

    private void search_max_point(float[] src, float[] lab, int w, int ind, float[] point_max) {
        int num = src.length;
        float max = src[0];
        int ind_max = 0;

        for (int i = 0; i < num; i++) {
            if (lab[i] == (float) ind && max < src[i]) {
                max = src[i];
                ind_max = i;
            }
        }

        point_max[0] = (float) (ind_max % w);
        point_max[1] = (float) (ind_max / w);
        point_max[2] = max;
    }
}