【问题标题】:Spline Interpolation Algorithm in GPUImage frameworkGPUImage框架中的样条插值算法
【发布时间】:2018-03-24 01:45:09
【问题描述】:

我最近使用 GPUImage 框架 (GPUImageToneCurveFilter) 来读取 Adob​​e acv 文件并生成着色器纹理的 LUT。在成功使用此实用程序在我的图像中渲染自定义色调后,我很好奇使用了什么样的样条插值算法来实现这一点。在示例代码中,我可以发现有一些二阶导数计算。但我不明白数学参数是从哪里来的。是否可以告诉我一些进一步插值研究的理论参考?谢谢。

[编辑]

正如下面提到的Spektre,我将样条计算的代码粘贴在GPUImageToneCurveFilter.m中。它是 Objective-C 版本。

从 ACV 文件中获取控制点并将其从 (0, 1) 转换为 (0, 255) 后,将点发送到 splineCurve 函数进行插值,如下所示:

NSMutableArray *splinePoints = [self splineCurve:convertedPoints];

两个插值函数为:

- (NSMutableArray *)splineCurve:(NSArray *)points
{
    NSMutableArray *sdA = [self secondDerivative:points];

    // [points count] is equal to [sdA count]
    NSInteger n = [sdA count];
    if (n < 1)
    {
        return nil;
    }
    double sd[n];

    // From NSMutableArray to sd[n];
    for (int i=0; i<n; i++) 
    {
        sd[i] = [[sdA objectAtIndex:i] doubleValue];
    }


    NSMutableArray *output = [NSMutableArray arrayWithCapacity:(n+1)];

    for(int i=0; i<n-1 ; i++) 
    {
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
        CGPoint cur = [[points objectAtIndex:i] CGPointValue];
        CGPoint next = [[points objectAtIndex:(i+1)] CGPointValue];
#else
        NSPoint cur = [[points objectAtIndex:i] pointValue];
        NSPoint next = [[points objectAtIndex:(i+1)] pointValue];
#endif

        for(int x=cur.x;x<(int)next.x;x++) 
        {
            double t = (double)(x-cur.x)/(next.x-cur.x);

            double a = 1-t;
            double b = t;
            double h = next.x-cur.x;

            double y= a*cur.y + b*next.y + (h*h/6)*( (a*a*a-a)*sd[i]+ (b*b*b-b)*sd[i+1] );

            if (y > 255.0)
            {
                y = 255.0;   
            }
            else if (y < 0.0)
            {
                y = 0.0;   
            }
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
            [output addObject:[NSValue valueWithCGPoint:CGPointMake(x, y)]];
#else
            [output addObject:[NSValue valueWithPoint:NSMakePoint(x, y)]];
#endif
        }
    }

    // The above always misses the last point because the last point is the last next, so we approach but don't equal it.
    [output addObject:[points lastObject]];
    return output;
}

- (NSMutableArray *)secondDerivative:(NSArray *)points
{
    const NSInteger n = [points count];
    if ((n <= 0) || (n == 1))
    {
        return nil;
    }

    double matrix[n][3];
    double result[n];
    matrix[0][1]=1;
    // What about matrix[0][1] and matrix[0][0]? Assuming 0 for now (Brad L.)
    matrix[0][0]=0;    
    matrix[0][2]=0;    

    for(int i=1;i<n-1;i++) 
    {
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
        CGPoint P1 = [[points objectAtIndex:(i-1)] CGPointValue];
        CGPoint P2 = [[points objectAtIndex:i] CGPointValue];
        CGPoint P3 = [[points objectAtIndex:(i+1)] CGPointValue];
#else
        NSPoint P1 = [[points objectAtIndex:(i-1)] pointValue];
        NSPoint P2 = [[points objectAtIndex:i] pointValue];
        NSPoint P3 = [[points objectAtIndex:(i+1)] pointValue];
#endif

        matrix[i][0]=(double)(P2.x-P1.x)/6;
        matrix[i][1]=(double)(P3.x-P1.x)/3;
        matrix[i][2]=(double)(P3.x-P2.x)/6;
        result[i]=(double)(P3.y-P2.y)/(P3.x-P2.x) - (double)(P2.y-P1.y)/(P2.x-P1.x);
    }

    // What about result[0] and result[n-1]? Assuming 0 for now (Brad L.)
    result[0] = 0;
    result[n-1] = 0;

    matrix[n-1][1]=1;
    // What about matrix[n-1][0] and matrix[n-1][2]? For now, assuming they are 0 (Brad L.)
    matrix[n-1][0]=0;
    matrix[n-1][2]=0;

    // solving pass1 (up->down)
    for(int i=1;i<n;i++) 
    {
        double k = matrix[i][0]/matrix[i-1][1];
        matrix[i][1] -= k*matrix[i-1][2];
        matrix[i][0] = 0;
        result[i] -= k*result[i-1];
    }
    // solving pass2 (down->up)
    for(NSInteger i=n-2;i>=0;i--)
    {
        double k = matrix[i][2]/matrix[i+1][1];
        matrix[i][1] -= k*matrix[i+1][0];
        matrix[i][2] = 0;
        result[i] -= k*result[i+1];
    }

    double y2[n];
    for(int i=0;i<n;i++) y2[i]=result[i]/matrix[i][1];

    NSMutableArray *output = [NSMutableArray arrayWithCapacity:n];
    for (int i=0;i<n;i++) 
    {
        [output addObject:[NSNumber numberWithDouble:y2[i]]];
    }

    return output;
}

希望这些解释可以帮助弄清楚 Brad L. 使用什么样的算法来插入产生 Photoshop 那样结果的曲线。

【问题讨论】:

  • 至少分享你所描述的链接......我们不知道你所写的方法,所以如果没有细节我们就无法提供帮助......
  • @Spektre,谢谢。我在帖子末尾附加了一些信息。

标签: algorithm interpolation gpuimage spline derivative


【解决方案1】:

我不认识这种语言(除了 C++ 之类的东西),所以(对我来说)很难阅读它,但我认为

double y= a*cur.y + b*next.y + (h*h/6)*( (a*a*a-a)*sd[i]+ (b*b*b-b)*sd[i+1] );

就是你要问的。术语:

a*cur.y + b*next.y

是基本的线性插值,所以当t=0.0 结果是当前点,如果t=1.0 结果是下一个点。其余的是某种具有特定参数的叠加曲线,可能会为按点距离缩放的梯度产生某种凹凸图案(这就是使用增量的原因)。扭转它会很困难(我太懒了),但热力学让我想起了 伯恩斯坦多项式(用于 BEZIERSPLINE >)。 a^3b^3 建议三次曲线。您应该从绘制它生成的图形开始,而不是尝试创建具有相似属性的多项式插值。如果结果与您找到的答案相符。有关更多信息,请参阅:

以及那里的所有链接(尤其是How to construct own interpolation 的链接)。

【讨论】:

  • 这些术语和解释正是我在这个领域开始学习所需要的。我在您提供的链接中阅读了其他文章,不仅是为了进一步的数学讨论,而且还在您的其他文章(例如使用 GDI、VCL、OpenGL 等)中学到了很多关于曲线和点渲染的知识。谢谢。
  • @CasperChang 很高兴为您提供帮助。一旦你完成了插值并将其用于图形或任何查看Understanding 4x4 homogenous transform matrices 的内容,它们通常用于将对象/曲线放置在 3D 空间中,并用于双向坐标转换,这对于 3D 图形至关重要。您可以将所有这些扩展到任意数量的维度,例如 4D rendering techniques4D rotations
猜你喜欢
  • 1970-01-01
  • 2012-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-29
  • 2018-02-12
  • 1970-01-01
相关资源
最近更新 更多