C++ 快速、高质量的像素插值,用于极端图像降尺度
在我的程序中,我将500px或更大的图像缩小到大约16px-32px的极限水平。源图像是用户指定的,因此我无法控制其大小。可以想象,很少有像素插值有效,结果不可避免地会出现严重的锯齿 我尝试过双线性、双三次和平方平均采样。平方平均采样实际上提供了最合适的结果,但它越小,采样半径就必须越大。因此,它变得相当慢-比其他插值方法慢 我还尝试了自适应平方平均采样,因此它越小,采样半径越大,而它越接近其原始大小,采样半径越小。然而,这会产生问题,我不相信这是最好的办法 所以问题是:什么样的像素插值推荐类型是快速的,并且在如此极端的降尺度水平上工作得很好 我不希望使用一个库,所以我需要一些我可以手工编写并且不太复杂的东西。我在C++中使用VS 2012。< /P> 下面是一些我按要求尝试过的示例代码(希望我的伪代码剪切粘贴没有错误)。这将执行7x7的平均缩小,虽然它的结果比双线性或双三次插值更好,但它也需要相当大的冲击:C++ 快速、高质量的像素插值,用于极端图像降尺度,c++,image-processing,visual-studio-2012,interpolation,C++,Image Processing,Visual Studio 2012,Interpolation,在我的程序中,我将500px或更大的图像缩小到大约16px-32px的极限水平。源图像是用户指定的,因此我无法控制其大小。可以想象,很少有像素插值有效,结果不可避免地会出现严重的锯齿 我尝试过双线性、双三次和平方平均采样。平方平均采样实际上提供了最合适的结果,但它越小,采样半径就必须越大。因此,它变得相当慢-比其他插值方法慢 我还尝试了自适应平方平均采样,因此它越小,采样半径越大,而它越接近其原始大小,采样半径越小。然而,这会产生问题,我不相信这是最好的办法 所以问题是:什么样的像素插值推荐类型
// Sizing control
ctl(0): "Resize",Range=(0,800),Val=100
// Variables
float fracx,fracy;
int Xnew,Ynew,p,q,Calc;
int x,y,p1,q1,i,j;
//New image dimensions
Xnew=image->width*ctl(0)/100;
Ynew=image->height*ctl(0)/100;
for (y=0; y<image->height; y++){ // rows
for (x=0; x<image->width; x++){ // columns
p1=(int)x*image->width/Xnew;
q1=(int)y*image->height/Ynew;
for (z=0; z<3; z++){ // channels
for (i=-3;i<=3;i++) {
for (j=-3;j<=3;j++) {
Calc += (int)(src(p1-i,q1-j,z));
} //j
} //i
Calc /= 49;
pset(x, y, z, Calc);
} // channels
} // columns
} // rows
//大小控制
ctl(0):“调整大小”,范围=(0800),值=100
//变数
浮动压裂,压裂;
int Xnew、Ynew、p、q、Calc;
int x,y,p1,q1,i,j;
//新的图像维度
Xnew=图像->宽度*ctl(0)/100;
Ynew=图像->高度*ctl(0)/100;
对于(y=0;yheight;y++){//行
对于(x=0;xwidth;x++){//列
p1=(int)x*图像->宽度/Xnew;
q1=(int)y*图像->高度/Ynew;
对于(z=0;z来说,第一点是使用指向数据的指针。永远不要在每个像素处使用索引。当您写入时:src(p1-i,q1-j,z)
或pset(x,y,z,Calc)
进行了多少计算?使用指向数据的指针并对其进行操作
第二:你的算法是错误的。你不想要一个平均值过滤器,但是你想要在你的源图像上做一个网格,然后为每个网格单元计算平均值并将其放入输出图像的相应像素中
具体的解决方案应该根据您的数据表示进行定制,但可以是这样的:
std::vector<uint32_t> accum(Xnew);
std::vector<uint32_t> count(Xnew);
uint32_t *paccum, *pcount;
uint8_t* pin = /*pointer to input data*/;
uint8_t* pout = /*pointer to output data*/;
for (int dr = 0, sr = 0, w = image->width, h = image->height; sr < h; ++dr) {
memset(paccum = accum.data(), 0, Xnew*4);
memset(pcount = count.data(), 0, Xnew*4);
while (sr * Ynew / h == dr) {
paccum = accum.data();
pcount = count.data();
for (int dc = 0, sc = 0; sc < w; ++sc) {
*paccum += *i;
*pcount += 1;
++pin;
if (sc * Xnew / w > dc) {
++dc;
++paccum;
++pcount;
}
}
sr++;
}
std::transform(begin(accum), end(accum), begin(count), pout, std::divides<uint32_t>());
pout += Xnew;
}
std::vector accum(Xnew);
std::病媒计数(Xnew);
uint32*paccum,*pcount;
uint8_t*引脚=/*指向输入数据的指针*/;
uint8_t*pout=/*指向输出数据的指针*/;
对于(int-dr=0,sr=0,w=image->width,h=image->height;srdc){
++dc;
++薄荷脑;
++pcount;
}
}
sr++;
}
std::transform(begin(acum)、end(acum)、begin(count)、pout、std::divides());
pout+=Xnew;
}
这是使用我自己的库(仍在开发中)编写的,它似乎可以工作,但后来我更改了变量名称以使其更简单,所以我不保证任何事情
其思想是有一个32位整数的本地缓冲区,可以保存输出图像中一行中所有像素的部分和。然后除以单元格计数,将输出保存到最终图像
你应该做的第一件事是建立一个绩效评估系统,以衡量任何变化对绩效的影响程度。如前所述,你不应该使用指标,而应该使用(可能)大量变化的指针
加速&不是简单的平均,因为像素的基本平均基本上是一个模糊过滤器
我强烈建议您重新编写代码,使用“内核”。这是表示所用每个像素比率的矩阵。这样,您就可以测试不同的策略并优化质量
内核示例:
上采样/下采样内核:
注意,从代码来看,您似乎应用了3x3内核,但最初是在7x7内核上完成的。发布的等效3x3内核是:
[1 1 1]
[1 1 1] * 1/9
[1 1 1]
您需要进行平均化,通常情况下,采样而不进行平均化的算法不适用于缩小图像的尺寸。在正方形邻域中对像素进行平均化是最便宜的解决方案,虽然不理想,但总体上是可行的。您确实需要小心地实施它以避免缓存未命中。按存储顺序运行输入图像,并累积像素l值在输出图像的正确位置。我不明白如果你打算采样,你打算如何使用双线性或双三次插值。正如Cris Luengo所说,你只需要对输入图像进行一次遍历即可获得输出,因此像素数的复杂性是线性的。给我们一些代码,以便我们可以提出可能的改进建议。I“我已经按照要求添加了示例代码。您自己这么做有什么具体原因吗?我想这在很多图像处理库中都是一个已解决的问题?主要原因是了解幕后的情况,第二个原因是因为我从来没有找到一个库来轻松工作并满足我的需要。我会的。”我更喜欢手动操作。