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


Обработка изображений и компьютерное зрение — это очень широкое поле деятельности и самых разнообразных применений!
Например, недавно на хабре была статья «Сравнение изображений и генерация картинки отличий на 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; xwidth; 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), модальные окна браузера или любые другие непредвиденные события в системе могут нарушить работу программы

    • Использую 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);
      
      

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Android Arduino Bluetooth CraftDuino DIY IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна конкурс манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение