用一个模糊替换一连串的图像模糊

jus*_*rld 7 c++ opencv image-processing computer-vision gaussianblur

这个问题中,我问过如何在一步中实现一连串的模糊.

然后我从维基百科的高斯模糊页面中发现:

将多个连续的高斯模糊应用于图像与应用单个较大的高斯模糊具有相同的效果,其半径是实际应用的模糊半径的平方和的平方根.例如,应用半径为6和8的连续高斯模糊给出与应用半径10的单个高斯模糊相同的结果,因为sqrt {6 ^ {2} + 8 ^ {2}} = 10.

所以我认为blur并且singleBlur在以下代码中是相同的:

cv::Mat firstLevel;
float sigma1, sigma2;
//intialize firstLevel, sigma1 and sigma2
cv::Mat blur = gaussianBlur(firstLevel, sigma1);
        blur = gaussianBlur(blur, sigma2);
float singleSigma = std::sqrt(std::pow(sigma1,2)+std::pow(sigma2,2));
cv::Mat singleBlur = gaussianBlur(firstLevel, singleSigma);
cv::Mat diff = blur != singleBLur;
// Equal if no elements disagree
assert( cv::countNonZero(diff) == 0);
Run Code Online (Sandbox Code Playgroud)

但是这assert失败了(实际上,例如,第一行与第一行blur不同singleBlur).

为什么?

更新:

在收到更多信息的不同评论之后,我会更新答案.

我要做的是并行化这段代码.特别是,我现在专注于提前计算所有级别的所有模糊.序列代码(可正常工作)如下:

   vector<Mat> blurs ((par.numberOfScales+3)*levels, Mat());
   cv::Mat octaveLayer = firstLevel;
   int scaleCycles = par.numberOfScales+2;

   //compute blurs at all layers (not parallelizable)
   for(int i=0; i<levels; i++){
       blurs[i*scaleCycles+1] = octaveLayer.clone();
       for (int j = 1; j < scaleCycles; j++){
           float sigma = par.sigmas[j]* sqrt(sigmaStep * sigmaStep - 1.0f);
           blurs[j+1+i*scaleCycles] = gaussianBlur(blurs[j+i*scaleCycles], sigma);
           if(j == par.numberOfScales)
               octaveLayer = halfImage(blurs[j+1+i*scaleCycles]);
       }
   }
Run Code Online (Sandbox Code Playgroud)

哪里:

Mat halfImage(const Mat &input)
{
   Mat n(input.rows/2, input.cols/2, input.type());
   float *out = n.ptr<float>(0);
   for (int r = 0, ri = 0; r < n.rows; r++, ri += 2)
      for (int c = 0, ci = 0; c < n.cols; c++, ci += 2)
         *out++ = input.at<float>(ri,ci);
   return n;
}

Mat gaussianBlur(const Mat input, const float sigma)
{
   Mat ret(input.rows, input.cols, input.type());
   int size = (int)(2.0 * 3.0 * sigma + 1.0); if (size % 2 == 0) size++;      
   GaussianBlur(input, ret, Size(size, size), sigma, sigma, BORDER_REPLICATE);
   return ret;
}
Run Code Online (Sandbox Code Playgroud)

我很抱歉上面的可怕索引,但我试图尊重原始代码系统(这很糟糕,就像开始计数1而不是0).上面的代码有,scaleCycles=5并且levels=6总共生成30个模糊.

这是"单一模糊"版本,首先我计算每个必须计算的模糊的sigmas(遵循维基百科的公式),然后我应用模糊(注意这仍然是串行的,不可并行化):

   vector<Mat> singleBlurs ((par.numberOfScales+3)*levels, Mat());
   vector<float> singleSigmas(scaleCycles);
   float acc = 0;
   for (int j = 1; j < scaleCycles; j++){
       float sigma = par.sigmas[j]* sqrt(sigmaStep * sigmaStep - 1.0f);
       acc += pow(sigma, 2);
       singleSigmas[j] = sqrt(acc);
   }

   octaveLayer = firstLevel;
   for(int i=0; i<levels; i++){
       singleBlurs[i*scaleCycles+1] = octaveLayer.clone();
       for (int j = 1; j < scaleCycles; j++){
           float sigma = singleSigmas[j];
           std::cout<<"j="<<j<<" sigma="<<sigma<<std::endl;
           singleBlurs[j+1+i*scaleCycles] = gaussianBlur(singleBlurs[j+i*scaleCycles], sigma);
           if(j == par.numberOfScales)
               octaveLayer = halfImage(singleBlurs[j+1+i*scaleCycles]);
       }
   }
Run Code Online (Sandbox Code Playgroud)

当然,上面的代码也会生成30个模糊,并且具有与先前版本相同的参数.

然后这是代码,看看每个signgleBlurs和之间的区别blurs:

   assert(blurs.size() == singleBlurs.size());
   vector<Mat> blurDiffs(blurs.size());
   for(int i=1; i<levels*scaleCycles; i++){
        cv::Mat diff;
        absdiff(blurs[i], singleBlurs[i], diff);
        std::cout<<"i="<<i<<"diff rows="<<diff.rows<<" cols="<<diff.cols<<std::endl;
        blurDiffs[i] = diff;
        std::cout<<"blurs rows="<<blurs[i].rows<<" cols="<<blurs[i].cols<<std::endl;
        std::cout<<"singleBlurs rows="<<singleBlurs[i].rows<<" cols="<<singleBlurs[i].cols<<std::endl;
        std::cout<<"blurDiffs rows="<<blurDiffs[i].rows<<" cols="<<blurDiffs[i].cols<<std::endl;
        namedWindow( "blueDiffs["+std::to_string(i)+"]", WINDOW_AUTOSIZE );// Create a window for display.
        //imshow( "blueDiffs["+std::to_string(i)+"]", blurDiffs[i] );                   // Show our image inside it.
        //waitKey(0);                                          // Wait for a keystroke in the window
        Mat imageF_8UC3;
        std::cout<<"converting..."<<std::endl;
        blurDiffs[i].convertTo(imageF_8UC3, CV_8U, 255);
        std::cout<<"converted"<<std::endl;
        imwrite( "blurDiffs_"+std::to_string(i)+".jpg", imageF_8UC3);
   }
Run Code Online (Sandbox Code Playgroud)

现在,我所看到的是那个blurDiffs_1.jpg并且blurDiffs_2.jpg是黑色的,但是blurDiffs_3.jpg直到它blurDiffs_29.jpg变得更白更白.出于某种原因,blurDiffs_30.jpg几乎完全是黑色的.

第一个(正确的)版本生成1761个描述符.第二个(不正确)版本生成> 2.3k描述符.

我不能发布blurDiffs矩阵因为(特别是第一个)非常大而且帖子的空间有限.我会发一些样品.我不会发帖blurDiffs_1.jpg,blurDiffs_2.jpg因为他们完全是黑人.请注意,因为halfImage图像变得越来越小(如预期的那样).

blurDiffs_3.jpg:

在此输入图像描述

blurDiffs_6.jpg:

在此输入图像描述

blurDiffs_15.jpg:

在此输入图像描述

blurDiffs_29.jpg:

在此输入图像描述

如何读取图像:

  Mat tmp = imread(argv[1]);
  Mat image(tmp.rows, tmp.cols, CV_32FC1, Scalar(0));

  float *out = image.ptr<float>(0);
  unsigned char *in  = tmp.ptr<unsigned char>(0); 

  for (size_t i=tmp.rows*tmp.cols; i > 0; i--)
  {
     *out = (float(in[0]) + in[1] + in[2])/3.0f;
     out++;
     in+=3;
  }
Run Code Online (Sandbox Code Playgroud)

这里有人建议除以diff255来看真正的区别,但我不明白我为什么理解他.

如果您需要更多详细信息,请告诉我们.

Qua*_*ang 0

基本上就是GM说的那样。请记住,您不仅通过浮点进行舍入,还通过仅查看整数点(图像上和高斯核上的)进行舍入。

这是我从小 ( 41x41) 图像中得到的结果:

在此输入图像描述

其中blursingleconvertTo(...,CV8U)和舍入的地方diff是它们不同的地方。因此,就 DSP 而言,这可能不是一个很好的协议。但在图像处理中,情况并没有那么糟糕。

另外,我怀疑当您在更大的图像上执行高斯分布时,差异会变得不那么显着。