Ask Your Question

Revision history [back]

Template matching with Orb

[I also posted this on StackOverflow]

Hi,

I'm trying to detect whether a template image (logo) is present in a pdf document. The document can be either a scan encapsulated in a pdf or a "pure" pdf document, but this is completely random.

First, I convert the pdf document to a png image using ImageMagick's convert tool, then I cut the output images in half because they're so big, and after that I try to match a logo from a database with any of the shapes present in the half-cut image.

To do so, I use an Orb Feature Detector with an Orb Descriptor, and a RobustMatcher (sort of improved BruteForce matcher, source code available here). Here is a snippet of code from my adaptation of it :

// Read input images
Mat image1 = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE);
Mat image2 = imread(argv[2], CV_LOAD_IMAGE_GRAYSCALE);

if (!image1.data || !image2.data) {
    std::cout << " --(!) Error reading images " << std::endl;
    exit(1);
}

// Setting up values for ORB Detector
int nfeatures = 800;
//float scaleFactor = 1.10;
int nlevels = 8;
int edgeThreshold = 12;
int firstLevel = 0;
int WTA_K = 2;
int scoreType = 0;
int patchSize = 31;

// Prepare the matcher
RobustMatcher rmatcher;
rmatcher.setConfidenceLevel(0.98);
rmatcher.setMinDistanceToEpipolar(1.0);
rmatcher.setRatio(0.80f);
cv::Ptr<cv::FeatureDetector> pfd = new cv::OrbFeatureDetector(nfeatures, scaleFactor, nlevels, edgeThreshold, firstLevel, WTA_K, scoreType, patchSize);
rmatcher.setFeatureDetector(pfd);
cv::Ptr<cv::DescriptorExtractor> pde = new cv::OrbDescriptorExtractor();
rmatcher.setDescriptorExtractor(pde);

// Match the two images
std::vector<cv::DMatch> matches;
std::vector<cv::KeyPoint> keypoints1, keypoints2;
cv::Mat fundemental = rmatcher.match(image1, image2, matches, keypoints1, keypoints2);

// If nothing could be matched, stop here
if(matches.size() < 4){
    exit(2);
}

The code works great on some examples that I chose carefully, with a highly-recognizable logo and a clean image, with certain proportions... etc. But when I try to apply the process to random pdf files, I start to get this error from OpenCV :

OpenCV Error: Assertion failed (type == src2.type() && src1.cols == src2.cols && (type == CV_32F || type == CV_8U)) in batchDistance, file /home/das/Downloads/opencv-2.4.5/modules/core/src/stat.cpp, line 1797 terminate called after throwing an instance of 'cv::Exception' what(): /home/das/Downloads/opencv-2.4.5/modules/core/src/stat.cpp:1797: error: (-215) type == src2.type() && src1.cols == src2.cols && (type == CV_32F || type == CV_8U) in function batchDistance

Aborted (core dumped)

I checked for this error, and it appeared that src1.cols != src2.cols, and a quick fix for it would be to test the condition before trying to match the images. The problem is that I miss a lot of images doing so, and this would be OK only if I were working on a video stream... but I'm not, and the next image has nothing in common with the previous one, and I can't determine whether my logo was present or not in the document.

Here is the code from stat.cpp, lines 1789 to 1826 : (assertion is at the beginning on line 1797)

void cv::batchDistance( InputArray _src1, InputArray _src2,
                    OutputArray _dist, int dtype, OutputArray _nidx,
                    int normType, int K, InputArray _mask,
                    int update, bool crosscheck )
{
Mat src1 = _src1.getMat(), src2 = _src2.getMat(), mask = _mask.getMat();
int type = src1.type();
CV_Assert( type == src2.type() && src1.cols == src2.cols &&
           (type == CV_32F || type == CV_8U));
CV_Assert( _nidx.needed() == (K > 0) );

if( dtype == -1 )
{
    dtype = normType == NORM_HAMMING || normType == NORM_HAMMING2 ? CV_32S : CV_32F;
}
CV_Assert( (type == CV_8U && dtype == CV_32S) || dtype == CV_32F);

K = std::min(K, src2.rows);

_dist.create(src1.rows, (K > 0 ? K : src2.rows), dtype);
Mat dist = _dist.getMat(), nidx;
if( _nidx.needed() )
{
    _nidx.create(dist.size(), CV_32S);
    nidx = _nidx.getMat();
}

if( update == 0 && K > 0 )
{
    dist = Scalar::all(dtype == CV_32S ? (double)INT_MAX : (double)FLT_MAX);
    nidx = Scalar::all(-1);
}

if( crosscheck )
{
    CV_Assert( K == 1 && update == 0 && mask.empty() );
    Mat tdist, tidx;
    batchDistance(src2, src1, tdist, dtype, tidx, normType, K, mask, 0, false);

So I'm wondering what does this assertion mean ? What are exactly the src1 and src2 files in stat.cpp ? Why do they need to have the same number of columns ?

I tried changing to a Surf detector and extractor, but I still get the error.

If anyone has an idea, do not hesitate to post, I welcome any advice or notice !

Thanks in advance.