Сравнение изображений OpenCV в Android
[РЕДАКТИРОВАТЬ] Я разработал некоторый код для сравнения изображений. Соответствующая часть все еще немного испорчена, и я хотел бы немного помочь. Проект можно найти по адресу - GitHub.
У меня есть эти два изображения Img1 и Img2:
Когда я использую следующую команду в 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
}
Я получаю двойное значение для l2_norm. Это двойное значение варьируется для дублированных пар изображений. Но если изображения отличаются, то выдается исключение. Это как определить дубликаты изображений? Или есть лучший метод? Я много гуглил и не смог найти действительно убедительного ответа. Я хотел бы, чтобы код и объяснение того, как я сравнил бы два изображения и получил логическое значение true
или же false
в зависимости от изображений.
РЕДАКТИРОВАТЬ
Scalar blah= Core.sumElems(img2);
Scalar blah1=Core.sumElems(img1);
if(blah.equals(blah1))
{
tv.setText("same image");
}
}
Я пробовал это, но if
состояние никогда не выполняется. Я предполагаю, что есть несколько различий, но нет compare
функция для Scalar
, Что я делаю?
РЕДАКТИРОВАТЬ
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);
}
Этот метод снова ошибочен. Хотя его можно использовать для сравнения изображений с достаточной точностью, он не работает, когда изображения имеют разные размеры.
Когда изображения имеют разные размеры, и я печатаю скалярные значения, я получаю это:
[9768383.0, 1.0052889E7, 1.0381814E7, 0.0] [1.5897384E7, 1.6322252E7, 1.690251E7, 0.0]
Разница между вторым и третьим числами, хотя и незначительная, достаточно велика по сравнению с изображениями одинакового размера. Первый номер, однако, страдает больше всего изменений.
Какой самый быстрый способ сравнить содержимое двух изображений?
[РЕДАКТИРОВАТЬ]
Я использую код, который я нашел здесь.
Что я не могу понять, это как инициализировать MatOfKeyPoint
переменные keypoints
а также logoKeypoints
, Вот мой фрагмент кода:
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));
Я, очевидно, не могу инициализировать переменные keypoints
а также logoKeypoints
к нулю, потому что я получу исключение нулевого указателя тогда. Как мне их инициализировать?
3 ответа
Вы должны понимать, что это не простой вопрос, и у вас есть разные концепции, которым вы могли бы следовать. Я только укажу два решения без исходного кода.
- Сравнение гистограммы: вы можете преобразовать оба изображения в оттенки серого, составив гистограмму в диапазоне [0,...,255]. Каждое значение будет подсчитано. Затем используйте обе гистограммы для сравнения. Если распределение интенсивности пикселей равно или превышает некоторый порог (возможно, 90% всех пикселей), вы можете рассматривать эти изображения как дубликаты. НО: это одно из самых простых решений, и оно не стабильно, если какое-либо изображение имеет одинаковое распределение.
- Детекторы точек интереса /-дескрипторы: взгляните на детекторы и дескрипторы изображений SIFT/SURF. Детектор попытается определить уникальные ключевые точки интенсивности на изображении. Дескриптор будет вычислен в этом месте I(x,y). Обычный сопоставитель с грубым приближением и евклидовым расстоянием может сопоставлять эти изображения, используя их дескрипторы. Если изображение является дубликатом, частота совпадений должна быть очень высокой. Это решение хорошо для реализации, и может быть достаточно учебников по этой теме.
Я надеюсь, это поможет. Пожалуйста, спросите, если у вас есть вопросы.
[ОБНОВЛЕНИЕ-1] C++- учебное пособие: http://morf.lv/modules.php?name=tutorials&lasit=2
Некоторые руководства по JavaCV: http://code.google.com/p/javacv/w/list
[ОБНОВЛЕНИЕ-2] Вот пример с SIFT-Detector и SIFT-Descriptor, использующими параметры по умолчанию. RANSAC-порог для гомографии - 65, ошибка репроекции (эпсилон) - 10, перекрестная проверка включена. Вы можете попытаться посчитать совпадающие. Если коэффициент Inliner-Outlier-Ratio слишком высок, вы можете увидеть эту пару как дубликаты. Например: эти изображения создают 180 ключевых точек в IMG1 и 198 в IMG2. Соответствуют дескрипторы 163 из которых только 3 являются выбросами. Так что это дает действительно хорошее соотношение, которое может означать, что эти изображения могут быть дубликатами.
[ОБНОВЛЕНИЕ-3] Я не понимаю, почему вы можете инициализировать MatOfKeypoints. Я прочитал API и есть публичный конструктор. AND: Вы можете использовать мат изображения, которое хотите проанализировать. Это очень мило. знак равно
MatOfKeyPoint reference = new MatOfKeyPoint(matOfReferenceImage);
Для сопоставления используйте дескриптор- сопоставитель BRUTEFORCE_SL2, поскольку вам потребуется евклидово расстояние для SURF или SIFT.
Использование cv2.absDiff
вычислить разницу между картинками и cv2.sumElems
чтобы получить сумму всех разностей пикселей.
Затем придумайте порог, по которому вы судите, похожи два изображения или нет.
Вы можете попробовать следующий код:
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
}
Здесь в функции сравнения кода будут сравниваться два изображения, а затем, если между изображениями существует сходство, то конкретное значение матрицы будет 255, а все остальные значения будут равны нулю. Затем вы можете посчитать количество ненулевых значений, чтобы определить, были ли изображения равны. Это будет работать только для точно таких же изображений.
Если вы хотите сравнить изображения, игнорируя световые эффекты, я предлагаю вам сначала сгенерировать изображение края (используя хитрую функцию OpenCV), а затем сравнить изображения.
Надеюсь, что этот ответ поможет вам!