11

lameエンコーダーでダウンサンプリングし、結果のデータポイントから波形を描画することにより、PHPで波形をレンダリングしています。現在、次のような画像を取得しています。

ここに画像の説明を入力

私がやりたいことは、波形の見かけのダイナミック レンジが本質的に「圧縮」されるようにコードを変更することです。このような波形を生成するには:

ここに画像の説明を入力

各データポイントの高さをレンダリングするために現在使用している式は次のとおりです。

 // draw this data point
          // relative value based on height of image being generated
          // data values can range between 0 and 255
           $v = (int) ( $data / 255 * $height );


          // don't print flat values on the canvas if not necessary
          if (!($v / $height == 0.5 && !$draw_flat))
            // draw the line on the image using the $v value and centering it vertically on the canvas
            imageline(
              $img,
              // x1
              (int) ($data_point / DETAIL),
              // y1: height of the image minus $v as a percentage of the height for the wave amplitude
              $height * $wav - $v,
              // x2
              (int) ($data_point / DETAIL),
              // y2: same as y1, but from the bottom of the image
              $height * $wav - ($height - $v),
              imagecolorallocate($img, $r, $g, $b)
            );      

実際の振幅は、このコードの最初の行で定義されています:-

  $v = (int) ( $data / 255 * $height );

残念ながら、私の数学のスキルはせいぜい貧弱です。私がする必要があるのは、本質的に $v の値に「曲線」を適用することです。これにより、方程式に入力された数値が小さい場合、結果の出力が高くなり、入力数値が増加するにつれて、最終的に入力が 255 に達すると、出力も 255 になるはずです。また、曲線は、入力が 0 の場合に出力も 0 になるようにする必要があります。

これが明確でない場合は申し訳ありませんが、数学の経験が限られているため、この質問を明確にするのは非常に難しいと思います.

おそらく、視覚的な表現が私の意図を説明するのに役立つでしょう:-

ここに画像の説明を入力

$v の値が 0 または 255 の場合、式の出力は正確に入力 (0 または 255) になるはずです。ただし、入力が中間の値である場合は、上記の曲線の結果の出力に従う必要があります。(上記は説明のための大まかな図です。)

編集:

Alnitiks の「pow」関数ソリューションに基づいて、次のような波形を生成しています。

ここに画像の説明を入力

$v 変数の置換式を次のように使用します。

 $v = pow($data / 255.0, 0.4) * $height;

0.4 の値を上げてみましたが、結果はまだ意図したとおりではありません。

編集2:

ここで要求されているのは、私の $data 変数の生データダンプです。

生データ

これは、波形の描画に使用される前に $v を返す方程式に渡されます (上記の元のコードで変数 $v に何を行っているかがわかります。$height は単純に、画像に設定した高さのピクセル数です)。レンダリングします。

このデータは、カンマ区切りの値のリストです。これが役立つことを願っています。平均値が 128 であるというあなたの主張は正しいようです。これまでのところ、これに対するあなたの修正について頭を悩ませることができませんでした. 残念ながら、それは私の現在の理解を少し超えています。

4

3 に答える 3

3

ガンマ補正に似たものが必要です。

0.0 -> 1.0 の範囲の入力値 x のy = pow(x, n)場合n、0.2 - 0.7 (ish) の範囲にある必要があります。目的の曲線を与える数値を選択するだけです。

値が 0 -> 255 の範囲にあるため、255.0 で割り、pow関数を適用してから、再度 255 を掛ける必要があります。

$y = 255 * pow($x / 255.0, 0.4);

このpow式は、0 と 1 がそれ​​自体にマッピングされるという基準を満たし、小さい値は大きい値よりも「増幅」されます。

これは、n = 1 / 1.6、1 / 2、1 / 2.4、および 1 / 2.8 のガンマ曲線とsin曲線 (赤) を比較したグラフです。

ガンマ曲線と正弦

の値がn小さいほど低域に「圧縮」がかかるため、水色の線は n = 1 / 2.8 の線です。

sin曲線が 0 から 0.5 の範囲でほぼ線形であるため、ローエンドの圧縮がほとんどないことに注意してください。

あなたの値が実際に128を中心にしていると思われる場合は、式を多少変更する必要があります。

$v = ($x - 128.0) / 128.0;
$y = 128 + 127 * sign($v) * pow(abs($v), 0.4);

ただし、PHP 開発者はsignPHP ライブラリに関数を含めていません。

于 2012-01-03T15:19:59.067 に答える
3

数学のスキルがない場合 (そして、迅速な表示があればおそらく便利です):

256 の可能な値があります。次の各値の「動的」値を含む配列を作成します。

$dynamic = array(
   0 => 0,
   1 => 2,
   ...
);

これで、動的な値を簡単に取得できます。

$v = (int) ($dynamic[(int) $data / 255] * $height);

多少精度が落ちるかもしれませんが、おそらく役に立ちます。


自然な動的値は、数学のサイン関数とコサイン関数によって生成されます。PHP では、このsinドキュメント(およびそこにリンクされている他のドキュメント) で使用されます。

ループとその関数を使用して、配列を事前に入力し、配列を再利用して、事前に計算された値を取得できます。

$sine = function($v)
{
    return sin($v * 0.5 * M_PI);
};

$dynamic = array();
$base = 255;
for ($i = 0; $i <= $base; $i++)
{
    $dynamic[$i] = $i/$base;
}

$dynamic = array_map($sine, $dynamic);

ここでは変数関数を使用しているため、複数記述でき、どれがニーズに合っているかを簡単にテストできます。

于 2012-01-03T15:10:28.903 に答える