边缘模糊是图像中经常发生的质量问题,导致轮廓不清晰和线条不清晰,使得图像特征的提取,识别和理解变得困难。增强图像边缘和线条以使图像边缘清晰的过程就是我们所说的图像锐化。
在移动设备上使用GPU进行图像锐化通常是使用空间滤镜对图像执行模板卷积处理。主要步骤如下:1)。使用模板遍历图像,以使模板的中心与图像中的像素重合。 2)。将模板上每个点的系数乘以图像中的相应像素3),将所有乘积4)相加,然后将总和分配给相应的中心像素
图像锐化中常用的方法主要包括梯度运算,拉普拉斯算子等。
梯度算法
梯度算法的结果值与相邻像素的灰度差成正比。图像经过渐变操作后,留下了灰度值急剧变化的边缘点。 Sobel运算符和Prewitt运算符的特定实现可以在GPUImage中找到。 Sobel和Prewitt都是3x3模板的渐变操作。模板如下:
GPUImageSobelEdgeDetectionFilter是GPUImage使用Sobel运算符实现的边缘检测器,其运行效果如下:
接下来,我们深入研究源代码,以了解如何在GPUImage中实现这种过滤器。
@interface GPUImageSobelEdgeDetectionFilter : GPUImageTwoPassFilter
首先,GPUImageSobelEdgeDetectionFilter继承自GPUImageTwoPassFilter。 GPUImageTwoPassFilter在内部管理2个GLProgram,每个GLProgram代表一个GPU处理过程。之所以需要两个GLProgram,是因为渐变计算基于灰度变化的灰度计算,因此第一个GLProgram用于将图像转换为灰度(具体来说,使用GPUImageGrayscaleFilter.h中的着色器),并且第二个GLProgram用于将图像转换为灰度。真正用于处理sobel算法的着色器。
if (!(self = [super initWithFirstStageVertexShaderFromString:kGPUImageVertexShaderString firstStageFragmentShaderFromString:kGPUImageLuminanceFragmentShaderString secondStageVertexShaderFromString:kGPUImageNearbyTexelSamplingVertexShaderString secondStageFragmentShaderFromString:fragmentShaderString]))
{
return nil;
}
...初始化过程非常简单。您可以直接调用父类的初始化接口,并传入GLProgram所需的两个着色器。此处不详细介绍灰度变换的着色器。让我们专注于sobel算法的顶点着色器和片段着色器。
NSString *const kGPUImageNearbyTexelSamplingVertexShaderString = SHADER_STRING
(
attribute ve position;
attribute ve 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;
}
);片段着色器的计算资源更加宝贵。您会发现有一种技巧可以计算顶点着色器中每个像素周围的坐标点,然后使用变化的变量将其传递给片段着色器。这样,当您要在片段着色器中获取周围的像素值时,只需直接使用通过的坐标点即可。
NSString *const kGPUImageSobelEdgeDetectionFragmentShaderString = SHADER_STRING
(
precision mediump float;
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;
uniform sampler2D inputImageTexture;
uniform float edgeStrength;
void main()
{
float bottomLeftIntensity = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;
float topRightIntensity = texture2D(inputImageTexture, topRightTextureCoordinate).r;
float topLeftIntensity = texture2D(inputImageTexture, topLeftTextureCoordinate).r;
float bottomRightIntensity = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;
float leftIntensity = texture2D(inputImageTexture, leftTextureCoordinate).r;
float rightIntensity = texture2D(inputImageTexture, rightTextureCoordinate).r;
float bottomIntensity = texture2D(inputImageTexture, bottomTextureCoordinate).r;
float topIntensity = texture2D(inputImageTexture, topTextureCoordinate).r;
float h = -topLeftIntensity - 2.0 * topIntensity - topRightIntensity + bottomLeftIntensity + 2.0 * bottomIntensity + bottomRightIntensity;
float v = -bottomLeftIntensity - 2.0 * leftIntensity - topLeftIntensity + bottomRightIntensity + 2.0 * rightIntensity + topRightIntensity;
float mag = length(vec2(h, v)) * edgeStrength;
gl_FragColor = ve(vec3(mag), 1.0);
}
);在片段着色器中,获取周围的像素值,然后根据sobel运算符计算水平和垂直乘积。最后,以水平和垂直差为坐标计算向量长度,并以最终的长度值作为结果。
过滤器中的其他代码主要是属性设置,几乎不需要其他GL代码调用。 GPU调用基本上由GPUImage封装,因此扩展非常方便。过滤器的使用也非常简单:
GPUImagePicture *gpuPic = [[GPUImagePicture alloc] initWithImage:sampleImage];
GPUImageSobelEdgeDetectionFilter *sobelFilter = [[GPUImageSobelEdgeDetectionFilter alloc] init];
[gpuPic addTarget:sobelFilter];
[gpuPic processImageUpToFilter:sobelFilter withCompletionHandler:^(UIImage *processedImage) {
// do something
}];拉普拉斯算子
拉普拉斯算子算法更适合于改善图像模糊,并且它是更常用的边缘增强处理算子。其模板表示如下:
您还可以在GPUImage中找到拉普拉斯算法GPUImageLaplacianFilter和GPUImageSharpenFilter的特定实现。 GPUImageSharpenFilter是基于拉普拉斯算子的拉普拉斯锐化,其运行效果如下:
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shumachanpin/article-378297-1.html
希望在以后能够继续专注我的作品
)超爱MV细腻动人
还是14度16度的橙色喝着爽