Рендеринг сигнала в PHP - Как создать более сжатый рендер? - PullRequest
11 голосов
/ 03 января 2012

Я рендерим осциллограмму в 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:

На основе решения функции Alnitiks 'pow' теперь я генерирую сигналы, которые выглядят следующим образом: -

enter image description here

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

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

Я попытался увеличить значение 0,4, но результат все еще не соответствует ожиданиям.

РЕДАКТИРОВАТЬ 2:

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

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

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

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

Ответы [ 2 ]

3 голосов
/ 03 января 2012

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

Для входных значений x в диапазоне 0,0 -> 1,0 принимайте y = pow(x, n), когда n должно находиться в диапазоне 0,2 - 0,7 (выход).Просто выберите число, которое дает желаемую кривую.

Поскольку ваши значения находятся в диапазоне 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 в PБиблиотека HP.

3 голосов
/ 03 января 2012

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

У вас есть 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);

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

...