使用 OpenCV 查找图像上数字的边界框
Using OpenCV to find the bounding box of numbers on an image
我试图找到下面 3 张图片中间的数字边界框。
这是我尝试使用的 3 张示例卡片。
我使用的代码基于(几乎是完整的副本)第一个答案 here 中提供的代码,尽管已转换为 Java(C++ 中的答案很好)并添加要合并的轮廓大小的参数(在我的代码中定义为 sizeHorizonal 和 sizeVertical),这是我在下图中使用的两个参数。
MatOfPoint2f approxCurve = new MatOfPoint2f();
Mat imgMAT = new Mat();
Utils.bitmapToMat(bmp32, imgMAT);
Mat grad = new Mat();
Imgproc.cvtColor(imgMAT, grad, Imgproc.COLOR_BGR2GRAY);
Mat img_sobel = new Mat();
Mat img_threshold = new Mat();
Imgproc.Sobel(grad, img_sobel, CvType.CV_8U, 1, 0, 3, 1, 0, Core.BORDER_DEFAULT);
Imgproc.threshold(img_sobel, img_threshold, 0, 255, Imgproc.THRESH_OTSU + Imgproc.THRESH_BINARY);
Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(sizeHorizontal, sizeVertical));
Imgproc.morphologyEx(img_threshold, img_threshold, Imgproc.MORPH_CLOSE, element);
Imgproc.cvtColor(imgMAT, imgMAT, Imgproc.COLOR_BGR2GRAY);
List<MatOfPoint> contours = new ArrayList<>();
Imgproc.findContours(img_threshold, contours, new Mat(), Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_SIMPLE, new org.opencv.core.Point(0, 0));
for (int i = 0; i < contours.size(); i++) {
//Convert contours(i) from MatOfPoint to MatOfPoint2f
MatOfPoint2f contour2f = new MatOfPoint2f( contours.get(i).toArray() );
//Processing on mMOP2f1 which is in type MatOfPoint2f
double approxDistance = Imgproc.arcLength(contour2f, true)*0.02;
Imgproc.approxPolyDP(contour2f, approxCurve, approxDistance, true);
//Convert back to MatOfPoint
MatOfPoint points = new MatOfPoint( approxCurve.toArray() );
// Get bounding rect of contour
org.opencv.core.Rect rect = Imgproc.boundingRect(points);
Imgproc.rectangle(imgMAT, rect.tl(), rect.br(), new Scalar(0, 255, 0), 2);
}
我已经绘制了单独的数字部分轮廓,但我找不到一种方法来隔离我想要的轮廓。这是用尺寸输入参数勾勒出轮廓的区域。正如您在第二张图片中看到的那样,这完全符合我的要求,并且绘制了整个数字的轮廓,而不是每个部分。
1:尺寸参数输入:17、5
2:尺寸参数输入:23、7
3:尺寸参数输入:23、13
所以,我需要帮助的事情:
- 将中间的四个轮廓隔离开来,想办法将这些轮廓合并在一起
我考虑过采用与给定纵横比匹配的轮廓并裁剪到包含所有这些轮廓的边界框,但周围还有其他具有相似比例的轮廓。
- 找到一种自动选择正确尺寸参数的方法(因为每种卡类型需要不同的参数来隔离数字)
如果不尝试所有 3 种尺寸输入并查看给出预期轮廓的是什么,我可以使用流行的颜色作为卡片类型的指示器,然后使用该卡片类型的参数。但是,任何其他建议都会有所帮助,因为我觉得有更好的方法来做到这一点。
非常感谢!
在我百忙之中,我可以帮助你完成一些绝种。请找到下面的代码,这将帮助您获得前两张图片。微调第三张图片。只需使用形态学操作即可获得所需的输出。
//#include "stdafx.h"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include "tchar.h"
using namespace cv;
using namespace std;
#define INPUT_FILE "p.jpg"
#define OUTPUT_FOLDER_PATH string("")
int _tmain(int argc, _TCHAR* argv[])
{
Mat large = imread(INPUT_FILE);
Mat rgb;
// downsample and use it for processing
pyrDown(large, rgb);
Mat small;
cvtColor(rgb, small, CV_BGR2GRAY);
// morphological gradient
Mat grad;
Mat morphKernel = getStructuringElement(MORPH_ELLIPSE, Size(2, 2));
Mat morphKernel1 = getStructuringElement(MORPH_ELLIPSE, Size(1, 1));
morphologyEx(small, grad, MORPH_GRADIENT, morphKernel);
// binarize
Mat bw;
threshold(grad, bw, 5.0, 50.0, THRESH_BINARY | THRESH_OTSU);
// connect horizontally oriented regions
Mat connected;
morphKernel = getStructuringElement(MORPH_RECT, Size(9, 1));
morphologyEx(bw, connected, MORPH_CLOSE, morphKernel);
morphologyEx(bw, connected, MORPH_OPEN, morphKernel1);
morphologyEx(connected, connected, MORPH_CLOSE, morphKernel);
morphologyEx(connected, connected, MORPH_CLOSE, morphKernel);
morphologyEx(connected, connected, MORPH_CLOSE, morphKernel);
// find contours
Mat mask = Mat::zeros(bw.size(), CV_8UC1);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(connected, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
// filter contours
int y=0;
for(int idx = 0; idx >= 0; idx = hierarchy[idx][0])
{
Rect rect = boundingRect(contours[idx]);
Mat maskROI(mask, rect);
maskROI = Scalar(0, 0, 0);
// fill the contour
drawContours(mask, contours, idx, Scalar(255, 255, 255), CV_FILLED);
double a=contourArea( contours[idx],false);
if(a> 575)
{
rectangle(rgb, rect, Scalar(0, 255, 0), 2);
y++;
}
imshow("Result1",rgb);
}
cout<<" The number of elements"<<y<< endl;
imshow("Result",mask);
imwrite(OUTPUT_FOLDER_PATH + string("rgb.jpg"), rgb);
waitKey(0);
return 0;
}
我试图找到下面 3 张图片中间的数字边界框。
这是我尝试使用的 3 张示例卡片。
我使用的代码基于(几乎是完整的副本)第一个答案 here 中提供的代码,尽管已转换为 Java(C++ 中的答案很好)并添加要合并的轮廓大小的参数(在我的代码中定义为 sizeHorizonal 和 sizeVertical),这是我在下图中使用的两个参数。
MatOfPoint2f approxCurve = new MatOfPoint2f();
Mat imgMAT = new Mat();
Utils.bitmapToMat(bmp32, imgMAT);
Mat grad = new Mat();
Imgproc.cvtColor(imgMAT, grad, Imgproc.COLOR_BGR2GRAY);
Mat img_sobel = new Mat();
Mat img_threshold = new Mat();
Imgproc.Sobel(grad, img_sobel, CvType.CV_8U, 1, 0, 3, 1, 0, Core.BORDER_DEFAULT);
Imgproc.threshold(img_sobel, img_threshold, 0, 255, Imgproc.THRESH_OTSU + Imgproc.THRESH_BINARY);
Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(sizeHorizontal, sizeVertical));
Imgproc.morphologyEx(img_threshold, img_threshold, Imgproc.MORPH_CLOSE, element);
Imgproc.cvtColor(imgMAT, imgMAT, Imgproc.COLOR_BGR2GRAY);
List<MatOfPoint> contours = new ArrayList<>();
Imgproc.findContours(img_threshold, contours, new Mat(), Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_SIMPLE, new org.opencv.core.Point(0, 0));
for (int i = 0; i < contours.size(); i++) {
//Convert contours(i) from MatOfPoint to MatOfPoint2f
MatOfPoint2f contour2f = new MatOfPoint2f( contours.get(i).toArray() );
//Processing on mMOP2f1 which is in type MatOfPoint2f
double approxDistance = Imgproc.arcLength(contour2f, true)*0.02;
Imgproc.approxPolyDP(contour2f, approxCurve, approxDistance, true);
//Convert back to MatOfPoint
MatOfPoint points = new MatOfPoint( approxCurve.toArray() );
// Get bounding rect of contour
org.opencv.core.Rect rect = Imgproc.boundingRect(points);
Imgproc.rectangle(imgMAT, rect.tl(), rect.br(), new Scalar(0, 255, 0), 2);
}
我已经绘制了单独的数字部分轮廓,但我找不到一种方法来隔离我想要的轮廓。这是用尺寸输入参数勾勒出轮廓的区域。正如您在第二张图片中看到的那样,这完全符合我的要求,并且绘制了整个数字的轮廓,而不是每个部分。
1:尺寸参数输入:17、5
2:尺寸参数输入:23、7
3:尺寸参数输入:23、13
所以,我需要帮助的事情:
- 将中间的四个轮廓隔离开来,想办法将这些轮廓合并在一起
我考虑过采用与给定纵横比匹配的轮廓并裁剪到包含所有这些轮廓的边界框,但周围还有其他具有相似比例的轮廓。
- 找到一种自动选择正确尺寸参数的方法(因为每种卡类型需要不同的参数来隔离数字)
如果不尝试所有 3 种尺寸输入并查看给出预期轮廓的是什么,我可以使用流行的颜色作为卡片类型的指示器,然后使用该卡片类型的参数。但是,任何其他建议都会有所帮助,因为我觉得有更好的方法来做到这一点。
非常感谢!
在我百忙之中,我可以帮助你完成一些绝种。请找到下面的代码,这将帮助您获得前两张图片。微调第三张图片。只需使用形态学操作即可获得所需的输出。
//#include "stdafx.h"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include "tchar.h"
using namespace cv;
using namespace std;
#define INPUT_FILE "p.jpg"
#define OUTPUT_FOLDER_PATH string("")
int _tmain(int argc, _TCHAR* argv[])
{
Mat large = imread(INPUT_FILE);
Mat rgb;
// downsample and use it for processing
pyrDown(large, rgb);
Mat small;
cvtColor(rgb, small, CV_BGR2GRAY);
// morphological gradient
Mat grad;
Mat morphKernel = getStructuringElement(MORPH_ELLIPSE, Size(2, 2));
Mat morphKernel1 = getStructuringElement(MORPH_ELLIPSE, Size(1, 1));
morphologyEx(small, grad, MORPH_GRADIENT, morphKernel);
// binarize
Mat bw;
threshold(grad, bw, 5.0, 50.0, THRESH_BINARY | THRESH_OTSU);
// connect horizontally oriented regions
Mat connected;
morphKernel = getStructuringElement(MORPH_RECT, Size(9, 1));
morphologyEx(bw, connected, MORPH_CLOSE, morphKernel);
morphologyEx(bw, connected, MORPH_OPEN, morphKernel1);
morphologyEx(connected, connected, MORPH_CLOSE, morphKernel);
morphologyEx(connected, connected, MORPH_CLOSE, morphKernel);
morphologyEx(connected, connected, MORPH_CLOSE, morphKernel);
// find contours
Mat mask = Mat::zeros(bw.size(), CV_8UC1);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(connected, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
// filter contours
int y=0;
for(int idx = 0; idx >= 0; idx = hierarchy[idx][0])
{
Rect rect = boundingRect(contours[idx]);
Mat maskROI(mask, rect);
maskROI = Scalar(0, 0, 0);
// fill the contour
drawContours(mask, contours, idx, Scalar(255, 255, 255), CV_FILLED);
double a=contourArea( contours[idx],false);
if(a> 575)
{
rectangle(rgb, rect, Scalar(0, 255, 0), 2);
y++;
}
imshow("Result1",rgb);
}
cout<<" The number of elements"<<y<< endl;
imshow("Result",mask);
imwrite(OUTPUT_FOLDER_PATH + string("rgb.jpg"), rgb);
waitKey(0);
return 0;
}