我正在尝试使用16X16生成的内核对图像执行卷积。我使用opencv filterengine类,但它只在CPU上运行,我正在尝试加速应用程序。 我知道opencv也有filterengine_gpu,但据我了解,这不是IOS支持的。 GPUimage允许您使用3X3生成的过滤器执行卷积。有没有其他方法来加速卷积?在GPU上运行的不同库?
答案 0 :(得分:2)
您可以使用Apple的Accelerate framework。它可以在iOS和MacOS上使用,因此可能会在以后重用您的代码。
为了获得最佳性能,您可能需要考虑以下选项:
答案 1 :(得分:1)
您可以使用GPUImage进行16x16卷积,但是您需要编写自己的过滤器来执行此操作。框架中的3x3卷积从输入图像中每个像素周围3x3区域中的像素进行采样,并应用您输入的权重矩阵。框架内的GPUImage3x3ConvolutionFilter.m源文件应该相当容易阅读,但我可以提供如果你想超越我的目标,那就是一个小小的背景。
我要做的第一件事是使用以下顶点着色器:
attribute vec4 position;
attribute vec4 inputTextureCoordinate;
uniform float texelWidth;
uniform float texelHeight;
varying vec2 textureCoordinate;
varying vec2 leftTextureCoordinate;
varying vec2 rightTextureCoordinate;
varying vec2 topTextureCoordinate;
varying vec2 topLeftTextureCoordinate;
varying vec2 topRightTextureCoordinate;
varying vec2 bottomTextureCoordinate;
varying vec2 bottomLeftTextureCoordinate;
varying vec2 bottomRightTextureCoordinate;
void main()
{
gl_Position = position;
vec2 widthStep = vec2(texelWidth, 0.0);
vec2 heightStep = vec2(0.0, texelHeight);
vec2 widthHeightStep = vec2(texelWidth, texelHeight);
vec2 widthNegativeHeightStep = vec2(texelWidth, -texelHeight);
textureCoordinate = inputTextureCoordinate.xy;
leftTextureCoordinate = inputTextureCoordinate.xy - widthStep;
rightTextureCoordinate = inputTextureCoordinate.xy + widthStep;
topTextureCoordinate = inputTextureCoordinate.xy - heightStep;
topLeftTextureCoordinate = inputTextureCoordinate.xy - widthHeightStep;
topRightTextureCoordinate = inputTextureCoordinate.xy + widthNegativeHeightStep;
bottomTextureCoordinate = inputTextureCoordinate.xy + heightStep;
bottomLeftTextureCoordinate = inputTextureCoordinate.xy - widthNegativeHeightStep;
bottomRightTextureCoordinate = inputTextureCoordinate.xy + widthHeightStep;
}
计算从中对卷积中使用的像素颜色进行采样的位置。因为使用归一化坐标,所以像素之间的X和Y间距分别为1.0 / [图像宽度]和1.0 / [图像高度]。
在顶点着色器中计算要采样的像素的纹理坐标有两个原因:每个顶点执行一次计算效率更高(构成图像矩形的两个三角形中有六个) )比每个片段(像素),并尽可能避免依赖纹理读取。依赖纹理读取是在片段着色器中计算要读取的纹理坐标的位置,而不是简单地从顶点着色器传入,并且它们在iOS GPU上要慢得多。
一旦我在顶点着色器中计算了纹理位置,我将它们作为变化传递到片段着色器中,并在那里使用以下代码:
uniform sampler2D inputImageTexture;
uniform mat3 convolutionMatrix;
varying vec2 textureCoordinate;
varying vec2 leftTextureCoordinate;
varying vec2 rightTextureCoordinate;
varying vec2 topTextureCoordinate;
varying vec2 topLeftTextureCoordinate;
varying vec2 topRightTextureCoordinate;
varying vec2 bottomTextureCoordinate;
varying vec2 bottomLeftTextureCoordinate;
varying vec2 bottomRightTextureCoordinate;
void main()
{
vec3 bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).rgb;
vec3 bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate).rgb;
vec3 bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate).rgb;
vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);
vec3 leftColor = texture2D(inputImageTexture, leftTextureCoordinate).rgb;
vec3 rightColor = texture2D(inputImageTexture, rightTextureCoordinate).rgb;
vec3 topColor = texture2D(inputImageTexture, topTextureCoordinate).rgb;
vec3 topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate).rgb;
vec3 topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate).rgb;
vec3 resultColor = topLeftColor * convolutionMatrix[0][0] + topColor * convolutionMatrix[0][1] + topRightColor * convolutionMatrix[0][2];
resultColor += leftColor * convolutionMatrix[1][0] + centerColor.rgb * convolutionMatrix[1][1] + rightColor * convolutionMatrix[1][2];
resultColor += bottomLeftColor * convolutionMatrix[2][0] + bottomColor * convolutionMatrix[2][1] + bottomRightColor * convolutionMatrix[2][2];
gl_FragColor = vec4(resultColor, centerColor.a);
这将读取9种颜色中的每种颜色,并应用为卷积提供的3x3矩阵的权重。
也就是说,一个16x16的卷积是一个相当昂贵的操作。您正在查看每个像素256个纹理读取。在较旧的设备(iPhone 4左右)上,如果它们是非依赖性读取,则每个像素可以免费获得大约8个纹理读取。一旦你完成了这一点,性能开始急剧下降。不过,后来的GPU显着加快了这一速度。例如,iPhone 5S几乎可以免费获得每个像素超过40个相关纹理读取。即使是1080p视频上最重的着色器也几乎没有减慢速度。
正如sansuiso建议的那样,如果你有办法将内核分成水平和垂直通道(就像高斯模糊内核那样),由于纹理读取的显着减少,你可以获得更好的性能。对于你的16x16内核,你可以从256次读取减少到32次,甚至那些32次也会快得多,因为它们来自一次仅采样16个纹素的传递。
执行此类操作的交叉点在CPU上的加速比在OpenGL ES中更快会因您运行的设备而异。一般来说,iOS设备上的GPU在每一代产品中的性能增长都超过了CPU,因此在过去的几款iOS模型中,这一点已经转移到了GPU端。