我只是碰巧在使用 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 上是出了名的慢。
对此衍生图像应用高斯模糊。我使用了分离的水平和垂直模糊,并利用硬件纹理过滤来进行九次模糊,每次只有五次纹理读取。我在这个 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 上,这种角点检测可以在来自相机的 640x480 帧视频上以 20 FPS 的速度运行,而 iPhone 4S 可以轻松地以 60+ FPS 的速度处理该尺寸的视频。对于这样的任务,这应该比 CPU-bound 处理快很多,尽管现在读回点的过程是 CPU-bound 并且比它应该的慢一点。
如果你想看到它的实际效果,你可以获取我的框架的代码并运行它附带的 FilterShowcase 示例。那里的 Harris 角点检测示例在来自设备摄像头的实时视频上运行,尽管正如我提到的,角点的回读当前发生在 CPU 上,这确实减慢了速度。为此,我也正在转向基于 GPU 的进程。