图像处理一百题 Q.22. ヒストグラム操作

图像处理一百题 Q.22. ヒストグラム操作

题目

操作直方图,以使平均值为m0 = 128,标准偏差为s0 = 52。

这是用于将直方图更改为平坦而不是更改直方图的动态范围的操作。

为了将平均值m和标准偏差s的直方图改变为平均值m0和标准偏差s0,根据以下等式进行转换。

$$ x_{out} = {s_0\over s}(x_{in} - m) + m_0 $$

分析

这玩意儿还是直线方程,只不过变化率(换算比率)由标准差给出。问题是:为啥这么一带公式,平均值和标准差就会变成给定值呢?这很好证明:

对上式两边求和,得

$$ \Sigma_{out} = \Sigma{s_0\over s}(x_{in} - m) + \Sigma m_0 $$

而由于 m 是均值,就是原来那些数值的和:

$$ \Sigma x_{in} =\Sigma m $$

带入上式可知:

$$ \Sigma a_{out}= \Sigma m_0 $$

可以发现平均值变成了$m_0$

而新旧标准差的比值:

$$ {s_0\over s} = {{\sqrt{\Sigma(x_{out}^i - m_0)^2\over \Sigma(x_{in}^i - m)^2}}} $$

可以发现,如果想要把每个值线性地变化后满足新标准差,其实可以有无数组解。而其中最特殊的一种是:s 正比于 x - m,此时有:

$$ {s_0\over s} = {{x_{out} - m_0}\over{x_{in} - m}} $$

即是题给公式。

代码

这里就贴个官方代码吧

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
#include <math.h>


// histogram transform
cv::Mat histogram_transform(cv::Mat img, int m0, int s0){
  // get height and width
  int width = img.cols;
  int height = img.rows;
  int channel = img.channels();

  // histogram transformation hyper-parameters
  double m, s;
  double sum = 0., squared_sum = 0.;
  double val;

  // output image
  cv::Mat out = cv::Mat::zeros(height, width, CV_8UC3);

  // get sum
  for (int y = 0; y < height; y++){
    for (int x = 0; x < width; x++){
      for (int c = 0; c < 3; c++){
        val = (float)img.at<cv::Vec3b>(y, x)[c];
        sum += val;
        squared_sum += (val * val);
      }
    }
  }

  // get standard deviation
  m = sum / (height * width * channel);
  s = sqrt(squared_sum / (height * width * channel) - m * m);


  // histogram transformation
  for (int y = 0; y < height; y++){
    for ( int x = 0; x < width; x++){
      for ( int c = 0; c < 3; c++){
        val = img.at<cv::Vec3b>(y, x)[c];
        
        out.at<cv::Vec3b>(y, x)[c] = (uchar)(s0 / s * (val - m) + m0);
      }
    }
  }

  return out;
}


int main(int argc, const char* argv[]){
  // read image
  cv::Mat img = cv::imread("imori_dark.jpg", cv::IMREAD_COLOR);

  // histogram transform
  cv::Mat out = histogram_transform(img, 128, 52);
  
  //cv::imwrite("out.jpg", out);
  cv::imshow("answer", out);
  cv::waitKey(0);
  cv::destroyAllWindows();
 
  return 0;
}

发表留言

本站启用了垃圾评论检测插件,如果误删请联系我~