web-dev-qa-db-fra.com

Comparaison d'images OpenCV sous Android

[MODIFIER] J'ai conçu du code pour la comparaison d'images. La partie correspondante est encore un peu imparfaite et j'aimerais de l'aide. Le projet peut être trouvé à - GitHub .

J'ai ces deux images Img1 et Img2:

enter image description hereenter image description here

Quand j'utilise la commande suivante dans openCV

Mat img1 = Highgui.imread("mnt/sdcard/IMG-20121228.jpg");
Mat img2 = Highgui.imread("mnt/sdcard/IMG-20121228-1.jpg");

try{
    double l2_norm = Core.norm( img1, img2 );
    tv.setText(l2_norm+"");
} catch(Exception e) {
    //image is not a duplicate
}

J'obtiens une double valeur pour l2_norm. Cette double valeur varie pour les paires d'images en double. Mais si les images sont différentes, une exception est levée. Est-ce ainsi que j'identifie les images en double? Ou y a-t-il une meilleure méthode? J'ai abondamment cherché sur Google et je n'ai pas trouvé de réponse vraiment convaincante. J'aimerais connaître le code et expliquer comment comparer deux images et obtenir une valeur booléenne de true ou false en fonction des images.

MODIFIER

Scalar blah= Core.sumElems(img2);
    Scalar blah1=Core.sumElems(img1);

    if(blah.equals(blah1))
    {
        tv.setText("same image");
    }
    }

J'ai essayé cela, mais la condition if n'est jamais satisfaite. Je suppose qu'il y a quelques différences, mais il n'y a pas de fonction compare pour Scalar. Que fais-je?

MODIFIER

try{
    Scalar blah= Core.sumElems(img2);
    Scalar blah1=Core.sumElems(img1);
    String b=blah.toString();
    String b1=blah1.toString();
    System.out.println(b+" "+b1);
    double comp=b.compareTo(b1);
    tv.setText(""+comp);
    }

Cette méthode est encore imparfaite. Bien qu'il puisse être utilisé pour comparer des images avec une précision décente, il échoue lorsque les images sont de tailles différentes.

Lorsque les images sont de tailles différentes et que j'imprime les valeurs scalaires, je reçois ceci:

[9768383.0, 1.0052889E7, 1.0381814E7, 0.0] [1.5897384E7, 1.6322252E7, 1.690251E7, 0.0]

La différence entre les deuxième et troisième nombres, bien que peu, est assez grande comparée à celle obtenue lorsque les images de même taille sont comparées. Le premier nombre subit toutefois le plus de changement.

Quel serait le meilleur moyen le plus rapide de comparer le contenu de deux images?

[MODIFIER]

J'utilise le code que j'ai trouvé ici .

Ce que je ne peux pas comprendre, c'est comment initialiser les variables MatOfKeyPointkeypoints et logoKeypoints. Voici mon extrait de code:

           FeatureDetector detector = FeatureDetector.create(FeatureDetector.SURF);
        //FeatureDetector detector = FeatureDetector.create(FeatureDetector.FAST);
        //Imgproc.cvtColor(img1, img1, Imgproc.COLOR_RGBA2RGB);
        //Imgproc.cvtColor(img2, img2, Imgproc.COLOR_RGBA2RGB);

        DescriptorExtractor SurfExtractor = DescriptorExtractor
        .create(DescriptorExtractor.SURF);


        //extract keypoints
        MatOfKeyPoint keypoints, logoKeypoints;
        long time= System.currentTimeMillis();
        detector.detect(img1, keypoints);
        Log.d("LOG!", "number of query Keypoints= " + keypoints.size());
        detector.detect(img2, logoKeypoints);
        Log.d("LOG!", "number of logo Keypoints= " + logoKeypoints.size());
        Log.d("LOG!", "keypoint calculation time elapsed" + (System.currentTimeMillis() -time));

        //Descript keypoints
        long time2 = System.currentTimeMillis();
        Mat descriptors = new Mat();
        Mat logoDescriptors = new Mat();
        Log.d("LOG!", "logo type" + img2.type() + "  intype" + img1.type());
        SurfExtractor.compute(img1, keypoints, descriptors);
        SurfExtractor.compute(img2, logoKeypoints, logoDescriptors);
        Log.d("LOG!", "Description time elapsed" + (System.currentTimeMillis()- time2));

Je ne peux évidemment pas initialiser les variables keypoints et logoKeypoints à null parce que je recevrai alors une exception de pointeur null. Comment puis-je les initialiser?

19
Karthik Balakrishnan

Vous devez comprendre que ce n’est pas une question simple et que vous pouvez suivre différents concepts. Je ne signalerai que deux solutions sans code source.

  1. Comparaison d'histogramme : Vous pouvez convertir les deux images en niveaux de gris pour créer un histogramme dans la plage [0, ..., 255]. Chaque valeur de pixel sera comptée. Ensuite, utilisez les deux histogrammes pour la comparaison. Si la distribution des intensités de pixels est égale ou supérieure à un seuil (peut-être 90% de tous les pixels), vous pouvez considérer ces images comme des doublons. MAIS: C’est l’une des solutions les plus simples et n’est pas stable si une image a une distribution égale.
  2. Détecteurs de points d'intérêt/- Descripteurs : jetez un coup d'œil aux détecteurs d'images et descripteurs SIFT/SURF. Un détecteur tentera de déterminer des pavés uniques d'intensités dans une image. Un descripteur sera calculé à cet endroit I (x, y). Un matcher normal avec une approche force brute et une distance euclidienne peut correspondre à ces images en utilisant leurs descripteurs. Si une image est une copie, le nombre de correspondances doit être très élevé. Cette solution est bonne à mettre en œuvre et il pourrait y avoir suffisamment de tutoriels sur ce sujet.

J'espère que cela aide. S'il vous plaît demander si vous avez des questions.

[UPDATE-1] Un tutoriel en C++: http://morf.lv/modules.php?name=tutorials&lasit=2#.UR-ewKU3vCk

Quelques tutoriels JavaCV: http://code.google.com/p/javacv/w/list

[UPDATE-2] Voici un exemple avec SIFT-Detector et SIFT-Descriptor utilisant les paramètres par défaut. RANSAC-Seuil pour l'homographie est 65, l'erreur de reprojection (epsilon) est 10, la validation croisée est activée. Vous pouvez essayer de compter les appariés. Si le ratio Inliner-Outlier-Ratio est trop élevé, vous pourriez voir cette paire comme des doublons. Matching img1 and img2 using SIFT-detector and SIFT-descriptor Par exemple: Ces images génèrent 180 points clés dans IMG1 et 198 dans IMG2. Les descripteurs appariés sont 163, dont 3 seulement sont des valeurs aberrantes. Cela donne donc un très bon rapport qui pourrait signifier que ces images pourraient être dupliquées.

[UPDATE-3] Je ne comprends pas pourquoi vous pouvez initialiser les MatOfKeypoints. J'ai lu l'API et il y a un constructeur public. ET: Vous pouvez utiliser le tapis de l'image que vous souhaitez analyser. C'est très gentil. =) 

MatOfKeyPoint reference = new MatOfKeyPoint(matOfReferenceImage);

Pour la correspondance, utilisez un (descripteur BRUTEFORCE_SL2 _ car vous aurez besoin de la distance euclidienne pour SURF ou SIFT.

25
Mr.Mountain

Utilisez cv2.absDiff pour calculer la différence entre les images et cv2.sumElems pour obtenir la somme de toutes les différences en pixels.

Puis inventez un seuil permettant de juger si deux images sont similaires ou non.

1
sschrass

Vous pouvez essayer le code suivant:

Mat img1 = Highgui.imread("mnt/sdcard/IMG-20121228.jpg");
Mat img2 = Highgui.imread("mnt/sdcard/IMG-20121228-1.jpg");
Mat result = new Mat();

Core.compare(img1,img2,result,Core.CMP_NE);

int val = Core.countNonZero(result);

if(val == 0) {
    //Duplicate Image
} else {
    //Different Image
}

Ici, dans la fonction de comparaison de code, deux images sont comparées, puis s'il y a une similitude entre les images, la valeur de la matrice est alors 255 et toutes les autres valeurs sont égales à zéro. Ensuite, vous pouvez compter le nombre de valeurs non nulles pour déterminer si les images étaient égales. Cela ne fonctionnerait que pour exactement les mêmes images.

Si vous souhaitez comparer des images en ignorant les effets de lumière, je vous suggère de générer d'abord l'image Edge (en utilisant la fonction intuitive d'OpenCV), puis de comparer les images.

J'espère que cette réponse vous aide !!

0
Sanober Malik