7

我正在使用模板匹配创建一个简单的 openCV 应用程序,我需要比较在大图像中找到一个小图像并将结果返回为 true(如果找到匹配)或 false(未找到匹配)。

    Imgproc.matchTemplate(largeImage, smallImage, result, matchMethod);
    Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());

    MinMaxLocResult mmr = Core.minMaxLoc(result);

    double minMaxValue = 1;
    if (matchMethod== Imgproc.TM_SQDIFF || matchMethod== Imgproc.TM_SQDIFF_NORMED)
    {
        minMaxValue = mmr.minVal;
        useMinThreshold = true;
    }
    else
    {
        minMaxValue = mmr.maxVal;
    }

现在问题在于使用这个 minMaxValue 做出决定(真/假)。我知道上述两种方法 TM_SQDIFF 和 TM_SQDIFF_NORMED 返回低值,而其他方法返回高值,因此我可以有 2 个不同的阈值并比较一个阈值(取决于模板方法类型)。

因此,如果有人能解释 MinMaxLocResult 返回的 minVal 和 maxVal 范围是多少,那就太好了。

它是0到1的范围吗?

如果是,对于 Max 类型模板方法值 1 是否完美匹配?

4

4 回答 4

10

MinMaxLocResult 不返回minValmaxVal范围。minVal并且只是链接maxVal中可以看到的最小和最大匹配分数。

该结构MinMaxLocResult还具有minLocmaxLoc类型的属性Point,给出匹配的位置。假设您使用TM_SQDIFForTM_SQDIFF_NORMED作为匹配标准,最佳匹配位置将是mmr.minLoc.

为了设置检测阈值,您可以声明一个变量double thresholdMatch并通过实验设置其值。如果 minVal < thresholdMatch 则可以说检测到目标对象

于 2013-07-22T10:20:27.830 回答
4

不要标准化结果,然后它会给出正确的值,我的意思是删除这一行

   Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
于 2013-12-17T07:27:07.493 回答
0

条款

  • 模板=我们试图找到的图像
  • haystack = 我们正在搜索的图像
  • region = haystack 中我们当前与模板匹配的区域

根据模板匹配类型,最小值和最大值将具有不同的可能范围。干草堆中每个位置的比较结果由以下公式确定(取自opencv 文档T()是模板,I()是干草堆):

模板匹配算法

正如您所指出的,平方差方法 (SQDIFF) 随着模板和区域之间的差异变大而变大,因此最佳匹配将具有最低值。对于其他方法(互相关、相关系数),最佳匹配将具有最高值。

范围本身很难确定您是否不了解数学(如我自己),但看看平方差我认为范围是(假设图像是 1 字节灰度):

0 ... (255 * 255 * template.width * template.height)

对于标准化版本,范围应为:

0 ... 1

于 2020-04-07T23:02:59.720 回答
0

faithk的答案非常好,但这里有一些实质上实现它的实际代码。我使用0.1作为阈值取得了不错的成绩:

import lombok.val;
import org.opencv.core.*;
import org.springframework.core.io.ClassPathResource;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

import static javax.imageio.ImageIO.read;
import static javax.imageio.ImageIO.write;
import static javax.swing.SwingUtilities.invokeAndWait;
import static org.opencv.core.CvType.CV_32FC1;
import static org.opencv.highgui.HighGui.imshow;
import static org.opencv.highgui.HighGui.waitKey;
import static org.opencv.imgcodecs.Imgcodecs.CV_LOAD_IMAGE_UNCHANGED;
import static org.opencv.imgcodecs.Imgcodecs.imdecode;
import static org.opencv.imgproc.Imgproc.*;

public class TemplateMatcher
{
    static
    {
        // loadNativeOpenCVLibrary();
    }

    private static final int MATCH_METHOD = TM_SQDIFF_NORMED;

    private static Mat BufferedImage2Mat(BufferedImage image) throws IOException
    {
        try (val byteArrayOutputStream = new ByteArrayOutputStream())
        {
            write(image, "jpg", byteArrayOutputStream);
            byteArrayOutputStream.flush();
            val matOfByte = new MatOfByte(byteArrayOutputStream.toByteArray());
            return imdecode(matOfByte, CV_LOAD_IMAGE_UNCHANGED);
        }
    }

    public static Point performTemplateMatching(BufferedImage bigImage, BufferedImage templateImage,
                                                double detectionThreshold, boolean showMatch) throws IOException
    {
        val image = BufferedImage2Mat(bigImage);
        val template = BufferedImage2Mat(templateImage);

        // Create the result matrix
        val result_cols = image.cols() - template.cols() + 1;
        val result_rows = image.rows() - template.rows() + 1;
        val result = new Mat(result_rows, result_cols, CV_32FC1);

        // Do the matching
        matchTemplate(image, template, result, MATCH_METHOD);

        // Localize the best match
        val minMaxLocResult = Core.minMaxLoc(result);

        // / Show me what you got
        val matchedLocation = minMaxLocResult.minLoc;
        rectangle(image, matchedLocation, new Point(matchedLocation.x + template.cols(),
                matchedLocation.y + template.rows()), new Scalar(0, 255, 0));

        if (showMatch)
        {
            try
            {
                invokeAndWait(() -> imshow("Image Search", image));
            } catch (InterruptedException | InvocationTargetException exception)
            {
                exception.printStackTrace();
            }
            waitKey();
        }

        // Determine whether this sub image has been found
        val minVal = minMaxLocResult.minVal;
        if (minVal < detectionThreshold)
        {
            return minMaxLocResult.maxLoc;
        }

        return null;
    }

    public static BufferedImage getBufferedImage(String classpathFile) throws IOException
    {
        val classPathResource = new ClassPathResource(classpathFile);
        val filePath = classPathResource.getFile();
        return read(filePath);
    }
}
于 2018-08-11T14:47:25.490 回答