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

时间:2015-10-08 14:27:38

标签: android c++ opencv opengl-es homography

我在Python和C ++中使用OpenCV完成了图像变形,看到可口可乐标志在我选择的角落里翘曲:

final image

使用以下图片:

logo image

和此:

enter image description here

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新手在这里,请承担。感谢。

3 个答案:

答案 0 :(得分:2)

在尝试了这里和其他地方提出的一些解决方案后,我通过编写一个片段着色器来解决这个问题,该片段着色器复制了什么' 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);
   }
}

请注意,我们在这里传递的单应矩阵是逆向人体矩阵!你必须反转你将传入的单应矩阵&#39; warpPerspective&#39;否则这段代码不管用。

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

// 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)],对于三角形条),这应该有效!

答案 1 :(得分:0)

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

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

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

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

答案 2 :(得分:0)

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

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

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);