Object Detection and Counting

Object detection, especially recognition can be done using different technics, like a combination of OpenCV functions. For me, it was rather interesting to build a quick model in R then to spend weeks writing long C++ or .NET code for it. I started with a people counter as a practical application for the object detection and took a footage of people passing by the office.

The first thing needed is to prepare images from video using FFmpeg. Then choose a background image and create a matrix of difference between an image with an object on it and the background one. As it can be found on my blog I have created an R library for raster image processing and vectorization – fasteraster, this can be used for object detection, thus the idea was to vectorize the matrix of differences by some gradient-detected zones.

On this picture, there is a variant #1, where the matrix of pictures was represented by simple RGB values. Their comparison gave me a strong object detection of the person’s shadow (see in the course code):object detection - shadows

Thus next idea (variant #2 in code) was to calc differentials between colors (Red / Green, Green / Blue) and then compare to the background. This cleaned shadow detection but introduced another issue with a lot of detection of dark areas that probably caused by poor CMOS-camera color detection capabilities:object detection - dark areas

Then I decided to subtract the colors (Red – Green, Green – Blue) and it worked just fine. I also added filtering for detected zones weight and shown these on the video.As you can see, there is another problem when a black object moves through the black background  – it is being split into two or three parts:

In this case, I just added code to join the areas and calculate the new center of the joined object. Added track line and two green margins to detect that object passed both in the same direction:

As one can see the model itself took a page of code, most of it was for the visualization. However following items was not included in the model:

  • background image – it has to adjust to the weather, daytime and other conditions (like somebody left a bag in the middle of the observation area).
  • count the objects – simply check the vectors crossed the green margins
  • multiple objects detection – needs identification algorithm based on the path approximation.
  • joined objects recognition – needs clusterization of shape medians to split the joined area into smaller ones by average weight and path approximation.
Object detection R source code
library("png");
library("raster");
library("fasteraster");

X <- 48 * 2 ;
Y <- 27 * 2;
from <- 140;
to <- 200;

matrixFromFrame <- function (idx)
{
  v <- readPNG(sprintf("in/%03d.png", idx));
  rgb <- lapply(1:3, function(x) as.matrix(aggregate(raster(v[ , , x]), fact = 5)));
  rgb <- lapply(rgb, function(x) t(x)[1:X, Y:1]);
  #1 return(rgb);
  #2 return(list(rgb[[1]] / rgb[[2]], rgb[[2]] / rgb[[3]]));
  return(list(rgb[[1]] - rgb[[2]], rgb[[2]] - rgb[[3]], (rgb[[1]] + rgb[[2]] + rgb[[3]]) / 3));
}

processFrame <- function(idx, back)
{
#  png(file = sprintf("out/final%03d.png", idx), width = 640, height = 480);
  rggb <- matrixFromFrame(idx);
  diff <- (rggb[[1]] - back[[1]]) ^ 2 + (rggb[[2]] - back[[2]]) ^ 2;
  pol <- raster2vector(diff, 0.001, 100, 100);
  plot(0, type = "l", xlim = c(1, X), ylim = c(1, Y));
  rasterImage(readPNG(sprintf("in/%03d.png", idx), native = TRUE), 1, 1, X, Y);
  abline(v = 30, col = 'green');
  abline(v = 70, col = 'green');
  lapply(pol, function(x) lines(rbind(x, x[1,]), col = 'blue'));
  zone <- rasterZoneAnalyzer(diff, 0.001, 100, 100);
  zone <- zone[zone[ , 2] > 10, , drop = FALSE];
  #text(zone[ , 3], zone[ , 4], labels = zone[ , 2], col = 'red');
  track[[idx - from + 1, 1]] <<- sum(zone[, 2] * zone[, 3]) / sum(zone[, 2]);
  track[[idx - from + 1, 2]] <<- sum(zone[, 2] * zone[, 4]) / sum(zone[, 2]);
  lines(track, col = 'red');
  points(track, col = 'red', pch = 20);
#  dev.off();
}

track <- matrix(nrow = to - from + 1, ncol = 2);
back <- matrixFromFrame(100);
lapply(from:to, function(x) processFrame(x, back));

Andy Bosyi,
Information Technology & Data Science
Linkedin: http://www.linkedin.com/in/andybosyi