我一直试图为内存位图编写双三次大小调整算法.我熟悉双三次插值的工作原理,我使用了维基百科文章和现有实现作为编写自己版本的指南。
以下是我的简单实现。在这里,bmap是一个包含位图数据的vector,而get_subpixel只是一个函数,它将位图看作由X x Y x Channel像素组成的三维数组,并在指定的坐标处返回单个子像素。
std::vector<unsigned char> bicubic_resize(
std::vector<unsigned char>& bmap, std::size_t bmap_width, std::size_t bmap_height,
std::size_t channels, std::size_t dest_width, std::size_t dest_height)
{
std::vector<unsigned char> out(dest_width * dest_height * 3);
const double tx = double(bmap_width) / dest_width;
const double ty = double(bmap_height) / dest_height;
const std::size_t row_stride = dest_width * channels;
unsigned char C[5] = { 0 };
for (unsigned i = 0; i < dest_height; ++i)
{
for (unsigned j = 0; j < dest_width; ++j)
{
const int x = int(tx * j);
const int y = int(ty * i);
const double dx = tx * j - x;
const double dy = ty * i - y;
for (int k = 0; k < 3; ++k)
{
for (int jj = 0; jj < 4; ++jj)
{
const int idx = y - 1 + jj;
unsigned char a0 = get_subpixel(bmap, idx, x, k);
unsigned char d0 = get_subpixel(bmap, idx, x - 1, k) - a0;
unsigned char d2 = get_subpixel(bmap, idx, x + 1, k) - a0;
unsigned char d3 = get_subpixel(bmap, idx, x + 2, k) - a0;
unsigned char a1 = -1.0 / 3 * d0 + d2 - 1.0 / 6 * d3;
unsigned char a2 = 1.0 / 2 * d0 + 1.0 / 2 * d2;
unsigned char a3 = -1.0 / 6 * d0 - 1.0 / 2 * d2 + 1.0 / 6 * d3;
C[jj] = a0 + a1 * dx + a2 * dx * dx + a3 * dx * dx * dx;
d0 = C[0] - C[1];
d2 = C[2] - C[1];
d3 = C[3] - C[1];
a0 = C[1];
a1 = -1.0 / 3 * d0 + d2 -1.0 / 6 * d3;
a2 = 1.0 / 2 * d0 + 1.0 / 2 * d2;
a3 = -1.0 / 6 * d0 - 1.0 / 2 * d2 + 1.0 / 6 * d3;
out[i * row_stride + j * channels + k] = a0 + a1 * dy + a2 * dy * dy + a3 * dy * dy * dy;
}
}
}
}
return out;
}此代码对于特定的目标大小非常有效。例如,如果原始位图为500 X 366,,目标大小为250 x 183,,则该算法工作得很好:
原版:

调整尺寸的

但是,对于某些其他目标大小(如100 x 73,),目标图像会被扭曲:

我一直在检查插值代码,我看不出我做错了什么。
如有任何提示、建议或答案,我将不胜感激。
发布于 2013-07-14 18:37:42
除了混合浮点和整数算法之外,我怀疑您正在得到一些中间值的数字溢出/下溢。
一个简单的解决方法就是保持一致性,并在整个过程中使用浮点。现在你有:
unsigned char C[5] = { 0 };
for (unsigned i = 0; i < dest_height; ++i)
{
for (unsigned j = 0; j < dest_width; ++j)
{
const int x = int(tx * j);
const int y = int(ty * i);
const double dx = tx * j - x;
const double dy = ty * i - y;
for (int k = 0; k < 3; ++k)
{
for (int jj = 0; jj < 4; ++jj)
{
const int idx = y - 1 + jj;
unsigned char a0 = get_subpixel(bmap, idx, x, k);
unsigned char d0 = get_subpixel(bmap, idx, x - 1, k) - a0;
unsigned char d2 = get_subpixel(bmap, idx, x + 1, k) - a0;
unsigned char d3 = get_subpixel(bmap, idx, x + 2, k) - a0;
unsigned char a1 = -1.0 / 3 * d0 + d2 - 1.0 / 6 * d3;
unsigned char a2 = 1.0 / 2 * d0 + 1.0 / 2 * d2;
unsigned char a3 = -1.0 / 6 * d0 - 1.0 / 2 * d2 + 1.0 / 6 * d3;
C[jj] = a0 + a1 * dx + a2 * dx * dx + a3 * dx * dx * dx;
d0 = C[0] - C[1];
d2 = C[2] - C[1];
d3 = C[3] - C[1];
a0 = C[1];
a1 = -1.0 / 3 * d0 + d2 -1.0 / 6 * d3;
a2 = 1.0 / 2 * d0 + 1.0 / 2 * d2;
a3 = -1.0 / 6 * d0 - 1.0 / 2 * d2 + 1.0 / 6 * d3;
out[i * row_stride + j * channels + k] = a0 + a1 * dy + a2 * dy * dy + a3 * dy * dy * dy;
}
}
}
}你有unsigned char,int和double的混合物。这些1.0 / 3中的每一个都将8位数据转换为双精度浮点数,然后赋值将其截断。
相反,为什么不直接使用float呢?
float C[5] = { 0 };
for (unsigned i = 0; i < dest_height; ++i)
{
for (unsigned j = 0; j < dest_width; ++j)
{
const float x = float(tx * j);
const float y = float(ty * i);
const float dx = tx * j - x;
const float dy = ty * i - y;
for (int k = 0; k < 3; ++k)
{
for (int jj = 0; jj < 4; ++jj)
{
const int idx = y - 1 + jj;
float a0 = get_subpixel(bmap, idx, x, k);
float d0 = get_subpixel(bmap, idx, x - 1, k) - a0;
float d2 = get_subpixel(bmap, idx, x + 1, k) - a0;
float d3 = get_subpixel(bmap, idx, x + 2, k) - a0;
float a1 = -(1.0f / 3.0f) * d0 + d2 - (1.0f / 6.0f) * d3;
float a2 = 0.5f * d0 + 0.5f * d2;
float a3 = -(1.0f / 6.0f) * d0 - 0.5f * d2 + (1.0f / 6.0f) * d3;
C[jj] = a0 + a1 * dx + a2 * dx * dx + a3 * dx * dx * dx;
d0 = C[0] - C[1];
d2 = C[2] - C[1];
d3 = C[3] - C[1];
a0 = C[1];
a1 = -(1.0f / 3.0f) * d0 + d2 -(1.0f / 6.0f) * d3;
a2 = 0.5f * d0 + 0.5f * d2;
a3 = -(1.0f / 6.0f) * d0 - 0.5f * d2 + (1.0f / 6.0f) * d3;
out[i * row_stride + j * channels + k] = saturate( a0 + a1 * dy + a2 * dy * dy + a3 * dy * dy * dy );
}
}
}
}然后定义一个函数saturate来执行以下操作:
inline unsigned char saturate( float x )
{
return x > 255.0f ? 255
: x < 0.0f ? 0
: unsigned char(x);
}这将解决您的溢出问题,并为您提供更好的精度和可能更好的性能。
如果您需要进一步提高性能,那么您应该研究不动点算法。但就目前而言,我认为上述实施更好。
另外,还有一个想法:通过预计算dx * dx、dx * dx * dx等等,您应该能够获得更高的效率:
float C[5] = { 0 };
for (unsigned i = 0; i < dest_height; ++i)
{
for (unsigned j = 0; j < dest_width; ++j)
{
const float x = float(tx * j);
const float y = float(ty * i);
const float dx = tx * j - x, dx2 = dx * dx, dx3 = dx2 * dx;
const float dy = ty * i - y, dy2 = dy * dy, dy3 = dy2 * dy;
for (int k = 0; k < 3; ++k)
{
for (int jj = 0; jj < 4; ++jj)
{
const int idx = y - 1 + jj;
float a0 = get_subpixel(bmap, idx, x, k);
float d0 = get_subpixel(bmap, idx, x - 1, k) - a0;
float d2 = get_subpixel(bmap, idx, x + 1, k) - a0;
float d3 = get_subpixel(bmap, idx, x + 2, k) - a0;
float a1 = -(1.0f / 3.0f) * d0 + d2 - (1.0f / 6.0f) * d3;
float a2 = 0.5f * d0 + 0.5f * d2;
float a3 = -(1.0f / 6.0f) * d0 - 0.5f * d2 + (1.0f / 6.0f) * d3;
C[jj] = a0 + a1 * dx + a2 * dx2 + a3 * dx3;
d0 = C[0] - C[1];
d2 = C[2] - C[1];
d3 = C[3] - C[1];
a0 = C[1];
a1 = -(1.0f / 3.0f) * d0 + d2 -(1.0f / 6.0f) * d3;
a2 = 0.5f * d0 + 0.5f * d2;
a3 = -(1.0f / 6.0f) * d0 - 0.5f * d2 + (1.0f / 6.0f) * d3;
out[i * row_stride + j * channels + k] = saturate( a0 + a1 * dy + a2 * dy2 + a3 * dy3 );
}
}
}
}https://stackoverflow.com/questions/17640173
复制相似问题