使用 findContours 时如何避免检测到图像帧
Posted
技术标签:
【中文标题】使用 findContours 时如何避免检测到图像帧【英文标题】:How to avoid detecting image frame when using findContours 【发布时间】:2015-03-29 13:46:24 【问题描述】:在使用findContours
(OpenCV) 时如何避免检测到图像的边框?直到我找到OpenCV findContours allways finds two contours for every object 并实现了那个答案,我并没有始终如一地检测到内部对象(对象线被分成几部分),但现在我每次都检测到图像帧。
图像是从底部看到的四旋翼无人机;我正在使用一系列图片来“训练”对象检测。为此,我需要确保我能够始终如一地获得 UAV 对象。我想我可以反转颜色,但这似乎是一种肮脏的技巧。
图像首先是findContours
之前的输入图像,以及生成的轮廓。我有七张测试图像,所有七张都有一个框架和无人机。 hu 时刻非常相似(正如预期的那样)。
用于查找轮廓/对象并计算 hu 矩的代码(C++11,相当混乱):
#include <opencv/cv.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <fstream>
#include <string>
using namespace cv;
using namespace std;
#define EROSION_SIZE 1
#define ERODE_CANNY_PREP_ITERATIONS 5
int main()
Mat image, canny_output, element, padded;
RNG rng(12345);
int numbers[] = 195, 223, 260, 295, 331, 368, 396;
string pre = "/home/alrekr/Pictures/UAS/hu-images/frame_";
string middle = "_threshold";
string post = ".png";
string filename = "";
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
ofstream fout("/home/alrekr/Pictures/UAS/hu-data/hu.dat");
element = getStructuringElement(MORPH_RECT,
Size(2*EROSION_SIZE + 1, 2*EROSION_SIZE+1),
Point(EROSION_SIZE, EROSION_SIZE));
namedWindow("Window", CV_WINDOW_AUTOSIZE);
for (int i : numbers)
filename = pre + to_string(i) + middle + post;
image = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);
erode(image, image, element, Point(-1,-1), ERODE_CANNY_PREP_ITERATIONS);
imwrite("/home/alrekr/Pictures/UAS/hu-data/prep_for_canny_" + to_string(i) + ".png", image);
findContours(image, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
vector<Moments> mu(contours.size());
if(contours.size() < 1)
cout << "No contours found" << endl;
else
cout << "Contours found: " << contours.size() << endl;
vector<Point2f> mc(contours.size());
for(int j = 0; j < (int)contours.size(); j++)
mc[j] = Point2f(mu[j].m10/mu[j].m00 , mu[j].m01/mu[j].m00);
Mat drawing = Mat::zeros(image.size(), CV_8UC3);
for(int j = 0; j < (int)contours.size(); j++)
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255));
drawContours(drawing, contours, j, color, 2, 8, hierarchy, 0, Point());
imshow("Window", drawing);
waitKey(0);
imwrite("/home/alrekr/Pictures/UAS/hu-data/cannied_" + to_string(i) + ".png", drawing);
fout << "Frame " << i << "\n";
for(int j = 0; j < (int)contours.size(); j++)
mu[j] = moments(contours[j]);
double hu[7];
HuMoments(mu[j], hu);
fout << "Object " << to_string(j) << "\n";
fout << hu[0] << "\n";
fout << hu[1] << "\n";
fout << hu[2] << "\n";
fout << hu[3] << "\n";
fout << hu[4] << "\n";
fout << hu[5] << "\n";
fout << hu[6] << "\n";
fout.close();
return 0;
【问题讨论】:
【参考方案1】:函数cv::findContours
描述由1 组成的区域的轮廓。不过,您感兴趣的区域是黑色的。
所以解决方案很简单。在检测轮廓之前反转输入图像:
image = 255 - image;
以下是我从您上面的示例中得出的代码示例:
#include <opencv2/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <string>
#define EROSION_SIZE 1
#define ERODE_CANNY_PREP_ITERATIONS 5
int main( int argc, char ** argv )
// Display the version of the linked OpenCV library.
std::cout << "Using OpenCV " << CV_VERSION_MAJOR << "." << CV_VERSION_MINOR << ".";
std::cout << CV_VERSION_REVISION << CV_VERSION_STATUS << std::endl;
// Load the input file.
std::string filename = std::string( argv[ 1 ] );
cv::Mat image = imread( filename, cv::IMREAD_GRAYSCALE );
// Invert the image so the area of the UAV is filled with 1's. This is necessary since
// cv::findContours describes the boundary of areas consisting of 1's.
image = 255 - image;
// Detect contours.
std::vector< std::vector< cv::Point> > contours;
std::vector< cv::Vec4i > hierarchy;
cv::findContours( image, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE );
std::cout << "Contours found: " << contours.size() << std::endl;
// Display and save the results.
cv::RNG rng( 12345 );
cv::Mat contourImage = cv::Mat::zeros( image.size(), CV_8UC3);
for( size_t j = 0; j < contours.size(); j++ )
cv::Scalar color( rng.uniform( 0, 255 ), rng.uniform( 0,255 ), rng.uniform( 0, 255 ) );
cv::drawContours( contourImage, contours, j, color, 2, 8, hierarchy, 0, cv::Point() );
// cv::imwrite( "contours.png", contourImage );
cv::imshow( "contours", contourImage );
cv::waitKey( 0 );
return 0;
控制台输出如下:
$ ./a.out gvlGK.png
Using OpenCV 3.0.0-beta
Contours found: 1
得到的轮廓图像是这样的:
【讨论】:
所以我提到的“肮脏的黑客”是要走的路。我在findContours
之前插入了image=255-image
,它按规定工作。【参考方案2】:
另一种解决方案是:-
找到轮廓的边界矩形
x,y,w,h = cv2.boundingRect(c)
例如比较图像的大小和边界矩形的大小
cnt_size=w*h
if(abs(cnt_size-img_size<=ERROR_THRESHOLD):
##discard this contour
【讨论】:
【参考方案3】:如果你有白色背景,首先使用 THRESH_BINARY_INV 类型反转它,然后使用轮廓。
image = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);
threshold(image,image,100,255,THRESH_BINARY_INV);
findContours( image, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE );
这只会返回您需要的轮廓。
【讨论】:
以上是关于使用 findContours 时如何避免检测到图像帧的主要内容,如果未能解决你的问题,请参考以下文章
二进制阈值图像-> 应用精明的边缘检测-> findContour(),这会改善轮廓检测吗?
C++ OpenCV:检测两行而不是一行(Canny & findContours)