Comparación de imágenes OpenCV en Android
feature-detection surf (3)
Debe comprender que esta no es una pregunta simple y que tiene conceptos diferentes que podría seguir. Solo señalaré dos soluciones sin código fuente.
- Comparación de histogramas : puede convertir ambas imágenes a escala de grises para hacer un histograma en el rango de [0, ..., 255]. Se contará cada valor de píxel. Luego usa ambos histogramas para la comparación. Si la distribución de las intensidades de píxeles es igual o está por encima de algún umbral (quizás el 90% de todos los píxeles), podría considerar estas imágenes como duplicados. PERO: esta es una de las soluciones más simples y no es estable si alguna imagen tiene una distribución igual.
- Punto de interés-Detectores / -Descriptores : Eche un vistazo a los detectores y descriptores de imágenes SIFT / SURF. Un detector intentará determinar puntos clave únicos de intensidades en una imagen. Se calculará un descriptor en esta ubicación I (x, y). Un emparejador normal con un enfoque de fuerza bruta y una distancia euclidiana puede emparejar estas imágenes usando sus descriptores. Si una imagen es un duplicado, la tasa de coincidencias dadas debería ser muy alta. Esta solución es buena de implementar y podría haber suficientes tutoriales sobre este tema.
Espero que esto ayude. Por favor pregunte si tiene preguntas.
[ACTUALIZACIÓN-1] A C ++ - tutorial: http://morf.lv/modules.php?name=tutorials&lasit=2#.UR-ewKU3vCk
Algunos tutoriales de JavaCV: http://code.google.com/p/javacv/w/list
[ACTUALIZACIÓN-2] Aquí hay un ejemplo con SIFT-Detector y SIFT-Descriptor utilizando parámetros predeterminados. RANSAC-El umbral para homografía es 65, error de reproyección (épsilon) es 10, habilitación de validación cruzada. Podrías intentar contar el emparejado. Si la relación de valor interior de Inliner es demasiado alta, podría ver este par como duplicados. Por ejemplo: estas imágenes producen 180 puntos clave en IMG1 y 198 en IMG2. Los descriptores coincidentes son 163 de los cuales solo 3 son valores atípicos. Así que esto da una proporción realmente buena que solo podría significar que estas imágenes podrían ser duplicadas.
[ACTUALIZACIÓN-3] No entiendo por qué puede inicializar los puntos de MatOfKey. He leído la API y hay un constructor público. Y: puedes usar el tapete de la imagen que quieres analizar. Esto esta muy bien. =)
MatOfKeyPoint reference = new MatOfKeyPoint(matOfReferenceImage);
Para el uso de Matching, utilice un Descriptor-Matcher BRUTEFORCE_SL2, ya que necesitará la distancia euclidiana para SURF o SIFT.
[EDITAR] He ideado un código para la comparación de imágenes. La parte correspondiente aún es un poco defectuosa y me encantaría un poco de asistencia. El proyecto se puede encontrar en - GitHub .
Tengo estas dos imágenes Img1 e Img2 :
Cuando uso el siguiente comando en 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
}
Obtengo un valor doble para l2_norm. Este doble valor varía para pares de imágenes duplicadas. Pero si las imágenes son diferentes, entonces se lanza una excepción. ¿Es así como identifico imágenes duplicadas? ¿O hay un método mejor? He buscado en Google extensivamente y no pude encontrar una respuesta realmente convincente. Me gustaría el código y la explicación de cómo compararía dos imágenes y obtendría un valor booleano de true
o false
según las imágenes.
EDITAR
Scalar blah= Core.sumElems(img2);
Scalar blah1=Core.sumElems(img1);
if(blah.equals(blah1))
{
tv.setText("same image");
}
}
He intentado esto, pero la condición if
nunca se cumple. Supongo que hay algunas diferencias, pero no hay una función de compare
para Scalar
. ¿Qué debo hacer?
EDITAR
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);
}
Este método es de nuevo defectuoso. Aunque se puede usar para comparar imágenes con una precisión decente, falla cuando las imágenes son de diferentes tamaños.
Cuando las imágenes son de diferentes tamaños e imprimo los valores escalares, obtengo esto:
[9768383.0, 1.0052889E7, 1.0381814E7, 0.0] [1.5897384E7, 1.6322252E7, 1.690251E7, 0.0]
La variación entre los números segundo y tercero, aunque no mucho, es bastante grande en comparación con cuando se comparan las imágenes del mismo tamaño. El primer número sin embargo sufre la mayoría del cambio.
¿Cuál sería la mejor manera más rápida de comparar los contenidos de dos imágenes?
[EDITAR]
Estoy usando el código que encontré here .
Lo que no puedo averiguar es cómo inicializar los keypoints
variables logoKeypoints
y los keypoints
logoKeypoints
. Aquí está mi fragmento de código:
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));
Obviamente, no puedo inicializar las variables keypoints
y logoKeypoints
a null porque recibo una excepción de puntero nulo. ¿Cómo los inicializo?
Puedes probar el siguiente código:
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
}
Aquí, en la función de comparación de códigos, compararemos dos imágenes y luego, si hay una similitud entre las imágenes, entonces el valor de la matriz particular será 255 y todos los demás valores serán cero. Luego puede contar el número de valores distintos de cero para determinar si las imágenes eran iguales. Esto funcionaría solo para exactamente las mismas imágenes.
Si desea comparar imágenes ignorando los efectos de luz, le sugiero que genere primero la imagen de borde (usando la función de Canny de OpenCV) y luego compare las imágenes.
Espero que esta respuesta te ayude !!
Use cv2.absDiff
para calcular la diferencia entre las imágenes y cv2.sumElems
para obtener la suma de todas las diferencias de píxeles.
Luego inventa un umbral por el que juzgues si dos imágenes son similares o no.