У меня есть серия из 100 целочисленных значений, которые мне нужно уменьшить / подвыбрать до 77 значений для того, чтобы вписаться в предопределенное пространство на экране. Это дает долю 77/100 значений на пиксель — не очень аккуратно.
Предполагая, что 77 является фиксированным и не может быть изменено, каковы некоторые типичные методы для подвыборки 100 чисел до 77. Я чувствую, что это будет зубчатое отображение, под которым я подразумеваю, что первое новое значение — это среднее значение [0, 1], затем следующее значение [3], затем среднее [4, 5] и т. Д. Но как мне подойти к получению шаблона для этого отображения?
Я работаю в C ++, хотя меня больше интересует техника, чем реализация.
Заранее спасибо.
Существуют разные способы интерполяции (см. википедия)
Линейный будет что-то вроде:
std::array<int, 77> sampling(const std::array<int, 100>& a)
{
std::array<int, 77> res;
for (int i = 0; i != 76; ++i) {
int index = i * 99 / 76;
int p = i * 99 % 76;
res[i] = ((p * a[index + 1]) + ((76 - p) * a[index])) / 76;
}
res[76] = a[99]; // done outside of loop to avoid out of bound access (0 * a[100])
return res;
}
Либо, если вы уменьшаете или повышаете частоту, вы пытаетесь восстановить сигнал по несэмплированным моментам времени … поэтому вы должны сделать некоторые предположения.
Теорема о сэмплировании говорит вам, что если вы сэмплируете сигнал, зная, что он не имеет частотных составляющих более половины частоты сэмплирования, вы можете непрерывно и полностью восстанавливать сигнал в течение всего периода времени. Есть способ восстановить сигнал, используя sinc()
функции (это sin(x)/x
)
sinc()
(в самом деле sin(M_PI/Sampling_period*x)/M_PI/x
) — это функция, которая имеет следующие свойства:
x == 0.0
и 0 для x == k*Sampling_period
с k == 0, +-1, +-2, ...
Sampling_period
,Итак, если вы считаете сумму функций F_x(x) = Y[k]*sinc(x/Sampling_period - k)
быть функцией sinc, которая равна значению выборки в позиции k
и 0 при другом значении выборки и сумме по всем k в вашей выборке, вы получите наилучшую непрерывную функцию, которая имеет свойства отсутствия компонентов на частотах, превышающих половину частоты выборки, и имеет те же значения, что и набор выборок.
Сказав это, вы можете пересчитать эту функцию в любое удобное для вас положение, получая лучший способ пересчитать ваши данные.
Это, безусловно, сложный способ повторной выборки данных (у него также есть проблема отсутствия причинно-следственной связи, поэтому его нельзя реализовать в реальном времени), и в прошлом у вас было несколько методов, используемых для упрощения интерполяции. Вы должны построить все функции sinc для каждой точки выборки и сложить их вместе. Затем вы должны повторно сэмплировать результирующую функцию к новым точкам выборки и дать это в результате.
Далее приведен пример только что описанного метода интерполяции. Он принимает некоторые входные данные (in_sz
выборки) и вывод интерполированных данных с помощью метода, описанного ранее (я предположил, что экстремумы совпадают, что делает N+1
образцы равны N+1
образцы, и это делает несколько запутанные вычисления (in_sz - 1)/(out_sz - 1)
в коде (изменить на in_sz/out_sz
если вы хотите разъяснить N samples -> M samples
преобразование:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
/* normalized sinc function */
double sinc(double x)
{
x *= M_PI;
if (x == 0.0) return 1.0;
return sin(x)/x;
} /* sinc */
/* interpolate a function made of in samples at point x */
double sinc_approx(double in[], size_t in_sz, double x)
{
int i;
double res = 0.0;
for (i = 0; i < in_sz; i++)
res += in[i] * sinc(x - i);
return res;
} /* sinc_approx */
/* do the actual resampling. Change (in_sz - 1)/(out_sz - 1) if you
* don't want the initial and final samples coincide, as is done here.
*/
void resample_sinc(
double in[],
size_t in_sz,
double out[],
size_t out_sz)
{
int i;
double dx = (double) (in_sz-1) / (out_sz-1);
for (i = 0; i < out_sz; i++)
out[i] = sinc_approx(in, in_sz, i*dx);
}
/* test case */
int main()
{
double in[] = {
0.0, 1.0, 0.5, 0.2, 0.1, 0.0,
};
const size_t in_sz = sizeof in / sizeof in[0];
const size_t out_sz = 5;
double out[out_sz];
int i;
for (i = 0; i < in_sz; i++)
printf("in[%d] = %.6f\n", i, in[i]);
resample_sinc(in, in_sz, out, out_sz);
for (i = 0; i < out_sz; i++)
printf("out[%.6f] = %.6f\n", (double) i * (in_sz-1)/(out_sz-1), out[i]);
return EXIT_SUCCESS;
} /* main */
Создайте 77 новых пикселей на основе средневзвешенного значения их позиций.
В качестве игрушечного примера подумайте о 3-пиксельном регистре, который вы хотите добавить к 2.
Оригинал (обозначим как многомерный массив original
с RGB как [0, 1, 2]):
|----|----|----|
Подвыборка (обозначим как многомерный массив subsample
с RGB как [0, 1, 2]):
|------|------|
Здесь интуитивно понятно, что первый подвыбор выглядит как 2/3 первого исходного пикселя и 1/3 следующего.
Для первого пикселя подобразца subsample[0]
Вы делаете это RGB среднее значение m
оригинальные пиксели, которые перекрываются, в этом случае original[0] and original[1]
, Но мы делаем это взвешенно.
subsample[0][0] = original[0][0] * 2/3 + original[1][0] * 1/3 # for red
subsample[0][1] = original[0][1] * 2/3 + original[1][1] * 1/3 # for green
subsample[0][2] = original[0][2] * 2/3 + original[1][2] * 1/3 # for blue
В этом примере original[1][2]
зеленый компонент второго исходного пикселя.
Имейте в виду, что для различных подвыборок вам нужно будет определить набор исходных ячеек, которые вносят вклад в подвыборку, а затем провести нормализацию, чтобы найти относительные веса каждой из них.
Существуют гораздо более сложные графические приемы, но этот прост и работает.
Все зависит от того, что вы хотите сделать с данными — как вы хотите их визуализировать.
Очень простой подход заключается в рендеринге в изображение шириной 100, а затем плавное масштабирование изображения до более узкого размера. Какую бы графику / среду разработки вы не использовали, она наверняка будет поддерживать такую операцию.
Скажем, однако, что ваша цель может заключаться в сохранении определенных качеств данных, таких как минимумы и максимумы. В таком случае для каждой ячейки вы рисуете линию более темного цвета до минимального значения, а затем продолжаете более светлым до максимального значения. Или вы можете вместо того, чтобы просто поместить пиксель в среднее значение, нарисовать линию от минимума до максимума.
Наконец, вы можете захотеть сделать так, как если бы у вас было только 77 значений — тогда цель состоит в том, чтобы каким-то образом преобразовать 100 значений в 77. Это будет означать некоторую интерполяцию. Линейная или квадратичная интерполяция проста, но добавляет искажения к сигналу. В идеале, вы, вероятно, захотите добавить в эту задачу интерполятор sinc. Хороший список их можно найти Вот. Для теоретического фона, посмотрите Вот.