T.TAO
Back to Blog
/13 min read/Computer Graphics

VCS#2 Image Process ドキュメント

#computer-science
VCS#2 Image Process ドキュメント

これは CMU 15-473/673 Visual Computing System の課題 2:kPhone 469s における image processing のドキュメントです。この課題の目的は、待望の kPhone 469s の image sensor から生成されるデータに対して、シンプルな image processing pipeline を実装することです。

Project Overview

課題の最初のパートでは、image data を処理して、可能な限り高品質な RGB image を生成する必要があります。sensor は sensor->ReadSensorData() メソッドから RAW data を出力します。その結果は Width × Height の buffer となります。

Assumptions

このプロジェクトでは、以下の前提条件を設けます。

  1. pixel sensor は Bayer Mosaic 状に配置されていると仮定します。これは、以下の図に示すように、green pixel の数が red pixel または blue pixel の 2 倍であることを意味します。 alt text 具体的には、これは人間の知覚が緑色の光に対して最も敏感であるためです。

  2. Pixel defect(stuck pixel や感度が異常に高い pixel)は、カメラで撮影されるすべての写真において共通の static な欠陥です。

  3. Defective Row(別名 Beam)は、欠陥のある pixel の行として扱う必要があり、これらも static です。

RAW Image Process

これで image の処理を開始する準備が整いました。各ステップを正しい順序で実行することが重要です。Defective pixel correction、Beam、Vignetting は demosaicing の前に除去する必要があります。Noise filtering、white balance は demosaicing の後に適用します。

それでは、defect pixel の除去から始めましょう。

Defective Pixel Removal

defective pixel を検出する方法として、ハードコードによる手法を採用しました。周知の通り、defective pixel は明らかに逸脱した brightness を示し、異なる image 間でもその状態が維持されます。そのため、黒いシーン(black scene)とグレーのシーン(gray scene)の写真を撮ることで、簡単に特定できます。両方のシーンで同じ brightness を持つ pixel はすべて defective pixel と見なされます。

したがって、ハイレベルなアルゴリズムは次のようにまとめられます。

  • black scene(black.bin)を撮影する。

  • gray scene(gray.bin)を撮影する。

  • 逸脱した brightness(両方のシーンで等しい brightness)を持つすべての pixel を特定する。

  • それらを map に保存する。

  • 隣接する正常な pixel を使用して修正する。

Taking Photo of a Black Scene

sensor を作成し、pixel の raw data を buffer に格納するための唯一の信頼できる方法は CreateSensor() メソッドを使用することです。しかし、TakePicture() 関数を使用する際には、すでに black scene と gray scene の出力が手元にある必要があります。

そのため、クラス内に TakeBlackPicture() という別の public 関数を作成しました。これは TakePicture() とほぼ同じ動作をしますが、image を処理するために ProcessShot() の代わりに ProcessBlackshot() を呼び出す点が異なります。ProcessBlackshot() は、sensor からの RAW data を直接 image に書き込むだけのシンプルな関数です。

Create Defective Pixel Map

これで、gray image と black image を利用して、defective pixel を簡単に特定できるようになりました。まず、TakePhoto() 内でこれら 2 つの image を取得する必要があるため、この関数の interface を以下のように変更しました。

C++void TakePicture(Image & blackresult, Image & grayresult, Image & result);

同様に、ProcessShot() もこれら 2 つの image を受け取る必要があるため、その interface も変更する必要があります。

C++void ProcessShot(Image & result, Image & blackresult, Image & grayresult, unsigned char * inputBuffer, int w, int h);

両方の image が揃ったら、次にすべきことは defective pixel map を作成し、image process pipeline 内でこの map を動的に利用することです。

map の作成は単純で、同じ位置にある pixel を比較するだけです。black image と gray image で同じ brightness として現れる場合、それは defective です。

Fixing Defective Pixels

defective pixel の修正方法は、隣接する 3x3 pixel の box window の平均値を使用するだけです。 alt text

Beam Removal

Beam を除去する方法は、実際には pixel を除去するプロセスと似ています。black image を用いて検出を行いました。ある行の平均 brightness が black image 全体の平均 brightness よりも明らかに高い場合、それは defective row、すなわち Beam としてマークされます。

Compensation Map

defective map と同様に、Beam を compensation map で管理します。これを compensation map と呼ぶのは、その行の sensor が光子に対して不十分な応答(deficient response)を返していると考え、その分を単純に加算して戻すためです。

Fixing the Beams

除去パートでは、不足している行に対して brightness をゲインとして戻します。

興味深いことに、この方法で大部分の Beam は修正されましたが、元々の image にはなかった新たな Beam がいくつか生成されてしまいました。原因を特定できなかったため、最終的に(おそらく最も不格好な)解決策であるハードコードに辿り着きました。

C++void 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 が除去されました。

Vignette Removal

demosaicing の前に行う最後のステップは、vignette の除去です。

image の中心から遠い部分の brightness をブーストすることで vignette を除去します。ゲインは距離の累乗に比例させます。

この関数内のすべてのパラメータはマジックナンバーのように見えますが、実際その通りです。これらの数値は数回のテストを経て調整されたもので、比較的良好な結果が得られることが証明されています。 Vignette Removal

Demosaic

いよいよ demosaicing パートに進みます。正しい demosaic プロセスを実現するには、Bayer Mosaic の扱い方を理解する必要があります。

Algorithms

基本的に、pixel sensor は特定の色の光のみを透過させるように設計されています。そのため、これらを Bayer Mosaic 状に並べる必要があり、sensor が持っていない他の 2 つの channel については補間(interpolate)を行う必要があります。 Demosaic 1 一般的な例として、red ではない sensor の場合、隣接する 4 つの red pixel を使用してその pixel の red channel を補間する必要があります。補間の方法は以下の図に示されています。 Demosaic2 これが実装へと繋がります。

White Balance

基本的に white balance では、無彩色(neutral tone)がニュートラルに見えるように RGB 値の相対的な強度を調整します。私が行っている white balance の手法は、image の最も明るい領域を見つけ、それを白であると仮定する方法です。

white balance は demosaic の後に行われるため、Post-Processing として扱えることを覚えておいてください。ここからは post-processing が image process の中心となり、すべての関数は ImageWidthHeight を入力として受け取ります。

Noise Removal

最後に、Median Filter を使用することで、image 内の高周波ノイズを除去し、image の表面を整えてより滑らかにすることができます。

Median Filter

Gaussian Blur とは異なり、Median Filter では 1 つの明るい pixel が領域全体の平均を押し上げることはありません。単純に、kernel の中央値(median)を使用します。

Median filter アルゴリズムを最適化するために、各 RGB channel の histogram を保持し、中央値を簡単に返せるようにすべきです。 Noise Removal

Implementation

kernel サイズは変数 windowSize によって決定されます。

Autofocus

課題の後半では、コントラスト検出方式の autofocus を実装する必要があります。

sensor 領域の解析に基づき(sensor->ReadSensorData() が full sensor の crop window を返せることに注意)、sensor->SetFocus() の呼び出しを通じてカメラの focus を設定するアルゴリズムを設計する必要があります。

Set Focus

基本的に autofocus とは、ショットを最もよく「表現」する値に focus を「自動的」に設定するプロセスです。「表現」とは、image の主要な部分で最も鮮明さ(crispness)が得られることを意味します。

私が採用した解決策は、カメラに最小 focus と最大 focus を設定し、最小 focus からステップ移動しながら最もコントラストの高いショットを見つけるというものです。コントラストは Sobel kernel を適用することで決定されます。

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

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

Contrast Detection

Algorithm

各 image のコントラストは、文字通り以下の 3×33\times3 Sobel kernel を使用して計算することで決定します。

Gx=[101202101]G_x = \begin{bmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{bmatrix}

水平方向には上記を、垂直方向には

Gy=[121000121]G_y = \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ 1 & 2 & 1 \end{bmatrix}

を使用します。

image のコントラストは各 channel の gradient によって決定されます。ここで、インデックス (i,j)[1,1]2(i,j) \in [-1, 1]^2 の pixel において、各 channel の gradient は以下のように決定されます。

x=pGx,(i+1,j+1)\nabla_x = p \cdot G_{x, (i+1, j+1)}

y=pGy,(i+1,j+1)\nabla_y = p \cdot G_{y, (i+1, j+1)}

そして、コントラストは単純に以下のようになります。

k=R,G,B=x,k2+y,k2\sum_{k=R,G,B} = \sqrt{\nabla^2_{x,k} + \nabla^2_{y,k}}