首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >双立方体尺寸调整的实现

双立方体尺寸调整的实现
EN

Stack Overflow用户
提问于 2013-07-14 14:15:40
回答 1查看 5K关注 0票数 5

我一直试图为内存位图编写双三次大小调整算法.我熟悉双三次插值的工作原理,我使用了维基百科文章现有实现作为编写自己版本的指南。

以下是我的简单实现。在这里,bmap是一个包含位图数据的vector,而get_subpixel只是一个函数,它将位图看作由X x Y x Channel像素组成的三维数组,并在指定的坐标处返回单个子像素。

代码语言:javascript
复制
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,),目标图像会被扭曲:

我一直在检查插值代码,我看不出我做错了什么。

如有任何提示、建议或答案,我将不胜感激。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-07-14 18:37:42

除了混合浮点和整数算法之外,我怀疑您正在得到一些中间值的数字溢出/下溢。

一个简单的解决方法就是保持一致性,并在整个过程中使用浮点。现在你有:

代码语言:javascript
复制
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 charintdouble的混合物。这些1.0 / 3中的每一个都将8位数据转换为双精度浮点数,然后赋值将其截断。

相反,为什么不直接使用float呢?

代码语言:javascript
复制
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来执行以下操作:

代码语言:javascript
复制
inline unsigned char saturate( float x )
{
    return x > 255.0f ? 255
         : x < 0.0f   ? 0
         :              unsigned char(x);
}

这将解决您的溢出问题,并为您提供更好的精度和可能更好的性能。

如果您需要进一步提高性能,那么您应该研究不动点算法。但就目前而言,我认为上述实施更好。

另外,还有一个想法:通过预计算dx * dxdx * dx * dx等等,您应该能够获得更高的效率:

代码语言:javascript
复制
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 );
            }
        }
    }
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17640173

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档