CraftDuino v2.0
  • - это CraftDuino - наш вариант полностью Arduino-совместимой платы.
  • CraftDuino - настоящий конструктор, для очень быстрого прототипирования и реализации идей.
  • Любая возможность автоматизировать что-то с лёгкостью реализуется с CraftDuino!
Просто добавьте CraftDuino!

OpenCV - Сравнение изображений и генерация картинки отличий

Обработка изображений и компьютерное зрение — это очень широкое поле деятельности и самых разнообразных применений!
Например, недавно на хабре была статья "Сравнение изображений и генерация картинки отличий на Ruby", которая рассказывает о способе показать разницу между двумя версиями картинок, используемой сервисом Github.
Самый простой вариант — это обход каждого пикселя в первой картинке и проверке, есть ли этот пиксель во второй, но Github использует т.н. режим тональной разницы — при этом, мы так же обходим каждый пиксель в двух изображениях и вычисляем их разницу по каналам RGB.
Этот метод сравнения двух фотографий выдаёт картину отличий, вполне неплохо показывая изменения.
Недолго думая, я набросал этот метод сравнения для OpenCV:
//
// режим тональной разницы двух изображений 
// - обходим каждый пиксель в двух изображениях и вычисляем их разницу по каналам RGB
//
//
// статья "Сравнение изображений и генерация картинки отличий на Ruby"
// http://habrahabr.ru/blogs/image_processing/117789/
//
//
// http://robocraft.ru
//

#include <cv.h>
#include <highgui.h>
#include <stdlib.h>
#include <stdio.h>

#ifndef min
#define min(a,b)            (((a) < (b)) ? (a) : (b))
#endif

int main(int argc, char* argv[])
{
	IplImage *img1=0, *img2=0, *diff=0, *sub=0;

	// дефолтные названия картинок для обработки
	char file1[] = "tup.png";
	char file2[] = "tup2.png";

	// имя картинки задаётся первым параметром
	char* filename1 = argc >= 2 ? argv[1] : file1;
	// получаем картинку
	img1 = cvLoadImage(filename1);
	printf("[i] first image: %s\n", filename1);

	// имя картинки задаётся первым параметром
	char* filename2 = argc >= 3 ? argv[2] : file2;
	// получаем картинку
	img2 = cvLoadImage(filename2);
	printf("[i] second image: %s\n", filename2);

	if(!img1){
		printf("[!] cant load image: %s\n", filename1);
		return -1;
	}
	if(!img2){
		printf("[!] cant load image: %s\n", filename2);
		return -1;
	}

	if(img1->width!=img2->width || img1->height!=img2->height){
		printf("[!] different image size!\n");
		return -2;
	}

	// покажем изображения
	cvNamedWindow( "image1");
	cvShowImage( "image1", img1 );
	cvNamedWindow( "image2");
	cvShowImage( "image2", img2 );

	// создаём картинку для хранения разницы
	diff = cvCloneImage(img1);
	sub = cvCloneImage(img1);

	cvZero(diff);

	// пробегаемся по всем пикселям изображения
	for( int y=0; y<diff->height; y++ ) {
		uchar* ptr1 = (uchar*) (img1->imageData + y * img1->widthStep);
		uchar* ptr2 = (uchar*) (img2->imageData + y * img2->widthStep);
		uchar* ptr = (uchar*) (diff->imageData + y * diff->widthStep);
		for( int x=0; x<diff->width; x++ ) {
			// 3 канала:
			// B
			ptr[3*x] = ptr1[3*x] + ptr2[3*x] - 2 * min(ptr1[3*x], ptr2[3*x]);
			// G
			ptr[3*x+1] = ptr1[3*x+1] + ptr2[3*x+1] - 2 * min(ptr1[3*x+1], ptr2[3*x+1]);
			// R
			ptr[3*x+2] = ptr1[3*x+2] + ptr2[3*x+2] - 2 * min(ptr1[3*x+2], ptr2[3*x+2]);
		}
	}

	// вычитаем 
	cvSub(img2, img1, sub);

	// выводим результат
	cvNamedWindow( "diff");
	cvShowImage( "diff", diff );

	cvNamedWindow( "sub");
	cvShowImage( "sub", sub );

	// ждём нажатия клавиши
	cvWaitKey(0);

	// освобождаем ресурсы
	cvReleaseImage(&img1);
	cvReleaseImage(&img2);
	cvReleaseImage(&diff);
	cvReleaseImage(&sub);

	// удаляем окна
	cvDestroyAllWindows();
	return 0;
}

скачать иcходник (img_difference.cpp)

подадим на вход программы те же картинки тапиров:


результат работы:

Работает :)
Но, должен отметить, что на сдвиги этот алгоритм генерирует очень и очень жуткие картинки:

Ваш Великий и Ужасный Чеширский Кот :)
  • 0
  • 29 апреля 2011, 08:51
  • noonv

Комментарии (7)

RSS свернуть / развернуть
+
0
и?

какова практическая польза?
avatar

xtile

  • 29 апреля 2011, 10:19
+
0
Github использует этот метод очень даже практически :)
avatar

noonv

  • 30 апреля 2011, 17:21
+
0
А если добавить вычисления. (Нашел на хабре) Вот сcылочка: habrahabr.ru/post/115661/
То можно определить и расстояние.
l = L*K / ( W/x — 1 + K ), где
 l – искомое расстояние до объекта, м;
 L – длина «линейки», м;
 W – длина «линейки» в пикселях, обычно совпадает с шириной изображения;
 x – координата объекта на изображении;
 K = (W — M) / M – коэффициент, отражающий наклон камеры, здесь M – координата середины «линейки».

А мне понравился метод. И вот генерация не картинки отличий, хотя кто мешает, а fgMask. И центра масс.
Знаю, что это плохой код. Если кто подскажет, как лучше буду благодарен.
Но это работает. И работает не плохо.
public class BackGround
{

CvBlobDetector _blobDetector = new CvBlobDetector();
CvBlobs blobs = new CvBlobs();
public Mat Back(ref Mat capture, Mat back, int sensetive, out int x, out int y)
{
int X = 0;
int Y = 0;

Image<Gray, Byte> maskCapture = capture.ToImage<Gray, Byte>();
Image<Gray, Byte> maskBack = back.ToImage<Gray, Byte>();
Image<Gray, Byte> result = new Image<Gray, byte>(capture.Width, capture.Height);

Image<Gray, byte>[] chanelsCapture = maskCapture.Split();
Image<Gray, byte>[] chanelsBack = maskBack.Split();
Image<Gray, byte>[] chanelsResult = result.Split();
for (int i = 0; i <= capture.Height — 1; i++)
{
for (int j = 0; j <= capture.Width — 1; j++)
{
if (chanelsBack[0].Data[i, j, 0] — sensetive >= chanelsCapture[0].Data[i, j, 0] & chanelsBack[0].Data[i, j, 0] + sensetive >= chanelsCapture[0].Data[i, j, 0])
{
chanelsResult[0].Data[i, j, 0] = 255;

}
}
}
CvInvoke.Dilate(chanelsResult[0], chanelsResult[0], null, new Point(-1, -1), 10, BorderType.Constant, new MCvScalar());

//CvInvoke.Erode(chanelsResult[0], chanelsResult[0], null, new Point(-1, -1), 10, BorderType.Constant, new MCvScalar());

_blobDetector.Detect(chanelsResult[0], blobs);

Mat ResultM = (chanelsResult[0]).Mat;

blobs.FilterByArea(5000, int.MaxValue);

foreach (var pair in blobs)
{
CvBlob b = pair.Value;
CvInvoke.Rectangle(capture, b.BoundingBox, new MCvScalar(255.0, 255.0, 255.0), 2);
X = (int)b.Centroid.X;
Y = (int)b.Centroid.Y;
}

x = X;
y = Y;
return ResultM;
}
avatar

Annihilate

  • 9 ноября 2015, 13:09
+
0
А как вывести картинку разницы на виндоус форму?
avatar

100b

  • 7 ноября 2013, 15:42
+
0
Извините, а можно этот же пример только для C#. Я так понял мы просто пробегаемся по массиву byte[] но, что то никак не выходит.
avatar

Annihilate

  • 9 октября 2015, 17:49
+
0
Использую EmguCV.

Mat imageFromCam;
imageFromCam = captureFromCam.QueryFrame();
byte[] imageMatrix = imageFromCam.GetData();
// Далее работаем с imageMatrix например
for (int m = 0; m < imageHeight; ++m )
{
                 for (int n = 0; n < imageWidth; ++n)
                 {
                     HorizontalSum += n * imageMatrix [m*imageWidth + n];
                     VerticallSum += m * imageMatrix [m * imageWidth + n];
                     BrigtnessSum += imageMatrix [m * imageWidth + n];
                 }
}
// А затем записываем измененную матрицу
imageFromCam.SetTo(imageMatrix);

avatar

RyDenisBak

  • 21 октября 2015, 19:37
+
0
Спасибо.
avatar

Annihilate

  • 23 октября 2015, 21:19

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.