OpenCV - C++ 到 Java - 模板匹配
OpenCV - C++ to Java - Template Match
正如我在此处询问的那样,我正在尝试检测图像中的多个正方形(标记)Detect Marker Position in 2D image
有人向我展示了一个 C++ 解决方案,这里是:
#include <iostream>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
//See: http://docs.opencv.org/doc/tutorials/imgproc/histograms/template_matching/template_matching.html
//See: http://answers.opencv.org/question/60382/detect-markers-position-in-2d-images/
int main() {
cv::Mat img, templateImg, result;
cv::VideoCapture capture("http://answers.opencv.org/upfiles/14297307634571599.png");
if(capture.isOpened()) {
capture >> img;
} else {
return -1;
}
capture = cv::VideoCapture("http://answers.opencv.org/upfiles/14297308125543022.png");
if(capture.isOpened()) {
capture >> templateImg;
} else {
return -1;
}
/// Reduce the size of the image to display it on my screen
cv::resize(img, img, cv::Size(), 0.5, 0.5);
/// Reduce the size of the template image
/// (first to fit the size used to create the image test, second to fit the size of the reduced image)
cv::resize(templateImg, templateImg, cv::Size(), 0.25, 0.25);
cv::Mat img_display;
img.copyTo(img_display);
// Create the result matrix
int result_cols = img.cols - templateImg.cols + 1;
int result_rows = img.rows - templateImg.rows + 1;
result.create(result_rows, result_cols, CV_32FC1);
/// Do the Matching and Normalize
cv::matchTemplate(img, templateImg, result, CV_TM_CCORR_NORMED);
cv::normalize(result, result, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
/// Localizing the best match with minMaxLoc
double minVal; double maxVal; cv::Point minLoc; cv::Point maxLoc;
cv::Point matchLoc;
for(;;) {
cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat());
matchLoc = maxLoc;
std::cout << "Max correlation=" << maxVal << std::endl;
if(maxVal < 0.8) {
break;
}
/// Show me what you got
cv::rectangle(img_display, matchLoc, cv::Point(matchLoc.x + templateImg.cols , matchLoc.y + templateImg.rows),
cv::Scalar::all(0), 2, 8, 0);
cv::rectangle(result, cv::Point(matchLoc.x - templateImg.cols/2 , matchLoc.y - templateImg.rows/2),
cv::Point(matchLoc.x + templateImg.cols/2 , matchLoc.y + templateImg.rows/2 ), cv::Scalar::all(0), 2, 8, 0);
cv::imshow("result", result);
cv::waitKey(0);
/// Fill the detected location with a rectangle of zero
cv::rectangle(result, cv::Point( matchLoc.x - templateImg.cols/2 , matchLoc.y - templateImg.rows/2),
cv::Point(matchLoc.x + templateImg.cols/2 , matchLoc.y + templateImg.rows/2 ), cv::Scalar::all(0), -1);
} while (maxVal > 0.9);
cv::imshow("result", result);
cv::imshow("img_display", img_display);
cv::waitKey(0);
return 0;
}
for 循环负责找到多个标记并检测它,我正在尝试使其适应我的 java 代码,但我得到了一个无限循环,这是我的代码:
public void run(String inFile, String templateFile, String outFile, int match_method) {
System.out.println("\nRunning Template Matching");
Mat img = Highgui.imread(inFile);
Mat templ = Highgui.imread(templateFile);
// / Create the result matrix
int result_cols = img.cols() - templ.cols() + 1;
int result_rows = img.rows() - templ.rows() + 1;
Mat result = new Mat(result_rows, result_cols, CvType.CV_32FC1);
// / Do the Matching and Normalize
Imgproc.matchTemplate(img, templ, result, match_method);
Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
Point matchLoc;
Point maxLoc;
Point minLoc;
MinMaxLocResult mmr;
boolean iterate = true;
while(iterate){
// / Localizing the best match with minMaxLoc
mmr = Core.minMaxLoc(result);
matchLoc = mmr.maxLoc;
if(mmr.maxVal < 0.8)
{
iterate = false;
}
// / Show me what you got
Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(),
matchLoc.y + templ.rows()), new Scalar(0, 255, 0));
}
// Save the visualized detection.
System.out.println("Writing "+ outFile);
Highgui.imwrite(outFile, img);
}
我注意到函数 minMaxLoc 在 c++ 中比在 java 中有更多的参数,也许这就是问题所在?为什么我无法在 java 中获得相同的行为,有人可以帮助我吗?
非常感谢您
您忘记绘制第二个矩形。原代码:
cv::rectangle(result, cv::Point(matchLoc.x - templateImg.cols/2 , matchLoc.y - templateImg.rows/2),
cv::Point(matchLoc.x + templateImg.cols/2 , matchLoc.y + templateImg.rows/2 ), cv::Scalar::all(0), 2, 8, 0);
...
/// Fill the detected location with a rectangle of zero
cv::rectangle(result, cv::Point( matchLoc.x - templateImg.cols/2 , matchLoc.y - templateImg.rows/2),
cv::Point(matchLoc.x + templateImg.cols/2 , matchLoc.y + templateImg.rows/2 ), cv::Scalar::all(0), -1);
但是在你的代码中只有这个:
Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(),
matchLoc.y + templ.rows()), new Scalar(0, 255, 0));
原始代码的第二部分很可能是关键 - 它绘制填充,而不只是像第一行那样的形状。
顺便说一下,这一行 Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()), new Scalar(0, 255, 0));
也可能是错误的 - 1) 您应该在 result
垫子上绘制矩形,而不是在 img
垫子上。 2) 在原始代码中有 Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()), new Scalar(0, 255, 0));
而在你的代码中有 new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows())
。你确定没问题吗?
虽然它可能有帮助,但您不需要 OpenCV
/ JavaCV
来完成这个相对简单的任务。
基本上,您想在图像中找到以下区域:
a: all black
b: square
c: a percentage of size of the total image
首先,对图像进行阈值处理,使所有像素不是黑色就是白色。
我的方法是通过逐像素扫描图像直到找到黑色像素来找到连通分量。然后 'flood fill' 直到你收集了所有的黑色像素。标记它们(给它们涂上不同的颜色)并将它们存储在某种结构(点列表)中并重复直到到达图像的最后一个像素。
对于每个组件:
- 确定边界框(最小和最大 x 和 y 值)。
- 计算
框的大小并确定它是否足够小/足够大
- 计算该框中匹配像素的百分比。如果是
接近 100%,你有一个正方形。
您可以将匹配的边界框绘制回原始图像。
正如 cyriel 所说,您忘记为最大位置填充零,因此您得到了无限循环。可能是他忘了跟你解释,
for each iteration
find the max location
check if max value is greater than desired threshold
if true
show me what is max
else
break // not found anything that matches
make the existing max to be zero and continue to search for other max// you forgot this and hence infinite loop
end
为了更深入地了解问题,
您可以在 c++ 代码中注释以下行并 运行 它,您将遇到类似的问题。 (此处 cv::Scalar::all(0),-1 args 表示 fill 现有 max/found 区域为 0 并继续)
// Fill the detected location with a rectangle of zero
cv::rectangle(result, cv::Point(matchLoc.x - templateImg.cols / 2, matchLoc.y - templateImg.rows / 2),
cv::Point(matchLoc.x + templateImg.cols / 2, matchLoc.y + templateImg.rows / 2), cv::Scalar::all(0), -1);
希望对您有所帮助。
正如我在此处询问的那样,我正在尝试检测图像中的多个正方形(标记)Detect Marker Position in 2D image
有人向我展示了一个 C++ 解决方案,这里是:
#include <iostream>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
//See: http://docs.opencv.org/doc/tutorials/imgproc/histograms/template_matching/template_matching.html
//See: http://answers.opencv.org/question/60382/detect-markers-position-in-2d-images/
int main() {
cv::Mat img, templateImg, result;
cv::VideoCapture capture("http://answers.opencv.org/upfiles/14297307634571599.png");
if(capture.isOpened()) {
capture >> img;
} else {
return -1;
}
capture = cv::VideoCapture("http://answers.opencv.org/upfiles/14297308125543022.png");
if(capture.isOpened()) {
capture >> templateImg;
} else {
return -1;
}
/// Reduce the size of the image to display it on my screen
cv::resize(img, img, cv::Size(), 0.5, 0.5);
/// Reduce the size of the template image
/// (first to fit the size used to create the image test, second to fit the size of the reduced image)
cv::resize(templateImg, templateImg, cv::Size(), 0.25, 0.25);
cv::Mat img_display;
img.copyTo(img_display);
// Create the result matrix
int result_cols = img.cols - templateImg.cols + 1;
int result_rows = img.rows - templateImg.rows + 1;
result.create(result_rows, result_cols, CV_32FC1);
/// Do the Matching and Normalize
cv::matchTemplate(img, templateImg, result, CV_TM_CCORR_NORMED);
cv::normalize(result, result, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
/// Localizing the best match with minMaxLoc
double minVal; double maxVal; cv::Point minLoc; cv::Point maxLoc;
cv::Point matchLoc;
for(;;) {
cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat());
matchLoc = maxLoc;
std::cout << "Max correlation=" << maxVal << std::endl;
if(maxVal < 0.8) {
break;
}
/// Show me what you got
cv::rectangle(img_display, matchLoc, cv::Point(matchLoc.x + templateImg.cols , matchLoc.y + templateImg.rows),
cv::Scalar::all(0), 2, 8, 0);
cv::rectangle(result, cv::Point(matchLoc.x - templateImg.cols/2 , matchLoc.y - templateImg.rows/2),
cv::Point(matchLoc.x + templateImg.cols/2 , matchLoc.y + templateImg.rows/2 ), cv::Scalar::all(0), 2, 8, 0);
cv::imshow("result", result);
cv::waitKey(0);
/// Fill the detected location with a rectangle of zero
cv::rectangle(result, cv::Point( matchLoc.x - templateImg.cols/2 , matchLoc.y - templateImg.rows/2),
cv::Point(matchLoc.x + templateImg.cols/2 , matchLoc.y + templateImg.rows/2 ), cv::Scalar::all(0), -1);
} while (maxVal > 0.9);
cv::imshow("result", result);
cv::imshow("img_display", img_display);
cv::waitKey(0);
return 0;
}
for 循环负责找到多个标记并检测它,我正在尝试使其适应我的 java 代码,但我得到了一个无限循环,这是我的代码:
public void run(String inFile, String templateFile, String outFile, int match_method) {
System.out.println("\nRunning Template Matching");
Mat img = Highgui.imread(inFile);
Mat templ = Highgui.imread(templateFile);
// / Create the result matrix
int result_cols = img.cols() - templ.cols() + 1;
int result_rows = img.rows() - templ.rows() + 1;
Mat result = new Mat(result_rows, result_cols, CvType.CV_32FC1);
// / Do the Matching and Normalize
Imgproc.matchTemplate(img, templ, result, match_method);
Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
Point matchLoc;
Point maxLoc;
Point minLoc;
MinMaxLocResult mmr;
boolean iterate = true;
while(iterate){
// / Localizing the best match with minMaxLoc
mmr = Core.minMaxLoc(result);
matchLoc = mmr.maxLoc;
if(mmr.maxVal < 0.8)
{
iterate = false;
}
// / Show me what you got
Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(),
matchLoc.y + templ.rows()), new Scalar(0, 255, 0));
}
// Save the visualized detection.
System.out.println("Writing "+ outFile);
Highgui.imwrite(outFile, img);
}
我注意到函数 minMaxLoc 在 c++ 中比在 java 中有更多的参数,也许这就是问题所在?为什么我无法在 java 中获得相同的行为,有人可以帮助我吗?
非常感谢您
您忘记绘制第二个矩形。原代码:
cv::rectangle(result, cv::Point(matchLoc.x - templateImg.cols/2 , matchLoc.y - templateImg.rows/2),
cv::Point(matchLoc.x + templateImg.cols/2 , matchLoc.y + templateImg.rows/2 ), cv::Scalar::all(0), 2, 8, 0);
...
/// Fill the detected location with a rectangle of zero
cv::rectangle(result, cv::Point( matchLoc.x - templateImg.cols/2 , matchLoc.y - templateImg.rows/2),
cv::Point(matchLoc.x + templateImg.cols/2 , matchLoc.y + templateImg.rows/2 ), cv::Scalar::all(0), -1);
但是在你的代码中只有这个:
Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(),
matchLoc.y + templ.rows()), new Scalar(0, 255, 0));
原始代码的第二部分很可能是关键 - 它绘制填充,而不只是像第一行那样的形状。
顺便说一下,这一行 Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()), new Scalar(0, 255, 0));
也可能是错误的 - 1) 您应该在 result
垫子上绘制矩形,而不是在 img
垫子上。 2) 在原始代码中有 Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()), new Scalar(0, 255, 0));
而在你的代码中有 new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows())
。你确定没问题吗?
虽然它可能有帮助,但您不需要 OpenCV
/ JavaCV
来完成这个相对简单的任务。
基本上,您想在图像中找到以下区域:
a: all black
b: square
c: a percentage of size of the total image
首先,对图像进行阈值处理,使所有像素不是黑色就是白色。
我的方法是通过逐像素扫描图像直到找到黑色像素来找到连通分量。然后 'flood fill' 直到你收集了所有的黑色像素。标记它们(给它们涂上不同的颜色)并将它们存储在某种结构(点列表)中并重复直到到达图像的最后一个像素。
对于每个组件:
- 确定边界框(最小和最大 x 和 y 值)。
- 计算 框的大小并确定它是否足够小/足够大
- 计算该框中匹配像素的百分比。如果是 接近 100%,你有一个正方形。
您可以将匹配的边界框绘制回原始图像。
正如 cyriel 所说,您忘记为最大位置填充零,因此您得到了无限循环。可能是他忘了跟你解释,
for each iteration
find the max location
check if max value is greater than desired threshold
if true
show me what is max
else
break // not found anything that matches
make the existing max to be zero and continue to search for other max// you forgot this and hence infinite loop
end
为了更深入地了解问题, 您可以在 c++ 代码中注释以下行并 运行 它,您将遇到类似的问题。 (此处 cv::Scalar::all(0),-1 args 表示 fill 现有 max/found 区域为 0 并继续)
// Fill the detected location with a rectangle of zero
cv::rectangle(result, cv::Point(matchLoc.x - templateImg.cols / 2, matchLoc.y - templateImg.rows / 2),
cv::Point(matchLoc.x + templateImg.cols / 2, matchLoc.y + templateImg.rows / 2), cv::Scalar::all(0), -1);
希望对您有所帮助。