Обработка изображений и компьютерное зрение — это очень широкое поле деятельности и самых разнообразных применений!
Например, недавно на хабре была статья «Сравнение изображений и генерация картинки отличий на Ruby«, которая рассказывает о способе показать разницу между двумя версиями картинок, используемой сервисом Github.
Самый простой вариант — это обход каждого пикселя в первой картинке и проверке, есть ли этот пиксель во второй, но Github использует т.н. режим тональной разницы — при этом, мы так же обходим каждый пиксель в двух изображениях и вычисляем их разницу по каналам RGB.
Этот метод сравнения двух фотографий выдаёт картину отличий, вполне неплохо показывая изменения.
Недолго думая, я набросал этот метод сравнения для OpenCV:
// // режим тональной разницы двух изображений // - обходим каждый пиксель в двух изображениях и вычисляем их разницу по каналам RGB // // // статья "Сравнение изображений и генерация картинки отличий на Ruby" // http://habrahabr.ru/blogs/image_processing/117789/ // // // https://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; yheight; 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 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)
подадим на вход программы те же картинки тапиров:
результат работы:
Работает 🙂
Но, должен отметить, что на сдвиги этот алгоритм генерирует очень и очень жуткие картинки:
Ваш Великий и Ужасный Чеширский Кот 🙂
8 комментариев на «“OpenCV — Сравнение изображений и генерация картинки отличий”»
и?
какова практическая польза?
Github использует этот метод очень даже практически 🙂
А если добавить вычисления. (Нашел на хабре) Вот с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;
}
например, создание ботов для игры в онлайн казино, которые имитируют поведение реального человека. всплывающие окна (например о заканчивающейся активации Windows или о новом обновлении SublimeText), модальные окна браузера или любые другие непредвиденные события в системе могут нарушить работу программы
А как вывести картинку разницы на виндоус форму?
Извините, а можно этот же пример только для C#. Я так понял мы просто пробегаемся по массиву byte[] но, что то никак не выходит.
Использую EmguCV.
Спасибо.