TI DM368プロセッサでのビデオ処理のアルゴリズム、続き...

記事の最初の部分では 、TI DM368ビデオプロセッサを構成するハードウェアブロックを調べ、次にアルゴリズムとコードの概要に進みます。











カメラハードウェアパイプライン全体がストリーミングモードで動作します。つまり、ブロックの1つでピクセルを処理した直後に、次のブロックに送信されます。 ビデオプロセッサソフトウェアは、オンになると、パラメータを設定し、すべてのパイプラインを初期化してから、各フレームを処理する連続サイクルに入ります。 センサーのフレームレートが30 fpsの場合、必要なすべての関数が1秒間に30回呼び出されます。

次に、アルゴリズムの動作原理について説明します。 AE(自動露出)アルゴリズムの本質は、露出、乗算係数(ゲイン)、およびシフト(オフセット)の値を設定して、出力が最小限の露出オーバーおよび暗くなった領域を持つ画像を受け取るようにすることです。







センサーに入る光の量は、露出または電子シャッターが原因です。 最新のセンサーでは、1,000,000から1マイクロ秒まで変化する可能性があるため、光量を制限するためにダイアフラムを使用する必要はありません。 ゲインとオフセットは、12ビットから8ビットへの変換に使用され、わずかな8ビットダイナミックレンジを最大化するように選択されます。

露出値を決定する方法は? いくつかのオプションを試しました。つまり、上からそれを制限しました。つまり、ヒストグラムの最大値が特定のしきい値を下回るように露出を変更し、ヒストグラムの下限を特定の値に下げました。







どちらの方法もダイナミックレンジ全体を最大限に活用しますが、1つの欠点があります。明るい晴れた日には、日陰のオブジェクトは実質的に見えません。 したがって、調光を検討するには、シーンの特定の部分を露出オーバーにする方が良い場合があります。 したがって、自由遊泳のヒストグラムの最大値をリリースし、Yの平均値が選択した値と一致するように露出を選択しました。







アルゴリズムに移りましょう。自動露出から始めましょう。



1.アルゴリズムの作業は、Boxcarから統計データを取得することから始まります。



Uint32 w, h; Uint16 *box; status = DRV_ipipeGetBoxcarBuf(&bufId, 0); if(status!= OSA_SOK) { OSA_ERROR("ERROR: DRV_ipipeGetBoxcarBuf()\n"); return status; } pBufInfo = DRV_ipipeGetBoxcarBufInfo(bufId); DRV_ipipePutBoxcarBuf(bufId); box = pBufInfo->virtAddr; w = gDRV_ipipeObj.boxcarInfo.width; //Boxcar width h = gDRV_ipipeObj.boxcarInfo.height; // Boxcar height
      
      







2.次に、これらのデータに基づいて、ヒストグラムが作成され、Yの平均値が計算されます。



 Uint32 sz = w*h, sz4 = sz*4, hsz = 512; Uint32 hist[hsz]; int GN[3] = { -16, 0, 16}; memset(hist, 0, sizeof(Uint32)*hsz); //AE and WB for(i=0; i < sz4; i+=4) { r = box[i+2]>>2; g = box[i+1]>>2; b = box[i ]>>2; RR += r; GG += g; BB += b; Y += ((117*b + 601*g + 306*r)>>10); hist[r>>3]++; hist[g>>3]++; hist[b>>3]++; for(j=0; j < ns; j++) { GB[j] += abs(g - (b*(512 + GN[j])>>9)); GR[j] += abs(g - (r*(512 + GN[j])>>9)); } } Y = Y/sz; hn->Y.New = Y; RR = RR/sz; GG = GG/sz; BB = BB/sz;
      
      





3.ヒストグラムの最小値を見つけます。



  //Find histogram min sum = 0; for(i=0; sum < hn->SatTh; i++) sum += hist[i]; hn->Hmin.New = i; //Find histogram max sum = 0; for(i=hsz-1; sum < hn->SatTh; i--) sum += hist[i]; hn->Hmax.New = i;
      
      





4.露出値を変更します。 Yの平均値の値がしきい値YAEの2倍より大きいか小さい場合、変化のステップは増加し、Yの変化が20%より小さい場合、露出は変化しません。



  if(hn->Y.New) tmp = (hn->Y.New > hn->YAE) ? hn->Y.New*100/hn->YAE : hn->YAE*100/hn->Y.New; if(tmp > 200){ if(hn->Y.New) hn->Exp.New = hn->Exp.Old*(hn->Y.New*2 + hn->YAE)/(hn->Y.New*3); } else if(tmp > 20){ if(hn->Y.New > hn->YAE) hn->Exp.New = hn->Exp.Old*99/100; else hn->Exp.New = hn->Exp.Old*100/99; } if(hn->Exp.New > hn->Exp.Range.max) hn->Exp.New = hn->Exp.Range.max;
      
      





5.ビデオストリームの画像をスムーズに変更するために、AEアルゴリズムで使用される値の最後のいくつかの値を平均します。



 #define HISTORY 30 int history = 0; typedef struct IAEWBF_Param{ XDAS_Int32 Old; //Old value XDAS_Int32 New; //New value XDAS_Int32 Step; //The step of changing XDAS_Int32 Avrg; //Sum of all history value XDAS_Int32 Change; //Need for smooth change XDAS_Int32 Hist[HISTORY]; //History array XDAS_Int32 HistC; //History count XDAS_Int32 NewA; //Avarage of value IAEWBF_Range Range; //The range of value changes }IAEWBF_Param; int add_history(IAEWBF_Param *p) { int diff = 0; p->Avrg += p->New; p->Avrg -= p->Hist[p->HistC]; if(p->New) diff = abs(p->Hist[p->HistC] - p->New)*100/p->New; p->Hist[p->HistC] = p->New; p->HistC = (p->HistC == (HISTORY - 1)) ? 0 : p->HistC + 1; p->NewA = (history < HISTORY) ? p->Avrg/history : p->Avrg/HISTORY; return diff; } history++; add_history(&hn->Hmax); add_history(&hn->Hmin); add_history(&hn->Y);
      
      





6.最後に、AEの最後のステップは、ゲインとシフトを設定することです。 出力では、Yの平均値が出力範囲HmaxThのほぼ中央にあることに注目します。 ガンマ補正と実験を考えると、この値はhn-> HmaxTh / 4に等しくなります。



  //Change the offset and gain hn->Offset.New = hn->Hmin.NewA; if(hn->Y.NewA - hn->Offset.New) hn->GIFIF.New = ((hn->HmaxTh/4)*512)/(hn->Y.NewA - hn->Offset.New); up = hn->Hmax.NewA*hn->GIFIF.New>>9; if((up < hn->HmaxTh) && (hn->Y.NewA - hn->Offset.New)) if(hn->Y.NewA - hn->Offset.New) hn->GIFIF.New = (((hn->HmaxTh*2 - up)/4)*512)/(hn->Y.NewA - hn->Offset.New); //Check gain range hn->GIFIF.New = hn->GIFIF.New > hn->GIFIF.Range.max ? hn->GIFIF.Range.max : hn->GIFIF.New; hn->GIFIF.New = hn->GIFIF.New < hn->GIFIF.Range.min ? hn->GIFIF.Range.min : hn->GIFIF.New;
      
      





次に、 ホワイトバランス (WB)に進みましょう。

それを設定するための多くのアルゴリズムがあり、カメラでそれらの2つを使用します。

日中モードでは、画像全体の局所的な色のずれを最小限に抑えます。 白、黒、グレーはR = G = Bであり、白もシーンで最も明るいので、その最小化は総エネルギーに最も大きく貢献します。

夜間、赤外線フィルターが開いているときは、 グレーワールドアルゴリズムを使用します。つまり、単純に色平均を揃えます。

メインサイクルでは、ヒストグラムに記入するときに、赤のGR [j]と青のGB [j]の緑からの合計偏差の値を探し、移動の方向を最小限に抑えます。



  if(IRcutClose){ min = GR[0]; minr = 0; for(j=1; j < ns; j++){ if(GR[j] < min) { min = GR[j]; minr = j; } } min = GB[0]; minb = 0; for(j=1; j < ns; j++){ if(GB[j] < min) { min = GB[j]; minb = j; } } if(minr != 1) hn->Rgain.New = hn->Rgain.Old + (GN[minr]*hn->Rgain.Old/512); if(minb != 1) hn->Bgain.New = hn->Bgain.Old + (GN[minb]*hn->Bgain.Old/512); } else { //Night AW mode if(RR) hn->Rgain.New = GG*hn->Rgain.Old/RR; if(BB) hn->Bgain.New = GG*hn->Bgain.Old/BB; }
      
      





センサーによって色の偏差はまったく異なりますが、アルゴリズムは日中常に正しく動作します。ホワイトバランスの前後のSONY IMX136センサーの例:







ガンマ補正について説明します。カメラと人間の目との感度を合わせるために必要です。 原則として、変換関数にはべき法則依存性がありますが、主観的な感覚に応じて別のものを使用し、現実をよりよく反映します。



gam [i] = out *((log(i + in * a)-log(in * a))/(log(in + in * a)-log(in * a)));



inは入力範囲の最大値、

out-最大出力範囲、

aは曲率係数です(この場合、0.05はさまざまなシーンに最適です)







システムを初期化するときに、この曲線をLUTテーブルにロードし、各フレームがそれを通過します。 ガンマ補正が画像をどのように変化させるかの例:







私たちのカメラの機能についても言及したいと思いますが、これはあまり一般的ではありません。 第一に、照度を下げてより低いフレームレートに切り替えること、そして第二に、夜間モードで赤外線フィルターを取り外すと、カメラはカラーのままになります。



カメラのファームウェアのソースコードは、git://sigrand.ru/sigticam.gitのGitバージョン管理システムから入手できます。 ソースコードをダウンロードするには、次のコマンドを使用できます:git clone git://sigrand.ru/sigticam.git。

上記のアルゴリズムは、ディレクトリsigticam / sigticam / platform / ti_dm368 / appro2 / av_capture / framework / alg / src / aewbf_sigにあります

こちらからだけでなく、当社のカメラからライブ放送を見ることができます

次の記事では、カメラにオートフォーカスがどのように実装されるかを見ていきます

また、HDRまたはWDRの広いダイナミックレンジとは何ですか。






All Articles