如何在小图像上使用Opencv特征检测器

7

我正在使用Java中的OpenCV 3,尝试在其他图像上查找小图片(例如25x25像素)。但是FeatureDetector检测到小图像上的(0,0)大小矩阵。

    Mat smallImage = ...

    FeatureDetector detector = FeatureDetector.create(FeatureDetector.ORB);
    DescriptorExtractor descriptor = DescriptorExtractor.create(DescriptorExtractor.ORB);
    DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);

    Mat descriptorsSmall = new Mat();
    MatOfKeyPoint keyPointsSmall = new MatOfKeyPoint();

    detector.detect(smallImage, keyPointsSmall);
    descriptor.compute(smallImage, keyPointsSmall, descriptorsSmall);

在这里,我发现keyPointsSmall和descriptorsSmall的大小为零,并且检测不起作用。

但如果我尝试对150x150像素这样的较大图像进行操作,则可以正常工作。 有什么建议吗?谢谢。

这里我提供一些示例。 我们有这张源图像: This is source image

假设我们有P字母的模板,因此我们需要在源图像上检测出该P。 This is template

将图像缩放到更高的分辨率不适合我。那将是时间和资源的浪费。 理想情况下,它应该是旋转比例不变的。 但简单的解决方案没有旋转和比例也可以。

对我来说,除OpenCv之外的其他解决方案都是不可接受的。(例如使用Tesseract)


你能否提供一些你正在处理的输入数据? - ZdaR
我认为最好的示例将是文本识别,想象一下你有每个字母的图标,比如20x25像素,因此尝试从其他图像中识别这些字母。为了简单起见,假设字体系列和字体重量与样本相同。 - RustamIS
是的,但如果您可以直接提供您正在使用的数据集,那么我们就能够重现这个问题。 - ZdaR
好的,我已经添加了样本。模板P信是透明的。 - RustamIS
3个回答

1
看起来你正在尝试读取PersonalID属性。基本上,你需要准备图像,将其矢量化(使其具有尺度和旋转不变性)并进行比较/匹配。这可以在OpenCV中完成。
  1. 准备工作:通常会减少颜色和亮度。如果你的字母很明显,你可以使用阈值(亮度/颜色/每个颜色通道)并去除那些颜色。对于你来说,你可能只想将所有不是几乎黑色的东西减少到只成为白色。你可能需要尝试额外的锐化甚至边缘检测。
  2. 矢量化非常简单,并且可以进一步改进,因为你知道你只对某些符号感兴趣,所以你应该找到其他可以用于改进矢量化结果的质量(抑制噪声,更好地选择和纠正某些边缘/角度等)。
  3. 匹配应该相当直接。由于你知道目标字体和潜在符号,匹配应该产生许多带有非常小误差的正面结果。此外,大多数错误应该很容易识别,以便你可以将少数可能存在的错误发送给一个人进行验证。
潜在的改进:
使用分形方法进行缩放通常能很好地保留字母和数字的属性,并可以提高结果的质量。
检测ID的不同部分将有助于您识别检测的目标区域。这样可以进一步改善结果。通常人们只关注他们想要识别的内容,忽略了其他不必要的信息。但是这些信息可以让您了解在检测中可能出现的潜在错误。因此,如果您无法正确识别名称,则很可能也无法正确识别ID。因此,尝试获取ID的所有信息是确定照片质量是否足够确保您真正关心的信息的良好指标。
如果您确切知道目标区域的样子,可以将目标区域缩放到固定大小并使用每像素匹配。由于您确切知道您关心的字体,这种检测可以具有令人惊讶的高检测率。使用每像素匹配和矢量化将为您提供卓越的检测率。与矢量化相比,每像素匹配速度非常快。
由于您知道期望符号的位置和大小,因此可以基于属性(符号的实际大小、某些区域中黑色的分布等)创建决策树。这将把问题从35个之一降低到四个或更少。

1
你可以对图像进行重新采样,这比缩放要快得多,并且在独立的过程中非常快,它只是将每个像素映射到一组像素,直到分辨率符合要求,在opencv中,您可以使用resize函数和INTER_AREA标志来实现: http://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html 另一个解决方案是将图像复制到更大的空白图像上,并在更大的图像上运行检测。

1
Keypoint检测对于文本识别来说不是最佳解决方案,因为你会得到许多外观相似的特征,如果模板非常小,则滑动窗口将无法产生足够的检测特征。
幸运的是,OpenCV 3在contrib存储库中包含了一个文本检测/识别模块:链接,其中包括从这里获取的示例和其他许多可在这里找到的内容。
/*
 * cropped_word_recognition.cpp
 *
 * A demo program of text recognition in a given cropped word.
 * Shows the use of the OCRBeamSearchDecoder class API using the provided default classifier.
 *
 * Created on: Jul 9, 2015
 *     Author: Lluis Gomez i Bigorda <lgomez AT cvc.uab.es>
 */

#include "opencv2/text.hpp"
#include "opencv2/core/utility.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"

#include <iostream>

using namespace std;
using namespace cv;
using namespace cv::text;

int main(int argc, char* argv[])
{

    cout << endl << argv[0] << endl << endl;
    cout << "A demo program of Scene Text Character Recognition: " << endl;
    cout << "Shows the use of the OCRBeamSearchDecoder::ClassifierCallback class using the Single Layer CNN character classifier described in:" << endl;
    cout << "Coates, Adam, et al. \"Text detection and character recognition in scene images with unsupervised feature learning.\" ICDAR 2011." << endl << endl;

    Mat image;
    if(argc>1)
        image  = imread(argv[1]);
    else
    {
        cout << "    Usage: " << argv[0] << " <input_image>" << endl;
        cout << "           the input image must contain a single character (e.g. scenetext_char01.jpg)." << endl << endl;
        return(0);
    }

    string vocabulary = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // must have the same order as the clasifier output classes

    Ptr<OCRHMMDecoder::ClassifierCallback> ocr = loadOCRHMMClassifierCNN("OCRBeamSearch_CNN_model_data.xml.gz");

    double t_r = (double)getTickCount();
    vector<int> out_classes;
    vector<double> out_confidences;

    ocr->eval(image, out_classes, out_confidences);

    cout << "OCR output = \"" << vocabulary[out_classes[0]] << "\" with confidence "
         << out_confidences[0] << ". Evaluated in "
         << ((double)getTickCount() - t_r)*1000/getTickFrequency() << " ms." << endl << endl;

    return 0;
}

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