T.TAO
Back to Blog
/12 min read/Computer Science

VCS #2 画像処理文書

#computer-science
VCS #2 画像処理文書

VCS #2 画像処理 ドキュメント

これは CMU 15-473/673 Visual Computing System の課題2:期待の kPhone 469s の画像センサーが生成するデータの画像処理のドキュメントです。タスクは、kPhone 469s の画像センサーが生成するデータ用のシンプルな画像処理パイプラインを実装することです。

プロジェクト概要

課題の最初の部分では、できるだけ良い見た目の RGB 画像を生成するために画像データを処理する必要があります。センサーは sensor->ReadSensorData() メソッドから RAW データを出力します。結果は Width × Height のバッファです。

前提条件

このプロジェクトでは以下の前提を置きます。

  1. ピクセルセンサーは Bayer Mosaic で配置されていると仮定できる。つまり、緑ピクセルの数は赤または青ピクセルの数の2倍。以下の図に示すように。

これは、人間の知覚が緑光に最も敏感であるためです。

  1. ピクセル欠陥(スタックピクセル、感度が異常に高いピクセル)は、カメラで撮影したすべての写真で同じ静的欠陥である。

  2. 欠陥行(Beams とも呼ばれる)は、欠陥ピクセルの行として扱うべき。これらも静的である。

RAW 画像処理

画像の処理を開始できます。ステップを正しい順序で行うことが重要です。欠陥ピクセル補正、Beams、ビネットはデモザイクの前に除去する必要があります。ノイズフィルタリング、ホワイトバランスはデモザイクの後に除去する必要があります。

したがって、欠陥ピクセル除去から始めましょう。

欠陥ピクセル除去

欠陥ピクセルを検出する方法はハードコード方式です。欠陥ピクセルは明らかに異常な輝度を示し、異なる画像でも同じままです。したがって、黒いシーンの写真とグレーのシーンの写真を撮ることで、簡単に見つけられます。両方のシーンで同じ輝度のピクセルは、欠陥ピクセルとみなされます。

したがって、高レベルでは、アルゴリズムは次のように要約できます:

  • 黒いシーンの写真を撮る(black.bin)。
  • グレーのシーンの写真を撮る(gray.bin)。
  • 異常な輝度のピクセル(両方のシーンで同じ輝度)をすべて見つける。
  • マップに格納する。
  • 隣接する非欠陥ピクセルを使用して修正する。

黒いシーンの写真を撮る

センサーを作成し、ピクセル RAW データでバッファを埋める唯一の信頼できる方法は、CreateSensor() メソッドを使用することです。ただし、TakePicture() 関数を使用するときは、すでに黒いシーンとグレーのシーンの出力を手元に持っている必要があります。

したがって、クラスに TakeBlackPicture() という別の public 関数を作成しました。TakePicture() とほぼ同じことを行いますが、ProcessShot() の代わりに ProcessBlackshot() を呼び出して画像を処理します。ProcessBlackshot() は、センサーからの RAW データを画像に直接書き込む単純な関数です。

欠陥ピクセルマップの作成

グレー画像と黒画像の助けを借りて、欠陥ピクセルを簡単に特定できます。まず、TakePhoto() でこれら2つの画像を撮る必要があるため、この関数のインターフェースを変更しました:

Plain Textvoid TakePicture(Image & blackresult, Image & grayresult, Image & result);

同様に、ProcessShot() もこれら2つの画像を受け取る必要があるため、そのインターフェースも変更する必要があります。

Plain Textvoid ProcessShot(Image & result, Image & blackresult, Image & grayresult, unsigned char * inputBuffer, int w, int h);

両方を持ったら、次にやることはこの欠陥ピクセルマップを作成し、画像処理パイプラインでこのマップを動的に使用することです。

マップの作成は簡単で、同じ位置のピクセルを比較するだけです。黒画像とグレー画像で同じ輝度で表示される場合、欠陥です。

欠陥ピクセルの修正

欠陥ピクセルを修正する方法は、隣接する 3x3 ピクセルのボックスウィンドウの平均を使用するだけです。

Beam 除去

Beam を除去する方法は、ピクセル除去のプロセスに似ています。黒画像で検出を行いました:行の平均輝度が黒画像全体の平均輝度より明らかに高い場合、欠陥行、つまり Beam としてマークする必要があります。

補正マップ

欠陥マップと同様に、Beam を補正マップで維持します。この行のセンサーが光子に対する不足した応答を得ていると信じているため、この部分を単純に戻すと呼びます。

Beam の修正

除去部分では、不足した行に輝度を戻すだけです。

興味深いことに、この方法で Beam の大部分は修正されましたが、元々画像になかったいくつかの Beam が新たに作成されました。理由がわからないので、(おそらく最も醜い)解決策で終わりました。ハードコードです。

Plain Textvoid FixDefectiveRow(int x, int y, unsigned char * inputBuffer, int w, const std::vector<float> &compensationMap)
{ 
	// OMIT
	if (compensationMap[y] != 0.0) {
		if(y != 195 && y != 437 && y != 438 && y != 487 && y != 558 && y != 559 && y != 557 && y != 560)
         // Other parts
	}
}

何と?これで本当にすべての Beam が除去されました。

ビネット除去

デモザイクの前に行う最後のステップは、ビネットを除去することです。

画像の中心から遠い輝度をブーストしてビネットを除去します。ゲインは距離の累乗に比例します。

この関数内のすべてのパラメータはマジックナンバーとして表示され、はい、実際にそうです。これらの数値はいくつかのテストの後に調整され、比較的良好な結果を生成することが証明されました。

デモザイク

ついにデモザイク部分に進めます。正しいデモザイクプロセスを得るには、Bayer Mosaic の扱い方を理解する必要があります。

アルゴリズム

基本的に、ピクセルセンサーは特定の色の光のみを通すように設計されています。したがって、Bayer Mosaic に配置する必要があり、センサーが持たない他の2つのチャンネルを補間する必要があります。

通常、例えば、赤でないセンサーの場合、隣接する4つの赤ピクセルを使用してそのピクセルの赤チャンネルを補間する必要があります。補間の方法は以下の図に示されています。

これが実装につながります。

ホワイトバランス

基本的にホワイトバランスでは、ニュートラルなトーンがニュートラルに見えるように RGB 値の相対強度を調整したい。ホワイトバランスの方法は、画像の最も明るい領域を見つけて、それを白と仮定することです。

ホワイトバランスはデモザイクの後なので、ポストプロセスとして扱えます。これ以降、ポストプロセスが画像処理を支配します。つまり、すべての関数は ImageWidthHeight を入力として受け取ります。

ノイズ除去

最後に、メディアンフィルターを使用して、画像の高周波ノイズをフィルタリングし、画像の表面に触れてより滑らかにします。

メディアンフィルター

ガウシアンブラーとは異なり、メディアンフィルターでは、1つの明るいピクセルが領域全体の平均を引き上げません。単純に、カーネルの中央値を使用します。

メディアンフィルターアルゴリズムを最適化するには、各 RGB チャンネル用にヒストグラムを保持して、中央値を簡単に返せるようにする必要があります。

実装

カーネルサイズは変数 windowSize で決定されます。

オートフォーカス

課題の後半では、コントラスト検出オートフォーカスを実装する必要があります。

センサーの領域の分析に基づいて(sensor->ReadSensorData() がフルセンサーのクロップウィンドウを返せることに注意)、sensor->SetFocus() の呼び出しを通じてカメラのフォーカスを設定するアルゴリズムを設計する必要があります。

フォーカス設定

基本的に、オートフォーカスは、ショットを表現する値にフォーカスを自動的に設定するプロセスです。表現とは、画像の主要部分で最も鮮明さを持ちたいということです。

行っている解決策は、カメラの最小フォーカスと最大フォーカスを設定し、最小フォーカスからステップして、最もコントラストの高いショットを見つけることです。コントラストは Sobel カーネルを適用することで決定されます。

この部分では RAW コントラストのみを考慮すればよいため、以前作成した ProcessBlackshot() 関数を再度適用できます。

最後に残っているメソッド CalculateImageContrast() を実装すれば十分です。

コントラスト検出

アルゴリズム

各画像のコントラストは、3×33\times3 Sobel カーネルを使用して計算することで決定します。これは文字通り

水平方向用と

垂直方向用です。

画像のコントラストは各チャンネルの勾配によって決定され、インデックス (i,j) ∈ [-1, 1]^2 のピクセルについて、各チャンネルの勾配は次で決定され、コントラストは単純に

学術誠実性の理由により、このプロジェクトのコードのほとんどは非表示にしています。何らかの理由でコードベースへのアクセスをご希望の場合は、linghent@andrew.cmu.edu または lockbrains@gmail.com の Tony までご連絡ください。より良い読書体験のために、こちらのリンクをご利用ください:https://hackmd.io/@Lockbrains/vcs_2_image_process