nao-milkの経験ブログ

25年間の半導体エンジニア経験で知り得た内容を記載したブログです。

浮動小数を使用せず演算

f:id:nao-milk:20210330230451p:plain

小数を含む演算は、浮動小数点ユニットを使用する為、処理速度が遅くなります。

しかし、その式に乗算とビットシフトを追加することで演算速度が速くなります。

 

その方法を順を追って、また、ソフトウェア(C言語)とハードウェア(半導体設計)に分けて説明します。

 

まずは説明する上で、画像処理の1つ「RGB→YUV(YCbCr)変換」をご存じでしょうか?

この変換式を例に以下説明します。

 

YUVの用途 (ちょっとした用途の説明)

YUVは、人間の目の特性を利用したデータであり、変化を敏感に感じ取る輝度そうではない色差に分かれます。

従って、

輝度さえしっかりしていれば色差は間引くことが可能とも言えます。

(詳細はネット上に説明があるので、調べてください。)

尚、

 Yは、輝度

 Uは、輝度信号と青色成分の差

 Vは、輝度信号と赤色成分の差

を示します。

 

この特性を持ったYUVは、画像(静止画)や映像(動画)圧縮(JPEG,MPEG,H.264など)で使われます。

例えば、

カメラで撮った映像(動画)を圧縮して動画ファイルとして保存する場合です。

 1、カメラからR(赤),G(緑),B(青)形式で撮ります。

 2、Image ProcessorRGB形式をYUV形式に変換して各圧縮に応じた処理を行います。

 3、圧縮後のデータをファイルに保存します。

f:id:nao-milk:20210330164800p:plain

 

RGB→YUV変換式

RGBからYUVへの変換式は、以下の通りです。

f:id:nao-milk:20210330181236p:plain

演算式①

上記の式では、YUV各成分の係数を足すと1.000になるのが特徴です。

 例)

  Y成分の係数の和 = 0.299 + 0.587 + 0.114  = 1.000

尚、変換式は圧縮規格を発表している所で変わるため、各勧告書を確認してください。

 

C言語の記述例①

演算式①をそのままC言語で記述すると以下のようになります。

f:id:nao-milk:20210330172133p:plain

C言語 記述例①

15~17行目で変換式そのままで演算し、cy,cu,cvを算出しています。

また、係数が小数の為、演算結果もdouble型(倍精度浮動小数)で演算しています。

※double型にすると、64bit演算となります。

※float型の場合は、32bitとなります。

 

この記述例①のソースでは、CPUはFPU(浮動小数点ユニット)を使用します。

 

たかがRGB各8bitデータからYUV各8bitデータを求めるのに64bit(or 32bit)浮動小数点ユニットを使用し、

しかも、キャスト演算(unsigned char 、char)で小数部は切り捨てられるにも関わらず。

私としては、浮動小数点ユニットは使用する必要は無いと考えます。

尚、浮動小数演算を使うことにより、1つの演算に時間がかかります

 

浮動小数を使わずに演算

では、小数を含む演算を浮動小数点ユニットを使用せず演算する方法は?

※注意事項となりますが、この方法は浮動小数点演算との差は発生します。

 ただ誤差も小さいため、製品精度又は画質評価を行って確認してください。

 (人間の目なのでわからないかもしれませんが。。。)

 

方法はすごく簡単です。

各係数を何倍かにして、算出した各YUV成分を倍した分で割るだけです。

尚、

割り算は処理に時間がかかるため、ビットシフトで演算できるよう、各係数を2のべき乗で掛けます。

演算式は以下のようになります。

f:id:nao-milk:20210330181138p:plain

演算式②

尚、係数(小数点以下あり)に2のべき乗を掛けた結果は、小数点以下切り捨て又は四捨五入して整数とします。

 

演算式①の係数を256倍(n=8) 及び 65536倍(n=16)にした場合の係数を以下に示します。

(下の表では、小数点以下を四捨五入しています。)

f:id:nao-milk:20210330181929p:plain

演算式②の各係数

上記係数を用いて演算した例を以下に示します。

尚、「256で割った値」と「65536で割った値」は誤差を示すため便宜上、小数点以下も表示させています。

 

f:id:nao-milk:20210330183415p:plain

演算例

上記演算例では、

係数を256倍すると、Y成分の整数部で誤差が発生しています。

この場合、YUV各成分結果を算出する時に小数点以下を切り捨てると、浮動小数計算と1bitの誤差が発生します。

また同様に65536倍した場合も小数部で浮動小数計算と誤差が発生しています。

 

これらの誤差が製品の許容範囲かどうかを画質評価などで確認する必要があります。

尚、演算式②の「n」を大きくするほど精度は向上します。

※YUV各成分は演算結果の整数部の8bitとなり、画質評価はその8bitの1~2bitの誤差をどこまで許せるかの判断になります。

 

C言語の記述例②

演算式②でn=16(65536倍)とした場合のC言語記述例を以下に示します。

尚、係数は表「演算式②の各係数」を参照してください。

f:id:nao-milk:20210330185415p:plain

C言語 記述例②

15~17行目で演算し、cy,cu,cvを算出しています。

この演算では、係数は小数ではなく整数 且つ RGB各成分をInt型にキャスト 且つ ビットシフトで割り算を行っています。

 

これにより、浮動小数点ユニットを使用する要素は無くなります。

 

浮動小数演算を使うと演算に時間がかかる

演算式①(記述例①)と演算式②(記述例②)でなぜ演算速度が変わるのか?

です。

 

浮動小数演算自体、乗算及び除算に対してはスムーズですが、加算及び減算は面倒なのと、整数から浮動小数浮動小数から整数にするため、フォーマット変換する必要があるためです。(詳細はネット上に説明があるので、調べてください。)

浮動小数のフォーマットと演算の仕組みが分かれば納得がいくと思います。

 

演算式①と演算式②で、Y成分を求めるまでの処理の流れを以下に示します。

※処理イメージとなり、CPUやコンパイラにより処理順序など変わります)

f:id:nao-milk:20210330191458p:plain

演算式①と演算式②の処理の流れ

大きな違いは、最初に「64bit浮動小数変換」が入ることです。

これにより、演算式②と比べて演算式①は「3つの処理」が増えるため、処理が遅くなります。

 

この「3つの処理」があることにより、

4Kテレビの場合では、縦横の画素数が3,840×2,160であり、計8,294,400(約800万)画素となり、演算式②に対して演算式①は24,883,200(=8,294,400×3)回数処理が余分にあることになります。そのためすごく遅く感じることとなります。
※処理クロック数でみると、整数演算と浮動小数演算とでも違いがあります。

 この違いは、CPUの仕様を確認してください。

 

RTL設計

上記まではソフトウェアを中心とした説明でしたが、ここからは半導体設計の場合で説明します。

 

半導体設計では、CPUを使わずRGB→YUV変換を自作します。

浮動小数演算で設計することも可能ですが、カメラ速度と出力速度を考慮すると演算式②で論理回路を作成します。

 

回路構成

演算式②の演算構成は、以下の通りとなります。

尚、先の「演算式①と演算式②の処理の流れ」と分かりやすいようにするため、各演算は2入力としています。

f:id:nao-milk:20210330210327p:plain

RTL図

※上図はY成分を算出する構成で記載していますが、U,Vも同じ構成 且つ Y成分構成と並列で処理したり、クロックの速度を3倍にしてシリアルで処理したりします。

 

verilog-HDLの記述例

演算式②のverilog-HDL記述例を以下に示します。

f:id:nao-milk:20210330211523p:plain

verilog-HDL記述例

65行目でClip処理を行っていますが、

係数の和が1(65536)となるようにしているため、TRGBが0xFF0000以上の値になることはありませんが、例として記述しています。

尚、Y成分は負数となることがありません。

 

タイミング図

RTL図のタイミング図を以下に示します。

f:id:nao-milk:20210330211303p:plain

タイミング図

 

最後に

係数が小数の場合、この方法で浮動小数点ユニットを使用せず計算できます。

それにより、処理速度も速くなります。

 f:id:nao-milk:20210330213653p:plain

補足)

 ×2^nは固定小数と同じ考え方となります。

 例えば

  2.12345の固定小数(小数部8bit)を求める場合、以下の式で求めます。

  f:id:nao-milk:20210330214931p:plain

  小数部8bitの固定小数を求める場合、2.12345×256=543.6032となり、

  この整数部の下位8bitが固定小数の小数点以下8bitとなります。

  (543=0x21F → 固定小数を2進数で示すと「10.00011111」となります。)

 

結局のところ、演算式②は固定小数点演算しているのと同じになります。