Znajdowanie wzorca w obrazie (template Matching)

0

Hej, próbuję napisać kod w Javie, który rozpozna, czy obraz A zawiera się w obrazie B - template matching. Chcę tylko dostać true/false bez zaznaczania na większym obrazie znalezionego wzorca. Mam w tym 0 doświadczenia i wygooglowałem sobie bibliotekę JavaCV - wrapper dla Javy do OpenCV. Bibliotekę importuję poprzez Gradle:

compile group: 'org.bytedeco', name: 'javacv-platform', version: '1.3.3'

Jeden przykład, jaki znalazłem, jest tutaj: https://docs.opencv.org/3.2.0/d5/d6f/tutorial_feature_flann_matcher.html. Jeśli dobrze rozumiem, oblicza on deskryptory dla obu obrazów, a następnie wyszukuje się te, dla których dystans jest odpowiednio mały. Myślałem, że mały dystans deskryptora oznacza, że zostały znalezione cechy wspólne obu obrazów i obecność "dobrych" deskryptorów, oznacza, że jeden obraz jest podobrazem drugiego. Niestety, "dobre" deskryptory występują zarówno w przypadkach, gdy jeden obraz zawiera się w drugim, jak i w sytuacjach, gdy obrazy są zupełnie różne, a liczba "dobrych" deskryptorów to zazwyczaj kilkaset. Mój kod zaadaptowany (głównie copy-paste) na podstawie przykładu:

public void m1(String image, String template) {
        final opencv_core.Mat image1 = imread(image, IMREAD_GRAYSCALE);
        final opencv_core.Mat image2 = imread(template, IMREAD_GRAYSCALE);
        final opencv_core.KeyPointVector keyPoints1 = new opencv_core.KeyPointVector();
        final opencv_core.KeyPointVector keyPoints2 = new opencv_core.KeyPointVector();
        final opencv_xfeatures2d.SURF surf = opencv_xfeatures2d.SURF.create();
        surf.setHessianThreshold(400);

        opencv_core.Mat descriptors_1 = new opencv_core.Mat();
        opencv_core.Mat descriptors_2 = new opencv_core.Mat();

        surf.detectAndCompute(image1, new opencv_core.Mat(), keyPoints1, descriptors_1);
        surf.detectAndCompute(image2, new opencv_core.Mat(), keyPoints2, descriptors_2);

        opencv_features2d.FlannBasedMatcher matcher = opencv_features2d.FlannBasedMatcher.create();
        opencv_core.DMatchVector matches = new opencv_core.DMatchVector();

        matcher.match(descriptors_1, descriptors_2, matches);

        double max_dist = 0; double min_dist = 100;
        for( int i = 0; i < descriptors_1.rows(); i++ )
        {
            double dist = matches.get(i).distance();
            if( dist < min_dist ) min_dist = dist;
            if( dist > max_dist ) max_dist = dist;
        }

        opencv_core.DMatchVector good_matches = new opencv_core.DMatchVector();
        ArrayList<opencv_core.DMatch> tmp = new ArrayList<>();

        for( int i = 0; i < descriptors_1.rows(); i++ )
        {
            if( matches.get(i).distance() <= max(2*min_dist, 0.02) )
            {
                good_matches.put( matches.get(i));
                tmp.add(matches.get(i));
            }
        }
    }

Znalazłem jeszcze jeden przykład: https://github.com/bytedeco/javacv/blob/master/samples/TemplateMatching.java, ale po przerobieniu:

public void m2(String image, String template) {
        opencv_core.Mat image1 = imread(image, IMREAD_GRAYSCALE);
        opencv_core.Mat image2 = imread(template,CV_LOAD_IMAGE_GRAYSCALE);//int = 0
        opencv_core.Size size = new opencv_core.Size(image1.cols()-image2.cols()+1, image1.rows()-image2.rows()+1);
        opencv_core.Mat result = new opencv_core.Mat(size, CV_32FC1);
        matchTemplate(image1, image2, result, TM_CCORR_NORMED);

        DoublePointer minVal= new DoublePointer();
        DoublePointer maxVal= new DoublePointer();
        opencv_core.Point min = new opencv_core.Point();
        opencv_core.Point max = new opencv_core.Point();
        minMaxLoc(result, minVal, maxVal, min, max, null);
    }

nie wiem, co dalej. Nie widzę w result żadnych pół ani metod, które, by mi pomogły.

Wszelka pomoc mile widziana. :) Może ktoś ma doświadczenie z JavaCV?

0

Nie sądzę by metoda deskryptorowa się sprawdziła w tego typu problemie. Deskryptory służą raczej do znalezienia wspólnych elementów charakterystycznych. o lokalnie dużej zmienności, na różnych obrazach prezentujących potencjalnie to samo, np z różnego punktu. To co jest poza nimi nie ma znaczenia, a przecież o podobieństwie dwóch obrazów decyduje wszystko, również elementy bez lokalnej zmienności, takie jak tło.

Twoje zadanie wydaje mi się w sumie prostsze - masz porównać czy obraz zawiera inny. Stwórz funkcję podobieństwa między dwoma obrazami, np jako sumę różnic jasności pikseli dla poszczególnych kanałów. W tej funkcji dokonaj na wstępie jakiegoś preprocesingu na tych obrazach w stylu wyrównania histogramu i przepuszczenia przez filtr dolnoprzepustowy. Mając taką funkcję, selektywnie skanuj fragmenty obrazu wejściowego i znajdź minimum. Ponieważ problem dotyczy detekcji, musisz wyznaczyć jakąś małą wartość progową, bo 0 możesz nigdy nie otrzymać choćby ze względu na artefakty wynikające z kompresji. Jeśli zadanie ma uwzględniać wyszukiwanie w różnych skalach, to też musisz stworzyć jakąś funkcję skalującą obraz i przeprowadzić skanowanie dla różnych skal.

0

Udało mi się drugim sposobem, tylko zaciągnąłem inną zależność przez Gradle i miałem do dyspozycji normalne Javowe klasy zamiast tych dziwacznych native metod, które wszędzie zwracały nulle.

1 użytkowników online, w tym zalogowanych: 0, gości: 1