opengl 使用GPU着色器错误进行失真校正

r3i60tvu  于 2022-11-04  发布在  其他
关注(0)|答案(1)|浏览(167)

我有一个广角透镜的相机。我知道失真系数,焦距,光学中心。我想消除我从这个相机得到的图像的失真。我用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);
}
xhv8bpkk

xhv8bpkk1#

我发现你的解决方案有两个问题。主要的问题是你混合了两个不同的空间。你似乎是在[0,1]纹理空间中工作,通过将光学中心转换到该空间,但你没有调整focalLenght。关键点是,对于这样一个扭曲模型,焦距是以像素为单位确定的。然而,现在一个像素不再是1个基本单位宽了。而是分别为1/width1/height单元。
您可以添加vec2 focalLengthUV = focalLength / imageSize,但在计算lensCoordinates时,您会看到两个除法将相互抵消。将纹理空间UV坐标转换为像素坐标并直接使用该空间要方便得多:

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

(and也分别改变了distortedUVresultUV的计算)。
到目前为止,我所描述的方法仍有一个问题:我之前提到的像素空间的约定。在GL中,原点是左下角,而在大多数像素空间中,原点是左上角。在进行转换时,你可能需要翻转y坐标。另一件事是像素中心的确切位置。到目前为止,代码假定像素中心是整数+ 0.5。纹理坐标(0,0)不是左下角像素的中心,而是角点。用于失真的参数 * 可能 *(我不知道OpenCV的约定)假设像素中心为整数,因此,您可能需要像pixelSpace = uv * imageSize - vec2(0.5)那样将其偏移半个像素,而不是转换pixelSpace = uv * imageSize
第二个问题是

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

sqrt在这里是不正确的,因为dot(a,a)已经给予了向量a的长度平方。

相关问题