OpenCV(C++)チュートリアル：画像のコントラストと明るさを変える

公式チュートリアルの[Changing the contrast and brightness of an image!](https://docs.opencv.org/3.4/d3/dc1/tutorial_basic_linear_transform.html)の要約。

##説明

###画像処理
* 一般的な画像処理演算とは1枚以上の画像を入力として1枚の画像を返す関数
* 画像の変換は下記に分類できる：
    * 点演算（ピクセル変換）
    * 近傍（エリアベース）演算

###ピクセル変換
* 出力の各ピクセルは対応する入力のピクセル値にのみ依存する（グローバルな情報・パラメータ等が影響することもある）
    * 例として明るさ(brightness)、コントラスト調整や色調補正、変換がある

###明るさ・コントラスト調整
* 広く使われている点処理には定数との積および加算がある：
    $ g(x)=αf(x)+β $
    $α>0$ と $β$ は**ゲイン**および**バイアス**パラメータと呼ばれ、それぞれコントラストと明るさを制御する

* $f(x)$を入力画像のピクセル、$g(x)$を出力画像のピクセルとすると
    $g(i,j)=α⋅f(i,j)+β$
    ここで$i$, $j$はピクセルがi行、j列にあることを示す。

##コード
入力画像のピクセルに$ g(x)=αf(x)+β $の変換を施すコード。
α、βはユーザ入力

```cpp:sample.cpp

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
// ここで"using namespace std;"しない。変数betaが std::beta in c++17と衝突する。
using std::cin;
using std::cout;
using std::endl;
using namespace cv;
int main( int argc, char** argv )
{
    //入力画像パスのデフォルトは../data/lena.jpg
    CommandLineParser parser( argc, argv, "{@input | ../data/lena.jpg | input image}" );
    Mat image = imread( parser.get<String>( "@input" ) );
    if( image.empty() )
    {
      cout << "Could not open or find the image!\n" << endl;
      cout << "Usage: " << argv[0] << " <Input image>" << endl;
      return -1;
    }
    //元画像と同じサイズ・タイプで、値が0埋めされた出力画像を準備
    Mat new_image = Mat::zeros( image.size(), image.type() );
    //αとβはユーザ入力
    double alpha = 1.0; /*< Simple contrast control */
    int beta = 0;       /*< Simple brightness control */
    cout << " Basic Linear Transforms " << endl;
    cout << "-------------------------" << endl;
    cout << "* Enter the alpha value [1.0-3.0]: "; cin >> alpha;
    cout << "* Enter the beta value [0-100]: ";    cin >> beta;

    //行・列・カラーチャネルをforで回し、各ピクセルに上式の変換を適用する
    for( int y = 0; y < image.rows; y++ ) {
        for( int x = 0; x < image.cols; x++ ) {
            for( int c = 0; c < image.channels(); c++ ) {
                new_image.at<Vec3b>(y,x)[c] =
                  //saturate_cast<uchar>は値が整数でない場合や範囲外の場合に有効な値に直す
                  saturate_cast<uchar>( alpha*image.at<Vec3b>(y,x)[c] + beta );
            }
        }
    }
    //元画像と結果を表示
    imshow("Original Image", image);
    imshow("New Image", new_image);
    //キー入力待ち
    waitKey();
    return 0;
}

```

なお、`image.convertTo(new_image, -1, alpha, beta);`を使えばforで回さずに同じことができる。

###コンパイル
` g++ sample.cpp  -I/usr/local/include/opencv2 -I/usr/local/include/opencv -L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_highgui`


[![結果](https://docs.opencv.org/3.4/Basic_Linear_Transform_Tutorial_Result_big.jpg "result.jpg")](https://docs.opencv.org/3.4/Basic_Linear_Transform_Tutorial_Result_big.jpg)
こんな感じになる（α=2.2, β=50）。

###ヒストグラム
####β
$β$はすべてのピクセルに対して一律に値を加算（減算）する。[0,255]の範囲を超えた値は0または255に固定される。
[![ヒストグラムb](https://docs.opencv.org/3.4/Basic_Linear_Transform_Tutorial_hist_beta.png "beta.png")](https://docs.opencv.org/3.4/Basic_Linear_Transform_Tutorial_hist_beta.png)
gimpを使ったピクセル値のヒストグラム。（たぶんコードで使ったlena.jpgとは関係ない。）
明るいグレーは入力画像、暗いグレーは明るさ=80。
定数バイアスを加えるとヒストグラム全体が右にシフトする。

####α
αは画素値の分布の広がりを変更することに相当する。$α<1$であれば広がりは圧縮され、画像はコントラストが減る。
[![ヒストグラムa](https://docs.opencv.org/3.4/Basic_Linear_Transform_Tutorial_hist_alpha.png "alpha.png")](https://docs.opencv.org/3.4/Basic_Linear_Transform_Tutorial_hist_alpha.png)
gimpを使ったピクセル値のヒストグラム。
明るいグレーは入力画像、暗いグレーはコントラスト<0
gimpの明るさ・コントラストツールは$β$については本記事と同じ処理だが、
$α$は異なるらしい（結果のヒストグラムが中央に移動している）



[元記事](https://docs.opencv.org/3.4/d3/dc1/tutorial_basic_linear_transform.html)では明るさ・コントラストよりも詳細な調整が可能がガンマ補正について解説が続きますが、ここでは割愛します。