使用gpu着色器bug进行失真校正

时间:2014-09-16 14:27:50

标签: opengl glsl shader gpu distortion

所以我有一台带广角镜头的相机。我知道失真系数,焦距,光学中心。我想要反映我从这台相机获得的图像。我第一次使用OpenCV(cv :: undistort),效果很好,但速度太慢了。

现在我想在gpu上执行此操作。有一个着色器完成了http://willsteptoe.com/post/67401705548/ar-rift-aligning-tracking-and-video-spaces-part-5

中记录的内容

这里可以看到公式: http://en.wikipedia.org/wiki/Distortion_%28optics%29#Software_correction

所以我去实现了我自己的版本作为glsl着色器。我发送一个四边形纹理坐标在0..1之间的角落。 我假设到达的纹理坐标是未失真图像的坐标。我计算与我的纹理坐标对应的扭曲点的坐标。然后我对失真的图像纹理进行采样。

使用此着色器在最终图像中没有更改。我通过cpu实现确定的问题是,系数项非常接近于零。通过半径平方等数字变得越来越小。所以我有一个缩放问题 - 我无法弄明白该做什么不同!我尝试了所有的东西......我想这是非常明显的,因为这种过程似乎适用于很多人。

为简单起见,我省略了切向失真校正。

#version 330 core

in vec2 UV;

out vec4 color;

uniform sampler2D textureSampler;

void main()
{   
    vec2 focalLength = vec2(438.568f, 437.699f);
    vec2 opticalCenter = vec2(667.724f, 500.059f);
    vec4 distortionCoefficients = vec4(-0.035109f, -0.002393f, 0.000335f, -0.000449f);

    const vec2 imageSize = vec2(1280.f, 960.f);

    vec2 opticalCenterUV = opticalCenter / imageSize;

    vec2 shiftedUVCoordinates = (UV - opticalCenterUV);

    vec2 lensCoordinates = shiftedUVCoordinates / focalLength;

    float radiusSquared = sqrt(dot(lensCoordinates, lensCoordinates));
    float radiusQuadrupled = radiusSquared * radiusSquared;

    float coefficientTerm = distortionCoefficients.x * radiusSquared + distortionCoefficients.y * radiusQuadrupled;

    vec2 distortedUV = ((lensCoordinates + lensCoordinates * (coefficientTerm))) * focalLength;

    vec2 resultUV = (distortedUV + opticalCenterUV);

    color = texture2D(textureSampler, resultUV);
}

1 个答案:

答案 0 :(得分:7)

我发现您的解决方案存在两个问题。主要问题是你混合了两个不同的空间。您似乎通过将光学中心转换为该空间来在[0,1]纹理空间中工作,但您没有调整focalLenght。关键点在于对于这样的失真模型,焦距是以像素为单位确定的。但是,现在一个像素不再是1个基本单位宽,而是分别为1/width1/height个单位。

您可以添加vec2 focalLengthUV = focalLength / imageSize,但在计算lensCoordinates时,您会看到两个分部都会相互抵消。将纹理空间UV坐标转换为像素坐标并直接使用该空间更方便:

vec2 lensCoordinates = (UV * imageSize - opticalCenter) / focalLenght;

(并且还分别更改了distortedUVresultUV)的计算。

到目前为止,我所描述的方法还存在一个问题:我之前提到过的像素空间的约定。在GL中,原点将是左下角,而在大多数像素空间中,原点位于 top 左侧。在进行转换时,您可能必须翻转y坐标。另一件事是确切的像素中心位于何处。到目前为止,代码假设像素中心为整数+ 0.5。纹理坐标(0,0)不是左下像素的中心,而是角点。用于失真的参数可能(我不知道OpenCV的约定)假设像素以整数为中心,因此您可能需要通过转换pixelSpace = uv * imageSize而不是转换pixelSpace = uv * imageSize - vec2(0.5)。像float radiusSquared = sqrt(dot(lensCoordinates, lensCoordinates)); 这样的半像素。

我看到的第二个问题是

sqrt

此处dot(a,a)不正确,因为a已经给出了向量{{1}}的平方长度。