代码之家  ›  专栏  ›  技术社区  ›  Casper Chang

GPUImage框架下的样条插值算法

  •  1
  • Casper Chang  · 技术社区  · 7 年前

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

    [编辑]

    如下文所述,我将样条计算代码粘贴在 GPUImageToneCurveFilter。m . 它是 目标-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那样的结果。

    1 回复  |  直到 7 年前
        1
  •  0
  •   Spektre    7 年前

    我不认识这种语言(除了 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 伯恩斯坦多项式 (用于 贝塞尔 样条曲线 ). 这个 a^3 b^3 建议使用三次曲线。您应该从绘制它生成的图形开始,然后尝试创建具有类似属性的多项式插值。如果结果与您找到的术语匹配,您将回答。有关更多信息,请参阅:

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