Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

Unexpected Results at Subtracting Two Images

Hello there,

I try to implement subtraction of two images. I am sure there are too many people who are ready to shot me with their "Why don't you just use cv::subtract or simply cv::Mat Result = Image1 - Image2;" gun. Jokes aside, the reason is that I try to implement my own version to use it as a function which is going to run on separate threads. When I used

    #include <opencv2/opencv.hpp>
    #include <opencv2/core/core.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <iostream>
    #include <string>
    #include <cstdlib>
    #include <cstdio>
    #include <new>
    #include <chrono>
    #include <omp.h>

void rgbToHsv(cv::Mat* inputImage, cv::Mat* outputImage);
void subtractImage(cv::Mat* inputImage, cv::Mat* outputImage, cv::Mat* imageMask);

int main(int argc, char const *argv[])
{
  cv::Mat inputImage = imread("lena.png", cv::IMREAD_UNCHANGED); //Input image

  //Converting image from RGB to HSV colorspace
  cv::Mat inputImageHsv = inputImage.clone();
  rgbToHsv(&inputImage, &inputImageHsv);

//Splitting V channel for later use
  cv::Mat inputImageHsvChannels[3];
  cv::split(inputImageHsv, inputImageHsvChannels);

  cv::Mat inputImageH = inputImageHsvChannels[0];
  cv::Mat inputImageS = inputImageHsvChannels[1];
  cv::Mat inputImageV = inputImageHsvChannels[2];

  cv::Mat blurredImage = inputImageV.clone();
  cv::Mat imageMask = inputImageV.clone();
  cv::Mat imageMask2 = inputImageV.clone();
  cv::Mat imageMask3 = inputImageV.clone();

  cv::GaussianBlur(inputImageV, blurredImage, cv::Size(5,5), 0, 0);


  cv::subtract(inputImageV,blurredImage, imageMask);
  imageMask2 = inputImageV - blurredImage;  
  subtractImage(&inputImageV, &blurredImage, &imageMask3);

  cv::imshow("Subtracted Image1 OPENCV", imageMask);
  cv::imshow("Subtracted Image2 OPENCV", imageMask2);
  cv::imshow("Subtracted Image3 MY FUNC", imageMask3);
  cv::waitKey();

  return 0;
}

void subtractImage(cv::Mat* inputImage1, cv::Mat* inputImage2, cv::Mat* outputImage)
{
  for(int i = 0; i < inputImage1->rows; ++i)
  {
    for(int j = 0; j < inputImage1->cols; ++j)
    {
      int iVal = inputImage1->at<uchar>(i,j) - inputImage2->at<uchar>(i,j);

      if(iVal < 0)
        outputImage->at<uchar>(i,j) = 0;

      outputImage->at<uchar>(i,j) = iVal;
    }
  }
}

void rgbToHsv(cv::Mat* inputImage, cv::Mat* outputImage)
{
  double redSc = 0, greenSc = 0, blueSc = 0; //Scaled R, G, B values of current pixel
  double h = 0, s = 0, v = 0; //R, G, B values of current pixel
  double cmin = 0, cmax = 0; //Min and max dummy variables
  double delta = 0; //Difference between min and max

  int channels = inputImage->channels();
  int nRows = inputImage->rows;
  int nCols = inputImage->cols*channels;

  if (inputImage->isContinuous())
  {
    nCols *= nRows;
    nRows = 1;
  }

  uchar* p;
  uchar* q;

  for(int i = 0; i < nRows; ++i){
    p = inputImage->ptr<uchar>(i);
    q = outputImage->ptr<uchar>(i);

    for(int j = 0; j < nCols; j+=3){    
      redSc = p[j+2] / 255.;
      greenSc = p[j+1] / 255.;
      blueSc = p[j] / 255.;

      cmin = std::min(std::min(redSc, greenSc), blueSc);
      cmax = std::max(std::max(redSc, greenSc), blueSc);
      delta = cmax - cmin;

      if(!delta){
        h = 0.;
        s = 0.;
        v = cmax * 255.;
      }
      else{
        if(cmax == redSc)
        h = 60. * ((greenSc - blueSc)/delta);

        if(cmax == greenSc)
        h = 120 + (60. * (((blueSc - redSc)/delta)));

        if(cmax == blueSc)
        h = 240 + (60. * (((redSc - greenSc)/delta)));

        if(h < 0)
        h += 360;

        h = (h/2);

        v = cmax* 255.;

        s = ((cmax==0)?0:((delta/cmax)*255.));

        q[j+2] = v;   //Red
        q[j+1] = s; //Green
        q[j] = h; //Blue   
      }
    }
  }
}

Here are the results.

OpenCV Style 1 OpenCV Style 2 My Implementation