将给定“类型”的图像文件加载到OpenCV Mat中?

3
我最近开始使用OpenCV的Java绑定来制作一个快速而简单的项目,用于模板匹配。基本上,我正在尝试将一组jpg图像(保存在MS Paint中)读入Mat,然后使用模板匹配从使用Java.Robot拍摄的屏幕截图中找到它们的位置。
当进行模板匹配时,会抛出以下错误。
OpenCV Error: Assertion failed ((depth == CV_8U || depth == CV_32F) 
&& type == _templ.type() && _img.dims() <= 2) in cv::matchTemplate

在搜索后,看起来问题是我正在尝试使用的两个Mat没有相同的“类型”。我不确定这是什么意思。我猜这是MatCvType,如果我打印出图像和模板的CvType,我得到一个4 == CvType.CV_32SC1type()作为我的模板,我得到一个20 == CvType.CV_32SC3type()

但我觉得这不是我要比较的正确type(),我有一种感觉它指的是数据存储在Mat中的数据类型?但我没有好的链接来支持这个想法,只是从许多SO搜索中记住的。

这是我加载jpg图像到Mat中的代码:

Mat pic_ = Imgcodecs.imread("MyPath\\image.jpg");
pic_.convertTo(pic_, CvType.CV_32SC1); 

这里第二行将我的type()从20更改为16,尽管根据我上次的评论,我不认为这是将Mat更改以匹配图像的正确方法?因为将此Mat进行convertTo以匹配屏幕截图的类型(如下)无法修复错误?

以下是我创建图像Mat的方式。

Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage screenShot = rob.createScreenCapture(screenRect);
Mat screenImage = bufferedImageToMat(screenShot);

我首先使用 Java.Robot.createScreenCapture 拍摄屏幕截图,然后使用 Mat 进行转换。

private Mat bufferedImageToMat(BufferedImage inBuffImg) 
{
    BufferedImage image = new BufferedImage(inBuffImg.getWidth(), inBuffImg.getHeight(), BufferedImage.TYPE_INT_RGB);
    Graphics2D g2d= image.createGraphics();
    g2d.drawImage(inBuffImg, 0, 0, null);
    g2d.dispose();

    Mat mat = new Mat(image.getHeight(), image.getWidth(), CvType.CV_32SC1);
    int[] data = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
    mat.put(0, 0, data);

    return mat;
}

从我所了解的情况来看,由Robot创建的BufferedImage类型为BufferedImage.TYPE_3BYTE_BGR,这导致我在尝试获取像素数据时出现错误“DataBufferInt不能转换为DataBufferByte”。因此,根据链接的问题,我将BufferedImage重绘为类型BufferedImage.TYPE_INT_RGB,并将数据作为DataBufferInt提取。

所以说,总的来说,我应该尝试匹配Mat.type()还是我的问题在其他地方?如果不是在其他地方,如何修改其中一个Mat,使其可以与Imgproc.matchTemplate正确使用?

我觉得最简单的解决方案是将从文件加载的图像转换为与屏幕截图的Mat相匹配吗?

编辑: 导致错误的精确代码部分如下

// Mat imageTemplate is a function argument; the loaded jpg image
// Take a picture of the screen
Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage screenShot = rob.createScreenCapture(screenRect);
Mat screenImage = bufferedImageToMat(screenShot);

// Create the result matrix
int result_cols = screenImage.cols() - imageTemplate.cols() + 1;
int result_rows = screenImage.rows() - imageTemplate.rows() + 1;
Mat result = new Mat(result_rows, result_cols, CvType.CV_32SC1);

newStatus("ScreenType: " + screenImage.type());
newStatus("TemplaType: " + imageTemplate.type());

// Choose a matching method
int matchMethod = Imgproc.TM_SQDIFF_NORMED;

// Do the Matching and Normalize
Imgproc.matchTemplate(screenImage, imageTemplate, result, matchMethod);
// Error occurs on previous line

1
你可能需要将模板转换为灰度。请参考如何使用 cvtColor(... , COLOR_BGR2GRAY) - Miki
模板和图像都应该是灰度的吗? - KDecker
无论是灰度还是彩色。 - Miki
我不是在卖弄聪明,但我相当确定它们都是颜色吧? - KDecker
1
嗯,我想这有点解决了我的问题。所以在我的函数bufferedImageToMat中,如果我使用CvType.CV_32FC3并且在转换读取的模板时使用相同的类型,它就“可以工作”。尽管从屏幕图像中读取的数据没有写入函数中的Mat,因为我遇到了“Mat数据类型不兼容:21”的错误,当我第一次使用该函数时(尽管它是16)。//我想我不确定从这里问什么,除了初学者的问题“出了什么问题”..? - KDecker
显示剩余4条评论
1个回答

4

正如@Miki在评论中指出的那样,答案是让图像和模板的通道类型匹配。最终我改变了我的bufferedImageToMat函数。

private Mat bufferedImageToMat(BufferedImage inBuffImg) 
{
    BufferedImage image = new BufferedImage(inBuffImg.getWidth(), inBuffImg.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
    Graphics2D g2d= image.createGraphics();
    g2d.drawImage(inBuffImg, 0, 0, null);
    g2d.dispose();

    Mat mat = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC3);
    byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
    mat.put(0, 0, data);

    return mat;
}

我的模板是以 CvType.CV_8UC3 的格式读入的,所以只需要使用该类型从屏幕图像创建一个 Mat 对象即可!


如果这是答案,请不要忘记将其标记为答案。很高兴你成功解决了! - Rachel L
1
因为回答自己的问题,我必须等待两天,但最终我会得到答案! - KDecker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接