如何使用 OpenGL 模拟 OpenCV 的 warpPerspective 功能(透视变换)

Posted

技术标签:

【中文标题】如何使用 OpenGL 模拟 OpenCV 的 warpPerspective 功能(透视变换)【英文标题】:How to use OpenGL to emulate OpenCV's warpPerspective functionality (perspective transform) 【发布时间】:2015-10-08 14:27:38 【问题描述】:

我在 Python 和 C++ 中使用 OpenCV 完成了图像变形,看到 Coca Cola 标志在我选择的角落变形:

使用以下图片:

还有这个:

Full album with transition pics and description here

我需要这样做,但在 OpenGL 中。我会:

我必须在其中映射扭曲图像的角落

映射徽标图像变换的单应矩阵 进入您在最终图像中看到的徽标图像(使用 OpenCV 的 warpPerspective),类似这样:

[[  2.59952324e+00,   3.33170976e-01,  -2.17014066e+02],
[  8.64133587e-01,   1.82580111e+00,  -3.20053715e+02],
[  2.78910149e-03,   4.47911310e-05,   1.00000000e+00]]

主图(这里是跑道图)

叠加图像(此处为可口可乐图像)

有可能吗?我已经阅读了很多内容并开始了 OpenGL 基础教程,但可以仅从我所拥有的内容中完成吗? OpenGL 实现会更快吗,比如说,大约 10 毫秒?

我目前正在这里玩这个教程: http://ogldev.atspace.co.uk/www/tutorial12/tutorial12.html 我是否朝着正确的方向前进? OpenGL新手在这里,请多多包涵。谢谢。

【问题讨论】:

【参考方案1】:

在尝试了这里和其他地方提出的许多解决方案后,我通过编写一个片段着色器来复制“warpPerspective”所做的事情来解决这个问题。

片段着色器代码如下所示:

varying highp vec2 textureCoordinate;

uniform sampler2D inputImageTexture;

// NOTE: you will need to pass the INVERSE of the homography matrix, as well as 
// the width and height of your image as uniforms!
uniform highp mat3 inverseHomographyMatrix;
uniform highp float width;
uniform highp float height;

void main()

   // Texture coordinates will run [0,1],[0,1];
   // Convert to "real world" coordinates
   highp vec3 frameCoordinate = vec3(textureCoordinate.x * width, textureCoordinate.y * height, 1.0);

   // Determine what 'z' is
   highp vec3 m = inverseHomographyMatrix[2] * frameCoordinate;
   highp float zed = 1.0 / (m.x + m.y + m.z);
   frameCoordinate = frameCoordinate * zed;

   // Determine translated x and y coordinates
   highp float xTrans = inverseHomographyMatrix[0][0] * frameCoordinate.x + inverseHomographyMatrix[0][1] * frameCoordinate.y + inverseHomographyMatrix[0][2] * frameCoordinate.z;
   highp float yTrans = inverseHomographyMatrix[1][0] * frameCoordinate.x + inverseHomographyMatrix[1][1] * frameCoordinate.y + inverseHomographyMatrix[1][2] * frameCoordinate.z;

   // Normalize back to [0,1],[0,1] space
   highp vec2 coords = vec2(xTrans / width, yTrans / height);

   // Sample the texture if we're mapping within the image, otherwise set color to black
   if (coords.x >= 0.0 && coords.x <= 1.0 && coords.y >= 0.0 && coords.y <= 1.0) 
       gl_FragColor = texture2D(inputImageTexture, coords);
    else 
       gl_FragColor = vec4(0.0,0.0,0.0,0.0);
   

请注意,我们在此处传递的单应矩阵是 INVERSE HOMOGRAPHY MATRIX! 您必须将要传递给“warpPerspective”的单应矩阵反转 - 否则此代码将无法工作。

顶点着色器除了通过坐标之外什么都不做:

// Vertex shader
attribute vec4 position;
attribute vec4 inputTextureCoordinate;

varying vec2 textureCoordinate;

void main() 
   // Nothing happens in the vertex shader
   textureCoordinate = inputTextureCoordinate.xy;
   gl_Position = position;

传入未改变的纹理坐标和位置坐标(即 textureCoordinates = [(0,0),(0,1),(1,0),(1,1)] 和 positionCoordinates = [(-1,-1 ),(-1,1),(1,-1),(1,1)],对于三角形条带),这应该可以工作!

【讨论】:

你能解释一下为什么单应性需要倒置吗?如果输出图像的大小与源图像的大小不同,代码会在哪里发生变化?【参考方案2】:

您可以使用texture2DProj() 对纹理进行透视变形,或者使用texture2D() 除以纹理的st 坐标(这是texture2DProj 所做的)。

看看这里:Perspective correct texturing of trapezoid in OpenGL ES 2.0。

warpPerspective 用矩阵投影 (x,y,1) 坐标,然后将(u,v) 除以w,如texture2DProj()。您必须修改矩阵,以便正确标准化生成的坐标。

就性能而言,如果您想将数据读回 CPU,您的瓶颈是glReadPixels。需要多长时间取决于您的设备。如果您只是显示,假设您将两个纹理都加载到 GPU 内存中,OpenGL ES 调用将花费不到 10 毫秒。

【讨论】:

你确定你的建议不会产生中间图片,而不是最右边的一张(我想要的):2.bp.blogspot.com/_TrRVoYy3Itc/SFF1aDGquZI/AAAAAAAAAUY/… ? 你完全正确。我将通过相关相关问题的链接更新答案 该链接确实显示了我想要做的事情。现在我需要弄清楚:1)用透视变换logo_image(以某种方式使用转换为OpenGL的单应矩阵)但在main_image的大小中,使其位置正确。 2)渲染两个图像(主图像和 logo_warped) 3)使用 glReadPixels 将 final_image(我的问题中的第一张图像)放入 OpenCV Mat 对象中。 我不知道是否需要在 OpenCV 中获取相机的内在参数,以帮助我将单应矩阵分解为 R、T 和 P 矩阵(旋转、平移和透视) )。有什么方法可以实现我的目标,而无需进入需要相机内在参数,这需要我实现相机校准,我不能,因为最终用户不会被校准等困扰。 如果您能给我一个 OpenGL 应用程序的示例或示例教程,可以解决我的问题,那就太好了。【参考方案3】:

[编辑] 这在我的 Galaxy S9 上有效,但在我的汽车的 android 上,它有一个问题,即整个输出纹理都是白色的。我坚持使用原始着色器,它可以工作:)

您可以在片段着色器中使用 mat3*vec3 操作:

varying highp vec2 textureCoordinate; 
uniform sampler2D inputImageTexture; 
uniform highp mat3 inverseHomographyMatrix; 
uniform highp float width; 
uniform highp float height; 
void main() 
 
    highp vec3 frameCoordinate = vec3(textureCoordinate.x * width, textureCoordinate.y * height, 1.0); 
    highp vec3 trans = inverseHomographyMatrix * frameCoordinate; 
    highp vec2 coords = vec2(trans.x / width, trans.y / height) / trans.z; 
    if (coords.x >= 0.0 && coords.x <= 1.0 && coords.y >= 0.0 && coords.y <= 1.0)  
        gl_FragColor = texture2D(inputImageTexture, coords); 
     else  
        gl_FragColor = vec4(0.0,0.0,0.0,0.0); 
     
;

如果你想要透明背景别忘了添加

GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
GLES20.glBlendEquation(GLES20.GL_FUNC_ADD);

并设置转置标志(以防你使用上面的着色器):

GLES20.glUniformMatrix3fv(H_P2D, 1, true, homography, 0);

【讨论】:

以上是关于如何使用 OpenGL 模拟 OpenCV 的 warpPerspective 功能(透视变换)的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV + OpenGL - 获取 OpenGL 图像作为 OpenCV 相机

OpenCV:是不是可以使用它执行 openGL 像素着色?

如何在 Android 中将 OpenCV 旋转和平移矢量与 OpenGL ES 一起使用?

Opencv 导入图片 Opengl 显示纹理(含用例代码)

C++ - OpenGL/OpenCV - 使用 UVMap 来映射图像?

如何根据相机校准(C++)生成的真实数据使用opencv模拟失真?