2015-12-14 6 views
4

Я пытаюсь создать приложение для простого распознавания листьев с Android и OpenCV; моя база данных состоит всего из 3 записей (3 изображения из 3 типов листьев), и я хотел бы узнать, есть ли одна из картин в базе данных внутри другого изображения, захваченного смартфоном. Я использую метод SURF для извлечения ключевых точек из изображений базы данных, а затем сравнивает их с извлеченными ключевыми точками захваченного изображения, ища совпадения. Моя проблема заключается в том, что результат отображается как «согласование цвета», больше, чем «соответствие функций»: когда я сравниваю изображение с базой данных и захваченным, количество совпадений равно всем 3 элементам, и, таким образом, я получаю неправильное совпадение.Распознавание изображений с использованием SURF с OpenCV в Android

Это один из картины из базы данных (обратите внимание, что без Backgroud)

picture from database

И это результат, который я получаю:

Screenshot with matches

Изображение на вершине тот, который снят с смартфона, и изображение ниже - результат с выделенными совпадениями.

Вот код, который я реализовал:

Mat orig = Highgui.imread(photoPathwithoutFile); 
Mat origBW = new Mat(); 
Imgproc.cvtColor(orig, origBW, Imgproc.COLOR_RGB2GRAY); 
MatOfKeyPoint kpOrigin = createSURFdetector(origBW); 
Mat descOrig = extractDescription(kpOrigin, origBW); 
Leaf result = findMatches(descOrig); 
Mat imageOut = orig.clone(); 
Features2d.drawMatches(orig, kpOrigin, maple, keypointsMaple, resultMaple, imageOut); 


public MatOfKeyPoint createSURFdetector (Mat origBW) { 
    FeatureDetector surf = FeatureDetector.create(FeatureDetector.FAST); 

    MatOfKeyPoint keypointsOrig = new MatOfKeyPoint(); 

    surf.detect(origBW, keypointsOrig); 

    return keypointsOrig; 
} 

public Mat extractDescription (MatOfKeyPoint kpOrig, Mat origBW) { 
    DescriptorExtractor surfExtractor = DescriptorExtractor.create(FeatureDetector.SURF); 

    Mat origDesc = new Mat(); 

    surfExtractor.compute(origBW, kpOrig, origDesc); 

    return origDesc; 
} 

public Leaf findMatches (Mat descriptors) { 
    DescriptorMatcher m = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE); 
    MatOfDMatch max = new MatOfDMatch(); 
    resultMaple = new MatOfDMatch(); 
    resultChestnut = new MatOfDMatch(); 
    resultSwedish = new MatOfDMatch(); 
    Leaf match = null; 

    m.match(descriptors, mapleDescriptors, resultMaple); 
    Log.d("Origin", resultMaple.toList().size()+" matches with Maples"); 
    if (resultMaple.toList().size() > max.toList().size()) { max = resultMaple; match = Leaf.MAPLE; } 
    m.match(descriptors, chestnutDescriptors, resultChestnut); 
    Log.d("Origin", resultChestnut.toList().size()+" matches with Chestnut"); 
    if (resultChestnut.toList().size() > max.toList().size()) { max = resultChestnut; match = Leaf.CHESTNUT; } 
    m.match(descriptors, swedishDescriptors, resultSwedish); 
    Log.d("Origin", resultSwedish.toList().size()+" matches with Swedish"); 
    if (resultSwedish.toList().size() > max.toList().size()) { max = resultSwedish; match = Leaf.SWEDISH; } 

    //return the match object with more matches 
    return match; 
} 

Как я могу получить более точное соответствие не основанные на цветах, а на реальных особенностях картины?

ответ

1

Ну, SURF не лучший кандидат на эту задачу. SURF-дескриптор в основном кодирует некоторую статистику градиента в малой окрестности угла. Это дает вам неизменность для многих преобразований, но при этом вы теряете «общую картину». Этот дескриптор используется для сужения диапазона соответствий между точками, которые должны быть сопоставлены, и затем вступают в силу некоторые геометрические ограничения.

В вашем случае кажется, что дескрипторы не выполняют отличную работу в совпадающих точках, и поскольку из них есть много, каждая точка в конечном итоге получает соответствие (хотя странно, что геометрическое тестирование не мешало этому).

Я могу посоветовать вам попробовать другой подход к сопоставлению, возможно, HOG с дескрипторами, обученными распознавать типы листьев или даже что-то контурное, поскольку форма - это то, что действительно отличается от ваших изображений. Вы можете, например, обнаружить контур листьев, нормализовать его длину, найти его центр, а затем равными интервалами рассчитать расстояние от каждой точки до центра - это будет ваш дескриптор. Затем найдите наибольшую длину и круговое смещение этого дескриптора, чтобы начать с экстремумов и делить на это значение - это даст вам некоторую базовую инвариантность выбора исходной точки, вращения и масштаба контура. Но это, скорее всего, потерпит неудачу при перспективных и аффинных преобразованиях.

Если вы хотите поэкспериментировать с особенностями - попробуйте обнаружить меньше из них, но более репрезентативные (фильтр по силе градиента, угловой балл или что-то еще). Возможно, использовать SIFT вместо SURF - это должно быть немного точнее. Проверьте количество линеек после сопоставления - наилучшее совпадение должно иметь более высокий коэффициент.

Но, честно говоря, это скорее похоже на задачу машинного обучения, чем компьютерное зрение.

Редактировать: Я проверил ваш код и выяснил, что вы не выполняете геометрические проверки совпадений, поэтому почему вы получаете неправильное совпадение.Попробуйте выполнить findHomography после сопоставления, а затем рассмотрите только те точки, которые были установлены на один в аргументе вывода mask. Это заставит вас рассматривать только те точки, которые могут быть деформированы друг с другом с помощью гомографии и могут значительно улучшить соответствие.

Edit2: добавлен фрагмент кода (извините, но я не могу проверить Java на данный момент, так что в Python)

import cv2 
import numpy as np 

# read input 
a = cv2.imread(r'C:\Temp\leaf1.jpg') 
b = cv2.imread(r'C:\Temp\leaf2.jpg') 

# convert to gray 
agray = cv2.cvtColor(a, cv2.COLOR_BGR2GRAY) 
bgray = cv2.cvtColor(b, cv2.COLOR_BGR2GRAY) 

# detect features and compute descriptors 
surf = cv2.SURF() # better use SIFT instead 
kp1, d1 = surf.detectAndCompute(agray,None) 
kp2, d2 = surf.detectAndCompute(bgray,None) 
print 'numFeatures1 =', len(kp1) 
print 'numFeatures2 =', len(kp2) 

# use KNN matcher 
bf = cv2.BFMatcher() 
matches = bf.knnMatch(d1,d2, k=2) 

# Apply Lowe ratio test 
good = [] 
for m,n in matches: 
    if m.distance < 0.75*n.distance: 
     good.append(m) 

print 'numMatches =', len(matches) 
print 'numGoodMatches =', len(good) 

# if have enough matches - try to calculare homography to discard matches 
# that don't fit perspective transformation model 
if len(good)>10: 
    # convert matches into correct format (python-specific) 
    src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2) 
    dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2) 

    M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0) 
    print 'numMatches =', sum(mask.ravel().tolist()) # calc number of 1s in mask 

else: 
    print "not enough good matches are found" 

Это дает мне следующий результат для различных листьев, используя SURF

numFeatures1 = 685 
numFeatures2 = 1566 
numMatches = 685 
numGoodMatches = 52 
numMatches = 11 

Вы можете видеть, что количество «реальных» совпадений очень мало. Но, к сожалению, numMatches аналогичны, когда мы сопоставляем разные изображения одного и того же типа листа. Возможно, вы можете улучшить результат, изменив параметры, но я думаю, что использование ключевых точек здесь - это не очень хороший подход. Возможно, это связано с изменением листа даже внутри одного класса.

+0

Интересно! Не могли бы вы приложить фрагмент кода для этого? Потому что я не понял, что вы имеете в виду: я пытался выполнить 'findHomography' после сопоставления, но я думаю, что я делаю что-то неправильно. Спасибо –

+2

Отредактированный ответ, чтобы включить фрагмент кода. Я использовал ваши листовые изображения в качестве входных данных, размером до 512 пикселей. – alexisrozhkov