如何使用OpenGL检测二进制图像中的角?


13

我有二进制160x120图像,例如:

原始图像

我想检测那些白色斑点的角落。它们以前是通过数学形态来封闭的,因此不应有任何内角。在这种情况下,我想要16个角,例如:

角点检测示例

我的第一次尝试是使用一些OpenCV函数,例如goodFeaturesToTrackFAST,但是它们特别慢(而且FAST非常不稳定)。我的想法是在GPU上进行这样的计算,因为我的源图像来自它。我在网上寻找有关如何编写此类着色器的想法(我使用的是OpenGL ES 2.0),但没有发现任何具体的想法。知道如何启动这样的算法吗?


2
FAST慢吗?:)
endlith 2011年

1
是的,有趣吗?事实上,它比先例算法,如SURF或SIFT更快,但它不太精确,很不稳定从一个图像到另一个还不够快要在CPU上完成
斯特凡Péchard

在每个帧上准确检测这些错误有多重要?矩形移动速度有多快?是否可以检测大多数帧的拐角并将其插值到算法遗漏的帧上?
justis 2011年

@just很好,我现在的方式(通过使用OpenCV的cvFindContours()和cvApproxPoly()函数)随着时间的推移不是很稳定,所以我用低通滤波器对结果进行滤波,从而引入了滞后。您认为插值可以得到更稳定的结果吗?
斯特凡·佩查德

Answers:


3

您正在处理什么尺寸的图像?以什么帧速率?在什么硬件上?根据我的经验,FAST非常漂亮,很慢。

我还看到FAST用作ROI检测器,在确定的ROI上运行goodFeaturesToTrack可以提供更好的稳定性,而不会在整个图像上产生gFTT的损失。

“哈里斯”角探测器也可能非常快,因为它是由非常简单的操作了(每例如像素没有的sqrt()!) -不一样稳定gFTT,但可能更比快。

(就GPU的实现而言,Google gpu corner似乎提供了很多链接,但我不知道它们可能有多合适-我倾向于在FPGA中实现。)


我的图像在iPhone上为160x120,以30fps的速度拍摄,但是该应用程序还有很多工作要做:-)我已经看到一个应用程序在这样的设备上实现FAST的速度非常快,但这只是一个演示而已这样做...这就是为什么我希望基于GPU的解决方案。
2011年

15

我只是碰巧使用Harris角检测在OpenGL ES 2.0上实现了类似的功能,虽然还没有完全完成,但我想我会分享到目前为止的基于着色器的实现。我已经将其作为基于iOS的开源框架的一部分进行了操作,因此,如果您对某些特定步骤的工作方式感到好奇,可以查看代码。

为此,我使用以下步骤:

  • 使用RGB值与矢量(0.2125,0.7154,0.0721)的点积将图像减小到其亮度值。
  • 通过从当前像素左右,上下左右像素减去红色通道值,计算X和Y导数。然后,我将x导数平方存储在红色通道中,将Y导数平方存储在绿色通道中,将X和Y导数的乘积存储在蓝色通道中。片段着色器如下所示:

    precision highp float;
    
    varying vec2 textureCoordinate;
    varying vec2 leftTextureCoordinate;
    varying vec2 rightTextureCoordinate;
    
    varying vec2 topTextureCoordinate; 
    varying vec2 bottomTextureCoordinate;
    
    uniform sampler2D inputImageTexture;
    
    void main()
    {
     float topIntensity = texture2D(inputImageTexture, topTextureCoordinate).r;
     float bottomIntensity = texture2D(inputImageTexture, bottomTextureCoordinate).r;
     float leftIntensity = texture2D(inputImageTexture, leftTextureCoordinate).r;
     float rightIntensity = texture2D(inputImageTexture, rightTextureCoordinate).r;
    
     float verticalDerivative = abs(-topIntensity + bottomIntensity);
     float horizontalDerivative = abs(-leftIntensity + rightIntensity);
    
     gl_FragColor = vec4(horizontalDerivative * horizontalDerivative, verticalDerivative * verticalDerivative, verticalDerivative * horizontalDerivative, 1.0);
    }
    

    变化只是每个方向上的偏移纹理坐标。我在顶点着色器中对其进行了预先计算,以消除依赖的纹理读取,而这些读取在这些移动GPU上非常慢。

  • 将高斯模糊应用于此导数图像。我使用了水平和垂直分离的模糊处理,并利用硬件纹理过滤功能进行了9次模糊处理,每遍仅读取了五个纹理。我在这个Stack Overflow答案中描述了这个着色器。

  • 使用模糊的输入导数值运行实际的Harris拐角检测计算。在这种情况下,我实际上是使用Alison Noble在其博士学位中描述的计算方法。论文“图像表面的描述”。处理此问题的着色器如下所示:

    varying highp vec2 textureCoordinate;
    
    uniform sampler2D inputImageTexture;
    
    const mediump float harrisConstant = 0.04;
    
    void main()
    {
     mediump vec3 derivativeElements = texture2D(inputImageTexture, textureCoordinate).rgb;
    
     mediump float derivativeSum = derivativeElements.x + derivativeElements.y;
    
     // This is the Noble variant on the Harris detector, from 
     // Alison Noble, "Descriptions of Image Surfaces", PhD thesis, Department of Engineering Science, Oxford University 1989, p45.     
     mediump float harrisIntensity = (derivativeElements.x * derivativeElements.y - (derivativeElements.z * derivativeElements.z)) / (derivativeSum);
    
     // Original Harris detector
     //     highp float harrisIntensity = derivativeElements.x * derivativeElements.y - (derivativeElements.z * derivativeElements.z) - harrisConstant * derivativeSum * derivativeSum;
    
     gl_FragColor = vec4(vec3(harrisIntensity * 10.0), 1.0);
    }
    
  • 执行局部非最大抑制并应用阈值以突出显示通过的像素。我使用以下片段着色器对中心像素附近的八个像素进行采样,并确定该像素是否为该分组中的最大值:

    uniform sampler2D inputImageTexture;
    
    varying highp vec2 textureCoordinate;
    varying highp vec2 leftTextureCoordinate;
    varying highp vec2 rightTextureCoordinate;
    
    varying highp vec2 topTextureCoordinate;
    varying highp vec2 topLeftTextureCoordinate;
    varying highp vec2 topRightTextureCoordinate;
    
    varying highp vec2 bottomTextureCoordinate;
    varying highp vec2 bottomLeftTextureCoordinate;
    varying highp vec2 bottomRightTextureCoordinate;
    
    void main()
    {
        lowp float bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).r;
        lowp float bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;
        lowp float bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;
        lowp vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);
        lowp float leftColor = texture2D(inputImageTexture, leftTextureCoordinate).r;
        lowp float rightColor = texture2D(inputImageTexture, rightTextureCoordinate).r;
        lowp float topColor = texture2D(inputImageTexture, topTextureCoordinate).r;
        lowp float topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate).r;
        lowp float topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate).r;
    
        // Use a tiebreaker for pixels to the left and immediately above this one
        lowp float multiplier = 1.0 - step(centerColor.r, topColor);
        multiplier = multiplier * 1.0 - step(centerColor.r, topLeftColor);
        multiplier = multiplier * 1.0 - step(centerColor.r, leftColor);
        multiplier = multiplier * 1.0 - step(centerColor.r, bottomLeftColor);
    
        lowp float maxValue = max(centerColor.r, bottomColor);
        maxValue = max(maxValue, bottomRightColor);
        maxValue = max(maxValue, rightColor);
        maxValue = max(maxValue, topRightColor);
    
        gl_FragColor = vec4((centerColor.rgb * step(maxValue, centerColor.r) * multiplier), 1.0);
    }
    

此过程从您的对象生成一个如下所示的拐角图:

边角图

根据非最大抑制和阈值确定以下点为角点:

确定的角落

通过为该滤镜设置适当的阈值,它可以识别该图像中的所有16个角,尽管它确实会将角放置在对象的实际边缘内一个像素左右。

在iPhone 4上,此角点检测可以以20 FPS的速度运行在来自相机的640x480帧视频上,而iPhone 4S可以轻松以60 FPS以上的速度处理该尺寸的视频。对于这样的任务,这应该比CPU约束处理快很多,尽管现在读回这些点的过程是CPU约束的,并且比它应该的要慢一些。

如果您希望实际操作,可以获取我框架的代码并运行其附带的FilterShowcase示例。哈里斯(Harris)拐角检测示例在设备摄像头的实时视频上运行,尽管正如我提到的,拐角点的读取当前发生在CPU上,这确实减慢了速度。我也正在为此基于GPU的过程。


1
非常好!我在github上遵循您的框架,恭喜!
2012年

在某个地方有一个示例如何将角坐标实际返回给CPU?是否存在某种智能GPU方式,或者是否需要回读,然后通过返回的位图在CPU上循环以查找标记的像素?
Quasimondo 2014年

@Quasimondo-我一直在努力使用直方图金字塔进行点提取:tevs.eu/files/vmv06.pdf,以避免拐角检测中像素上受CPU限制的迭代。最近有点分心,所以还没有完全完成,但是我想尽快。
布拉德·拉尔森

@BradLarson,您好,我知道这是一个非常老的话题,谢谢您的回答。我刚刚在GPUImage框架中检查了KGPUImageHarrisCornerDetection.m。要从图像提取角点位置,已使用glReadPixels将图像读取到缓冲区中,然后在缓冲区上循环以将colotByte> 0的点存储在数组中。有什么方法可以在GPU中完成所有操作,而不必在缓冲区和循环中读取图像?
Sahil Bajaj

1
@SahilBajaj-我所见过的一种技术(尚无时间实施)是使用直方图金字塔从这样的稀疏图像中快速提取点。这样可以大大加快速度。
布拉德·拉尔森

3

众所周知,像Shi-Tomasi和Moravec这样的“稳健”转角检测器速度很慢。在此处检查它们-http: //en.wikipedia.org/wiki/Corner_detection FAST可能是唯一足够好的轻巧型角检测器。您可以通过执行非最大抑制来改善FAST-选择具有最佳“角度”得分的FAST输出(有几种直观的计算方法,包括Shi-Tomasi和Moravec作为边角得分)您还可以从多个FAST检测器中进行选择-从FAST-5到FAST-12和FAST_ER(对于移动设备来说,最后一个可能太大)另一种方法是获取生成的FAST-从作者站点获取FAST代码生成器,并在可能的图像集上对其进行训练。 http://www.edwardrosten.com/work/fast.html


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.