Форма визуализации в PHP - Как создать более сжатый рендер?

Я обрабатываю форму волны в PHP, понижая ее с помощью хромого кодировщика, а затем вытягивая форму волны из результирующих точек данных. В настоящее время я получаю изображения следующим образом:

enter image description here

Что бы я хотел сделать, это изменить мой код, чтобы кажущийся динамический диапазон формы сигнала был по существу "сжат". Чтобы создать форму волны, которая выглядит более как это:

enter image description here

Уравнение, которое я использую для отображения высоты каждой точки данных, выглядит следующим образом: -

 // 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.

Я извиняюсь, если это не ясно, но я очень быстро нахожу этот вопрос, чтобы сформулировать его с ограниченным математическим опытом.

Возможно, визуальное представление поможет описать мое намерение: -

enter image description here

Когда значение $v равно либо 0, либо 255, выход уравнения должен быть точно входом (0 или 255). Однако, когда вход представляет собой значение между ними, он должен следовать результирующему выходу кривой выше. (приведенное выше было лишь приблизительным рисунком для иллюстрации.)

EDIT:

Основываясь на решении функции "pow" от Alnitiks, я теперь генерирую сигналы, которые выглядят так: -

enter image description here

Используя уравнение замены для переменной $v следующим образом: -

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

Я попытался повысить значение 0.4, но результат все еще не так, как предполагалось.

ИЗМЕНИТЬ 2:

В соответствии с запрошенным здесь представляет собой исходный datadump моей переменной $data:

Необработанные данные

Это передается в уравнение, чтобы возвращать $v перед тем, как его использовать для рисования формы волны (вы можете видеть, что я делаю с переменной $v в исходном коде, который я разместил выше. $height - просто число пикселей, высокое у меня есть установите изображение для рендеринга.

Эти данные представляют собой разделенный запятыми список значений. Надеюсь, это поможет. Похоже, ваше утверждение о том, что среднее значение равно 128, является правильным. До сих пор я не мог задуматься об этом. Я боюсь, что это немного превышает мое нынешнее понимание.

Ответ 1

Без математических навыков (и, вероятно, полезно иметь быстрый дисплей):

У вас есть 256 возможных значений. Создайте массив, который содержит "динамическое" значение для каждого из этих значений:

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

Это сделано, вы можете легко получить динамическое значение:

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

Вы можете потерять некоторую точность, но, вероятно, это полезно.


Естественные динамические значения генерируются математикой sine и косинусами, в PHP это sin & shy; Docs (и другие, связанные с ним).

Вы можете использовать цикл и эту функцию для предварительной заливки массива и повторного использования массива, чтобы у вас были предварительно вычисленные значения:

$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);

Я использую здесь функцию переменной, поэтому вы можете писать несколько и можете легко проверить, какой из них соответствует вашим потребностям.

Ответ 2

Вам нужно что-то подобное гамма-коррекции.

Для входных значений x в диапазоне 0.0 → 1.0 возьмите 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 (красным):

Gamma Curves vs 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 не включили функцию sign в библиотеку PHP.