コントラストを上げる

今回は画像のコントラストを上げる、つまり「色(主に明度)の変化」を極端にする方法を試してみます。基本的には、色の成分の変化を増やす処理になるのですが、この処理を発展させて行くとコントラスト調整だけでなく色調全体の調整にも応用出来るようになります。

色の変化とコントラスト

24ビットのフルカラービットマップでは、各ピクセルのRGB成分を0-255の範囲で記録します(図の黒い線)。コントラストを上げるという事は、この直線の「傾き」を急にしてやれば良いわけです。

ただし、傾きを急にしてもその値は0~255に収めないといけないので、一定以下の値は0、一定以上の値は255という感じで値を切り捨てる必要があります(赤線)。つまり、暗い部分は真っ暗、白っぽい部分は真っ白にして、その中間の傾きを増やすという処理をします。


今回は、切り捨てる一定値を上下同じとします。例えば、一定値を64にするのなら64未満は0、255-64=191より大きな値は255としてその間を直線的に変化させるわけです。

この場合、直線部分の傾きは256/(192-64)=2となりますので、あるピクセルの成分のコントラストを上げて結果を出力する関数は以下のようになります。


  if (x<64)
      y=0;
  else if (y>191)
      y=255;
  else
      y=2*(x-64);

最後の直線部分で64を引いているのは、直線部分が64から始まるからですね。さらに一般的にして一定値をd、ピクセルの成分値をcにするなら、以下のようになります(実際には、割り算部分に型キャストが必要)。

  if (c<d)
      return 0;
  else if (c>(255-d))
      return 255;

  return (c-d)*(256/(256-d*2));

実際にコントラストを上げる処理では、この関数を各ピクセルのRGB成分それぞれに適用して行きます。例えば、dwWidth×dwHeightピクセル(バッファの横幅dwLength)のフルカラー24ビットDIBピクセル列lpPixelのコントラストを上げるには、以下のようにします。

  void cont(void) { /* コントラストを上げる */

      DWORD i,j;

      for (i=0;i<dwHeight;i++)/* 各ピクセルのコントラストを上げる */
          for (j=0;j<dwWidth;j++) {

              lpPixel[j*3+i*dwLength]=high(lpPixel[j*3+i*dwLength],32);
              lpPixel[j*3+i*dwLength+1]=high(lpPixel[j*3+i*dwLength+1],32);
              lpPixel[j*3+i*dwLength+2]=high(lpPixel[j*3+i*dwLength+2],32);

      }

  }

  BYTE high(BYTE c,int d) { /* 各成分のコントラストを上げる */

      if (c<d)
          return 0;
      else if (c>(255-d))
          return 255;

      return (BYTE)((c-d)*(double)((double)256/(double)(256-d*2)));

  }

プログラム

プログラムソース表示

まず、ドラッグ&ドロップでフルカラービットマップを読み込んでください。続いて、コントラストを上げる強度(上の式のd)を127以下で入力欄に入れ「処理開始」ボタンをクリックすると、画像のコントラストを上げます。大体、32~64くらいで試してみると良いでしょう。

今回のコードでは、RGB値の変化を直接操作しましたが、YCrCb形式に変換した上でY(明度)やCr/Cbの絶対値(彩度)の傾き(トーンカーブ)を変えてみるのも面白そうですね。