色の値をfloatからbyteに変換する正しい方法は何でしょうか?最初はb=f*255.0
で実行できると思っていましたが、今は1.0
だけが255
に変換されますが、0.9999
はすでに変換されると思っています254
になります。これはおそらく私が望んでいることではありません。
b=f*256.0
は、正確な256
の場合に1.0
を作成するという望ましくない場合があることを除いて、より優れているようです。
結局、私はこれを使用しています:
#define F2B(f) ((f) >= 1.0 ? 255 : (int)((f)*256.0))
うまくいかない可能性があるのは1.0だけなので、そのケースは個別に処理してください。
b = floor(f >= 1.0 ? 255 : f * 256.0)
また、丸め誤差(f = 1.0000001など)による誤った動作を回避するために、fが実際に0 <= f <= 1であることを強制する価値がある場合があります。
f2 = max(0.0, min(1.0, f))
b = floor(f2 == 1.0 ? 255 : f2 * 256.0)
代替の安全なソリューション:
b = (f >= 1.0 ? 255 : (f <= 0.0 ? 0 : (int)floor(f * 256.0)))
または
b = max(0, min(255, (int)floor(f * 256.0)))
私はいつもround(f * 255.0)
をしました。
テスト(1の特殊なケース)や他の回答のクランプは必要ありません。これがあなたの目的にとって望ましい答えであるかどうかは、あなたの目標が入力値にできるだけ一致することであるか[私の式]、または各コンポーネントを256の等しい間隔に分割することであるか[他の式]に依存します。
私の数式の考えられる欠点は、0と255の間隔が他の間隔の半分の幅しかないことです。何年にもわたって使用されてきましたが、それが悪いという視覚的な証拠はまだ見ていません。それどころか、入力が非常に近くなるまで、どちらの極値もヒットしない方が好ましいことがわかりましたが、それは好みの問題です。
考えられる利点は、R-G-Bコンポーネントのrelative値が、より広い範囲の入力値に対して(わずかに)より正確であるということです。
これを証明しようとはしていませんが、各コンポーネントについて最も近い利用可能な整数を取得するために丸めることを考えると、それは私の直感です。 (たとえば、色のG〜 = 2 x Rの場合、この式はその比率に近いままであることが多いと思います。ただし、差は非常に小さく、_256
_式の方が優れている色は他にもたくさんあります。だから、それは洗浄かもしれません。)
実際には、_256
_または_255
_ベースのアプローチのいずれかが良い結果をもたらすようです。
評価する別の方法 _255
_対_256
_は、other方向を調べることです-
0..255バイトから0.0..1.0フロートへの変換。
0..255の整数値を0.0..1.0の範囲の等間隔の値に変換する式は次のとおりです。
_f = b / 255.0
_
この方向に進むと、_255
_と_256
_のどちらを使用するかについては疑問の余地がありません。上記の式はの式です。等間隔の結果が得られます。 _255
_を使用していることを確認してください。
2方向の_255
_式の関係を理解するために、2ビットしかない場合、つまり整数値0..3の場合は、この図を検討してください。
2ビットに_3
_を使用した図。8ビットに_255
_を使用した場合と同様です。上から下、または下から上に変換できます。
_0 --|-- 1 --|-- 2 --|-- 3
0 --|--1/3--|--2/3--|-- 0
1/6 1/2 5/6
_
_|
_は、4つの範囲の境界です。内部では、float値と整数値がそれらの範囲の中間点にあることに注意してください。すべての値の間のspacingが両方の表現で一定であることに注意してください。
これらの図を理解すれば、私が_255
_ベースの式よりも_256
_ベースの式を好む理由を理解できます。
Claim:fromバイトをフロートするときに_/ 255.0
_を使用するが、round(f * 255.0)
floatからtoバイトに移動すると、「平均ラウンドトリップ」エラーが増加します。詳細は以下の通りです。
これは、フロートから開始してバイトに進み、次にフロートに戻ることで最も簡単に測定できます。簡単な分析には、2ビットの「0..3」図を使用します。
0.0から1.0まで等間隔に配置された多数のfloat値から始めます。ラウンドトリップでは、これらすべての値が_4
_値にグループ化されます。
この図には、6つの半区間長の範囲があります。
0..1/6、1/6..1/3、..、5/6..1
各範囲の平均往復エラーは範囲の半分であるため、_1/12
_(最小エラーはゼロ、最大エラーは1/6、均等に分散)。
すべての範囲で同じエラーが発生します。 _1/12
_は、往復時の全体的な平均誤差です。
代わりに_* 256
_または_* 255.999
_式のいずれかを使用する場合、往復結果のmostは同じですが、いくつかは隣接する範囲に移動されます。
別の範囲に変更を加えると、エラーが増加します;たとえば、以前の単一のフロート入力のエラーが1/6よりわずかに少ない場合、隣接する範囲の中心を返すと、わずかにエラーが発生します1/6より多い。例えば。最適な式で0.18 =>バイト1 => float 1/3〜 = 0.333、エラーの場合| _0.33-0.18|
_ = _0.147
_; _256
_式=>バイト0 =>フロート0を使用して、エラー_0.18
_に対して、最適なエラー_0.147
_からの増加です。
_* 4
_と_/ 3
_を使用した図。変換は、ある行から次の行へです。
最初の行の間隔が不均一であることに注意してください:0..3/8、3/8..5/8、5 /8..1。それらの距離は3/8、2/8、3/8です。最後の行の間隔境界が最初の行とは異なることに注意してください。
_ 0------|--3/8--|--5/8--|------0
1/4 1/2 3/4
=> 0------|-- 1 --|-- 2 --|------3
=> 0----|---1/3---|---2/3---|----0
1/6 1/2 5/6
_
この増加したエラーを回避する唯一の方法は、バイトからフロートに移行するときにいくつかの異なる式を使用することです。 _256
_の数式の1つを強く信じている場合は、最適な逆数式を決定するのはあなたにお任せします。
(バイト値ごとに、そのバイト値になったfloat値の中点を返す必要があります。0から0、および3から1を除きます。または、おそらく0から1/8、3から7/8!上の図では、真ん中の行から一番上の行に戻るはずです。)
しかし、これで、等間隔のバイト値を取得し、それらを等間隔ではない浮動小数点値に変換したという、防御が難しい状況になります。
整数0..255に対して、正確に_255
_以外の値を使用する場合のオプションです:平均ラウンドトリップエラーの増加、またはフロートドメインの不均一な間隔の値。
次のようなものを試してみませんか
b=f*255.999
特殊なケースを取り除きますf==1
しかし、0.999はまだ255です
正確に等しいサイズのチャンクが必要な場合は、次の方法が最適です。 [0,1]
の範囲を[0,256[
に変換します。
#include <cstdint>
#include <limits>
// Greatest double predecessor of 256:
constexpr double MAXCOLOR = 256.0 - std::numeric_limits<double>::epsilon() * 128;
inline uint32_t float_to_int_color(const double color){
return static_cast<uint32_t>(color * MAXCOLOR);
}
EDIT:明確にするために、なぜepsilon(1.0)* 256.0ではなくepsilon(1.0)* 128が使用されるのか: cpp standard =マシンイプシロンを次のように指定します
1.0と浮動小数点型Tで表される次の値との差。
256.0は8の指数と1.0の仮数で表されるため、イプシロン(256.0)は、指数が7になる前の数値を取得するには大きすぎます。例:
0 10000000111 0000000000000000000000000000000000000000000000000000 256.0
- 0 11110100110 0000000000000000000000000000000000000000000000000000 eps(256.0)
_____________________________________________________________________
= 0 10000000110 1111111111111111111111111111111111111111111111111110
これは次のようになります。
_____________________________________________________________________
= 0 10000000110 1111111111111111111111111111111111111111111111111111
浮動小数点を整数として比較すると、受け入れられたソリューションは失敗しました。
このコードは問題なく機能します。
float f;
uint8_t i;
//byte to float
f =CLAMP(((float)((i &0x0000ff))) /255.0, 0.0, 1.0);
//float to byte
i =((uint8_t)(255.0f *CLAMP(f, 0.0, 1.0)));
cLAMPがない場合:
#define CLAMP(value, min, max) (((value) >(max)) ? (max) : (((value) <(min)) ? (min) : (value)))
またはフルRGBの場合:
integer_color =((uint8_t)(255.0f *CLAMP(float_color.r, 0.0, 1.0)) <<16) |
((uint8_t)(255.0f *CLAMP(float_color.g, 0.0, 1.0)) <<8) |
((uint8_t)(255.0f *CLAMP(float_color.b, 0.0, 1.0))) & 0xffffff;
float_color.r =CLAMP(((float)((integer_color &0xff0000) >>16)) /255.0, 0.0, 1.0);
float_color.g =CLAMP(((float)((integer_color &0x00ff00) >>8)) /255.0, 0.0, 1.0);
float_color.b =CLAMP(((float)((integer_color &0x0000ff))) /255.0, 0.0, 1.0);
public static void floatToByte(float f)
{
return (byte)(f * 255 % 256)
}
1未満の値は正確に変換されます。
変換後、255から256の間にある値は、バイトに変換されると255にフロアされます。
値> 1は、%
演算子を使用して0にループバックされます。
正しいのは丸ではなく、floor(f * 256)だと思います。これにより、間隔0..1が同じ長さの正確に256のゾーンにマップされます。
[編集]そして特別な場合として256をチェックしてください。