上采样:在向量的每个连续元素之间插入额外的值
Posted
技术标签:
【中文标题】上采样:在向量的每个连续元素之间插入额外的值【英文标题】:Upsampling: insert extra values between each consecutive elements of a vector 【发布时间】:2019-12-08 18:23:13 【问题描述】:假设我们有一个由 20 个浮点数组成的向量 V。是否可以在每对这些浮点数之间插入值,使向量 V 成为正好 50 个数字的向量。
插入的值应该是上下值之间的随机数我决定在两者之间插入两个值的中点。
我尝试了以下方法:
vector<double> upsample(vector<double>& in)
vector<double> temp;
for (int i = 1; i <= in.size() - 1 ; i++)
double sample = (in[i] + in[i - 1]) / 2;
temp.push_back(in[i - 1]);
temp.push_back(sample);
temp.push_back(in.back());
return temp;
使用此函数,输入向量元素增加 2(n) - 1(20 个元素变为 39 个)。输入向量的大小可能小于 50。
我认为可以通过在两个元素之间随机插入一个以上的值来获得一个大小为 50 的向量(例如在 V[0] 和 V[1] 之间插入 3 个值,在 V[3] 和 V[ 4]插入1个值等)。这可能吗?
您能指导我如何执行此操作吗? 谢谢。
【问题讨论】:
听起来您正在搜索确定在每个新值之前需要添加多少值的代码。您可以在比率的下限(所需长度/输入长度)及其细胞。您使用 floor 或 ceil 的频率也取决于比例。这将导致非常规则的插值。或者你想要一个随机的决定,让它变得不规则? 是否应该尽可能均匀地对数组进行上采样?即,如果在元素 n 和 n+1 之间添加了 5 个项目,但在 n+1 和 n+2 之间没有添加,是否有问题? @ypnos 例如,如果 V= 0.1,0.2,0.3 所需的输出可能类似于 V' =0.1,0.12,0.14,0.17,0.2,0.27,0.29,0.3跨度> 您还可以检查 Bresenham 的线算法(将几何线混叠到像素栅格上原则上是相同的混叠问题...您有 20 个输入值 = 假设“y 轴”并且您想要 50输出值=“x轴”,所以你正在画线(+50,+20),新线的每个起点都是输入的“副本”,剩下的一两个点是生成的新值。如果你想将新值尽可能均匀地分布在整个范围内。要理解的重要一点是,由于“混叠”,它仅适用于非常特定的值,因此首先决定如何混叠。 在数字信号处理中,一个标准的方法是,如果你想将信号从 X Hz 缩放到 Y Hz,那么做到这一点的方法是上采样到一个公倍数,然后再次向下抽取.在这种情况下,它将上采样到 100 Hz,并丢弃每个第二个样本。这样做的好处是您只能处理整数,无论您要上采样还是下采样,都是相同的过程。问题是,如果 to/from 很少有共同的主要因素(例如,49 Hz 到 50 Hz 需要上采样到 2450 Hz 作为中间值),它会变得非常低效。 【参考方案1】:所以我自己做了一些数学运算,因为我很好奇如何获得权重比(好像线性上采样到公倍数,然后只从大数组中提取目标值 - 但没有创建大数组,只使用权重知道左+右元素对特定值有多少贡献)。
示例代码总是通过简单的加权平均创建新值(即 123.4 的 40% 和 60% 的 567.8 将给出“升级”值 390.04),没有随机用于对升级后的值进行填充(将那部分留给 OP)。
比例如下:
如果大小为 M 的向量被放大到大小为 N (M N)( “upscale”将始终保留输入向量的第一个和最后一个元素,这些在本提案中是“固定的”)
那么每个放大的元素都可以被视为介于一些原始元素 [i, i+1] 之间的某个位置。
如果我们声明源元素[i,i+1]之间的“距离”等于d = N-1,那么放大后的元素之间的距离总是可以表示为一些j/d 其中 j:[0,d] (当 j 是实际的 d 时,它正好在 "i +1" 元素,可以认为与 j=0 相同,但具有 [i+1,i+2] 源元素)
那么两个放大后的元素之间的距离就是M-1。
所以当源向量的大小为 4 且放大后的向量大小应为 5 时,放大后的元素的比率为 [ [4/4,0/4], [1/4,3/4], [2/4 ,2/4], [3/4,1/4], [0/4,4/4] ] 元素(索引到向量) [ [0,1], [0,1], [1,2 ], [2, 3], [2, 3] ]。 (源元素之间的“距离”是 5-1=4,那就是标准化权重的“/4”,放大元素之间的“距离”是 4-1=3,这就是为什么比率移动 [-3,+ 3] 在每一步)。
恐怕我的描述远非“显而易见”(弄清楚后在我脑海中的感觉),但是如果您将其中的一些放入电子表格并玩弄它,希望它会有所作为感觉。或者,也许您可以调试代码,以便更好地感受上面的喃喃自语是如何转化为真实代码的。
代码示例 1,只有当权重完全完全覆盖源元素时,这个才会“复制”源元素(即在示例数据中,只有第一个和最后一个元素被“复制”,其余的放大元素是原始元素的加权平均值值)。
#include <iostream>
#include <vector>
#include <cassert>
static double get_upscale_value(const size_t total_weight, const size_t right_weight, const double left, const double right)
// do the simple weighted average for demonstration purposes
const size_t left_weight = total_weight - right_weight;
return (left * left_weight + right * right_weight) / total_weight;
std::vector<double> upsample_weighted(std::vector<double>& in, size_t n)
assert( 2 <= in.size() && in.size() <= n ); // this is really only upscaling (can't downscale)
// resulting vector variable
std::vector<double> upscaled;
upscaled.reserve(n);
// upscaling factors variables and constants
size_t index_left = 0; // first "left" item is the in[0] element
size_t weight_right = 0; // and "right" has zero weight (i.e. in[0] is copied)
const size_t in_weight = n - 1; // total weight of single "in" element
const size_t weight_add = in.size() - 1; // shift of weight between "upscaled" elements
while (upscaled.size() < n) // add N upscaled items
if (0 == weight_right)
// full weight of left -> just copy it (never tainted by "upscaling")
upscaled.push_back(in[index_left]);
else
// the weight is somewhere between "left" and "right" items of "in" vector
// i.e. weight = 1..(in_weight-1) ("in_weight" is full "right" value, never happens)
double upscaled_val = get_upscale_value(in_weight, weight_right, in[index_left], in[index_left+1]);
upscaled.push_back(upscaled_val);
weight_right += weight_add;
if (in_weight <= weight_right)
// the weight shifted so much that "right" is new "left"
++index_left;
weight_right -= in_weight;
return upscaled;
int main(int argc, const char *argv[])
std::vector<double> in 10, 20, 30 ;
// std::vector<double> in 20, 10, 40 ;
std::vector<double> upscaled = upsample_weighted(in, 14);
std::cout << "upsample_weighted from " << in.size() << " to " << upscaled.size() << ": ";
for (const auto i : upscaled)
std::cout << i << " ";
std::cout << std::endl;
return 0;
输出:
upsample_weighted 从 3 到 14:10 11.5385 13.0769 14.6154 16.1538 17.6923 19.2308 20.7692 22.3077 23.8462 25.3846 26.9231 28.4615 30
代码示例 2,此代码将“复制”每个源元素并使用加权平均来填补它们之间的空白,因此尽可能多地保留原始数据(因为结果的价格不是线性放大的原始数据集,但“别名”为由目标大小定义的“网格”):
(代码与第一个几乎相同,除了升频器中有一个if
行)
#include <iostream>
#include <vector>
#include <cassert>
static double get_upscale_value(const size_t total_weight, const size_t right_weight, const double left, const double right)
// do the simple weighted average for demonstration purposes
const size_t left_weight = total_weight - right_weight;
return (left * left_weight + right * right_weight) / total_weight;
// identical to "upsample_weighted", except all source values from "in" are copied into result
// and only extra added values (to make the target size) are generated by "get_upscale_value"
std::vector<double> upsample_copy_preferred(std::vector<double>& in, size_t n)
assert( 2 <= in.size() && in.size() <= n ); // this is really only upscaling (can't downscale)
// resulting vector variable
std::vector<double> upscaled;
upscaled.reserve(n);
// upscaling factors variables and constants
size_t index_left = 0; // first "left" item is the in[0] element
size_t weight_right = 0; // and "right" has zero weight (i.e. in[0] is copied)
const size_t in_weight = n - 1; // total weight of single "in" element
const size_t weight_add = in.size() - 1; // shift of weight between "upscaled" elements
while (upscaled.size() < n) // add N upscaled items
/* ! */ if (weight_right < weight_add) /* ! this line is modified */
// most of the weight on left -> copy it (don't taint it by upscaling)
upscaled.push_back(in[index_left]);
else
// the weight is somewhere between "left" and "right" items of "in" vector
// i.e. weight = 1..(in_weight-1) ("in_weight" is full "right" value, never happens)
double upscaled_val = get_upscale_value(in_weight, weight_right, in[index_left], in[index_left+1]);
upscaled.push_back(upscaled_val);
weight_right += weight_add;
if (in_weight <= weight_right)
// the weight shifted so much that "right" is new "left"
++index_left;
weight_right -= in_weight;
return upscaled;
int main(int argc, const char *argv[])
std::vector<double> in 10, 20, 30 ;
// std::vector<double> in 20, 10, 40 ;
std::vector<double> upscaled = upsample_copy_preferred(in, 14);
std::cout << "upsample_copy_preferred from " << in.size() << " to " << upscaled.size() << ": ";
for (const auto i : upscaled)
std::cout << i << " ";
std::cout << std::endl;
return 0;
输出:
upsample_copy_preferred 从 3 到 14:10 11.5385 13.0769 14.6154 16.1538 17.6923 19.2308 20 22.3077 23.8462 25.3846 26.9231 28.4615 30
(请注意,示例 1 中的“20.7692”在这里只是“20” - 原始样本的副本,即使此时如果考虑线性插值,“30”的权重也很小)
【讨论】:
非常感谢您的帮助。似乎加权上采样为我的程序提供了更准确的输出。但是我发现你的代码细节有点难以理解。 @student_11 在这里的 cmets 中询问特定部分...我尽量保持简单,如果这将是我的应用程序的“生产”代码,我会更改一些部分以获得更好的性能,但它会稍微难以阅读,所以它实际上应该读得很好? = 这让你对哪些部分难的反馈对我来说非常有价值,这对我来说总是很有趣,听听其他人如何看待代码,以及对他们来说什么是困难的。 :) 但主要原理与原始评论中提到的相同,Frodyne 写的只是我提供的代码的效率较低的变体,在考虑了他的评论后,我终于(经过几十年)意识到Bresenham 得到了他的线算法的数学,它实际上是相同的原理。当您有两个值 M,N 时,这两个原则都以 M*N(或优化为公倍数)作为“总步数”开始,因为这样的值可以被除以 M 和 N 步而不会保留。这就像一般的“刻度”值 [0..M*N],其他一切都与之相关。 感谢您宝贵的 cmets。我刚刚在我的项目中使用了您的代码(进行了一些修改),并且效果非常好。所以我还没有调试它。在调试并深入研究 Bresenham 算法之后。我会问我关于代码的真正问题:)。再次感谢您。以上是关于上采样:在向量的每个连续元素之间插入额外的值的主要内容,如果未能解决你的问题,请参考以下文章