图像处理一百题 Q.21. ヒストグラム正規化(直方图归一化)

图像处理一百题 Q.21. ヒストグラム正規化(直方图归一化)

题目

Histogram normalization

ヒストグラム正規化を実装せよ。

ヒストグラムは偏りを持っていることが伺える。 例えば、0に近い画素が多ければ画像は全体的に暗く、255に近い画素が多ければ画像は明るくなる。 ヒストグラムが局所的に偏っていることをダイナミックレンジが狭いなどと表現する。 そのため画像を人の目に見やすくするために、ヒストグラムを正規化したり平坦化したりなどの処理が必要である。

このヒストグラム正規化は濃度階調変換(gray-scale transformation) と呼ばれ、[c,d]の画素値を持つ画像を[a,b]のレンジに変換する場合は次式で実現できる。 今回はimori_dark.jpgを[0, 255]のレンジにそれぞれ変換する。

2019-10-24T15:25:08.png

实现直方图归一化。

可以看出直方图具有偏差。 例如,如果有许多像素接近0,则图像整体较暗,而如果有许多像素接近255,则图像变亮。 直方图局部偏向的事实表示为动态范围较窄。 因此,为了使人眼容易看到图像,需要对直方图进行归一化或平坦化等处理。

这种直方图归一化称为灰度转换。将像素值为[c,d]的图像转换为[a,b]的范围时, 可以实现。 这次, imori_dark.jpg 分别转换为范围[0,255]。

分析

这个很简单,只是题目没说清楚要干什么。总而言之就是,输入一个比较灰暗的图像。灰暗的原因,是这个图像的灰度值分布太集中(具体来说是分布在区间[c,d]),需要让其分布得均匀些(让其变成分布在[a, b], a = 0, b = 255)。于是将输入值带入公式,得到输出值。我们看上面的那个分段函数,会发现中间段就是一个一次函数,这个公式怎么来的呢?其实我们可以看成一个三角:

       /|p2(d,b)
      / |
     /  |
    /   |
   /____|
p1(c,a)

就是很简单地把水平方向的 c,d 宽度映射到了竖直方向的 a,b 高度。所以上面的公式其实就是两点间的直线公式。

代码

这里就贴个官方代码吧

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


// histogram normalization
cv::Mat histogram_normalization(cv::Mat img, int a, int b){
  // get height and width
  int width = img.cols;
  int height = img.rows;
  int channel = img.channels();

  int c, d;
  int val;

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

  // get [c, d]
  for (int y = 0; y < height; y++){
    for (int x = 0; x < width; x++){
      for (int _c = 0; _c < channel; _c++){
        val = (float)img.at<cv::Vec3b>(y, x)[_c];
        c = fmin(c, val);
        d = fmax(d, val);
      }
    }
  }

  // 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];
        
        if (val < a){
          out.at<cv::Vec3b>(y, x)[_c] = (uchar)a;
        }
        else if (val <= b){
          out.at<cv::Vec3b>(y, x)[_c] = (uchar)((b - a) / (d - c) * (val - c) + a);
        }
        else {
          out.at<cv::Vec3b>(y, x)[_c] = (uchar)b;
        }
      }
    }
  }

  return out;
}


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

  // histogram normalization
  cv::Mat out = histogram_normalization(img, 0, 255);
  
  //cv::imwrite("out.jpg", out);
  cv::imshow("answer", out);
  cv::waitKey(0);
  cv::destroyAllWindows();
 
  return 0;
}

发表留言

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