Metal每日分享,3x3矩阵卷积滤镜效果

简介: Metal每日分享,3x3矩阵卷积滤镜效果

Demo


HarbethDemo地址

iDay每日分享文档地址


实操代码


// 锐化卷积效果滤镜
let filter = C7ConvolutionMatrix3x3(convolutionType: .sharpen(iterations: 2))
// 方案1:
let dest = BoxxIO.init(element: originImage, filter: filter)
ImageView.image = try? dest.output()
dest.filters.forEach {
    NSLog("%@", "\($0.parameterDescription)")
}
// 方案2:
ImageView.image = try? originImage.make(filter: filter)
// 方案3:
ImageView.image = originImage ->> filter


实现原理


过滤器

这款滤镜采用并行计算编码器设计.compute(kernel: "C7ConvolutionMatrix3x3"),参数因子[Float(convolutionPixel)]

对外开放参数

convolutionPixel: 卷积像素

/// 3 x 3卷积
public struct C7ConvolutionMatrix3x3: C7FilterProtocol {
    public enum ConvolutionType {
        case `default`
        case identity
        case edgedetect
        case embossment
        case embossment45
        case morphological
        case sobel(orientation: Bool)
        case laplance(iterations: Float)
        case sharpen(iterations: Float)
        case custom(Matrix3x3)
    }
    /// Convolution pixels, default 1
    public var convolutionPixel: Int = 1
    private var matrix: Matrix3x3
    public var modifier: Modifier {
        return .compute(kernel: "C7ConvolutionMatrix3x3")
    }
    public var factors: [Float] {
        return [Float(convolutionPixel)]
    }
    public func setupSpecialFactors(for encoder: MTLCommandEncoder, index: Int) {
        guard let computeEncoder = encoder as? MTLComputeCommandEncoder else { return }
        var factor = matrix.to_factor()
        computeEncoder.setBytes(&factor, length: Matrix3x3.size, index: index + 1)
    }
    public init(matrix: Matrix3x3) {
        self.matrix = matrix
    }
    public init(convolutionType: ConvolutionType) {
        self.init(matrix: convolutionType.matrix)
    }
    public mutating func updateConvolutionType(_ convolutionType: ConvolutionType) {
        self.matrix = convolutionType.matrix
    }
    public mutating func updateMatrix3x3(_ matrix: Matrix3x3) {
        self.matrix = matrix
    }
}
extension C7ConvolutionMatrix3x3.ConvolutionType {
    var matrix: Matrix3x3 {
        switch self {
        case .identity:
            return Matrix3x3.Kernel.identity
        case .edgedetect:
            return Matrix3x3.Kernel.edgedetect
        case .embossment:
            return Matrix3x3.Kernel.embossment
        case .embossment45:
            return Matrix3x3.Kernel.embossment45
        case .morphological:
            return Matrix3x3.Kernel.morphological
        case .sobel(let orientation):
            return Matrix3x3.Kernel.sobel(orientation)
        case .laplance(let iterations):
            return Matrix3x3.Kernel.laplance(iterations)
        case .sharpen(let iterations):
            return Matrix3x3.Kernel.sharpen(iterations)
        case .custom(let matrix3x3):
            return matrix3x3
        default:
            return Matrix3x3.Kernel.`default`
        }
    }
}

着色器

取像素点周边九个区域半径点像素,然后归一化处理,然后取出每个像素对应rgb,再进行卷积矩阵运算得到卷积之后的rgb值,生成新的像素颜色;

kernel void C7ConvolutionMatrix3x3(texture2d<half, access::write> outputTexture [[texture(0)]],
                                   texture2d<half, access::sample> inputTexture [[texture(1)]],
                                   constant float *pixel [[buffer(0)]],
                                   constant float3x3 *matrix3x3 [[buffer(1)]],
                                   uint2 grid [[thread_position_in_grid]]) {
    constexpr sampler quadSampler(mag_filter::linear, min_filter::linear);
    const float x = float(grid.x);
    const float y = float(grid.y);
    const float w = float(inputTexture.get_width());
    const float h = float(inputTexture.get_height());
    const float l = float(x - *pixel);
    const float r = float(x + *pixel);
    const float t = float(y - *pixel);
    const float b = float(y + *pixel);
    // Normalization
    const float2 m11Coordinate = float2(l / w, t / h);
    const float2 m12Coordinate = float2(x / w, t / h);
    const float2 m13Coordinate = float2(r / w, t / h);
    const float2 m21Coordinate = float2(l / w, y / h);
    const float2 m22Coordinate = float2(x / w, y / h);
    const float2 m23Coordinate = float2(r / w, y / h);
    const float2 m31Coordinate = float2(l / w, b / h);
    const float2 m32Coordinate = float2(x / w, b / h);
    const float2 m33Coordinate = float2(r / w, b / h);
    const half4 centerColor = inputTexture.sample(quadSampler, m22Coordinate);
    const half3 m11Color = inputTexture.sample(quadSampler, m11Coordinate).rgb;
    const half3 m12Color = inputTexture.sample(quadSampler, m12Coordinate).rgb;
    const half3 m13Color = inputTexture.sample(quadSampler, m13Coordinate).rgb;
    const half3 m21Color = inputTexture.sample(quadSampler, m21Coordinate).rgb;
    const half3 m22Color = centerColor.rgb;
    const half3 m23Color = inputTexture.sample(quadSampler, m23Coordinate).rgb;
    const half3 m31Color = inputTexture.sample(quadSampler, m31Coordinate).rgb;
    const half3 m32Color = inputTexture.sample(quadSampler, m32Coordinate).rgb;
    const half3 m33Color = inputTexture.sample(quadSampler, m33Coordinate).rgb;
    const float3x3 matrix = (*matrix3x3);
    half3 resultColor = half3(0.0h);
    resultColor += m11Color * (matrix[0][0]) + m12Color * (matrix[0][1]) + m13Color * (matrix[0][2]);
    resultColor += m21Color * (matrix[1][0]) + m22Color * (matrix[1][1]) + m23Color * (matrix[1][2]);
    resultColor += m31Color * (matrix[2][0]) + m32Color * (matrix[2][1]) + m33Color * (matrix[2][2]);
    const half4 outColor = half4(resultColor, centerColor.a);
    outputTexture.write(outColor, grid);
}


其他卷积核


extension Matrix3x3 {
    /// 常见 3x3 矩阵卷积内核,考线性代数时刻😪
    /// Common 3x3 matrix convolution kernel
    public struct Kernel { }
}
extension Matrix3x3.Kernel {
    /// 原始矩阵,空卷积核
    /// The original matrix, the empty convolution kernel
    public static let `default` = Matrix3x3(values: [
        0.0, 0.0, 0.0,
        0.0, 1.0, 0.0,
        0.0, 0.0, 0.0,
    ])
    public static let identity = Matrix3x3(values: [
        1.0, 0.0, 0.0,
        0.0, 1.0, 0.0,
        0.0, 0.0, 1.0,
    ])
    /// 边缘检测矩阵
    /// Edge detection matrix
    public static let edgedetect = Matrix3x3(values: [
        -1.0, -1.0, -1.0,
        -1.0,  8.0, -1.0,
        -1.0, -1.0, -1.0,
    ])
    /// 浮雕矩阵
    /// Anaglyph matrix
    public static let embossment = Matrix3x3(values: [
        -2.0, 0.0, 0.0,
         0.0, 1.0, 0.0,
         0.0, 0.0, 2.0,
    ])
    /// 45度的浮雕滤波器
    /// A 45 degree emboss filter
    public static let embossment45 = Matrix3x3(values: [
        -1.0, -1.0, 0.0,
        -1.0,  0.0, 1.0,
         0.0,  1.0, 1.0,
    ])
    /// 侵蚀矩阵
    /// Matrix erosion
    public static let morphological = Matrix3x3(values: [
        1.0, 1.0, 1.0,
        1.0, 1.0, 1.0,
        1.0, 1.0, 1.0,
    ])
    /// 拉普拉斯算子,边缘检测算子
    /// Laplace operator, edge detection operator
    public static func laplance(_ iterations: Float) -> Matrix3x3 {
        let xxx = iterations
        return Matrix3x3(values: [
             0.0, -1.0,  0.0,
            -1.0,  xxx, -1.0,
             0.0, -1.0,  0.0,
        ])
    }
    /// 锐化矩阵
    /// Sharpening matrix
    public static func sharpen(_ iterations: Float) -> Matrix3x3 {
        let cc = (8 * iterations + 1)
        let xx = (-iterations)
        return Matrix3x3(values: [
            xx, xx, xx,
            xx, cc, xx,
            xx, xx, xx,
        ])
    }
    /// Sobel矩阵图像边缘提取,求梯度比较常用
    /// Sobel matrix image edge extraction, gradient is more commonly used
    public static func sobel(_ orientation: Bool) -> Matrix3x3 {
        if orientation {
            return Matrix3x3(values: [
                -1.0, 0.0, 1.0,
                -2.0, 0.0, 2.0,
                -1.0, 0.0, 1.0,
            ])
        } else {
            return Matrix3x3(values: [
                -1.0, -2.0, -1.0,
                 0.0,  0.0,  0.0,
                 1.0,  2.0,  1.0,
            ])
        }
    }
    /// BT.601, which is the standard for SDTV.
    public static let to601 = Matrix3x3(values: [
        1.164,  1.164, 1.164,
        0.000, -0.392, 2.017,
        1.596, -0.813, 0.000,
    ])
    /// BT.601 full range (ref: http://www.equasys.de/colorconversion.html)
    public static let to601FullRange = Matrix3x3(values: [
        1.0,  1.000, 1.000,
        0.0, -0.343, 1.765,
        1.4, -0.711, 0.000,
    ])
    /// BT.709, which is the standard for HDTV.
    public static let to709 = Matrix3x3(values: [
        1.164,  1.164, 1.164,
        0.000, -0.213, 2.112,
        1.793, -0.533, 0.000,
    ])
}


效果图


常见核卷积图

边缘检测矩阵 浮雕矩阵 45度的浮雕滤波器

1.png

1.png

1.png

锐化矩阵 拉普拉斯算子 Sobel矩阵图像边缘提取

1.png

1.png

1.png


Harbeth功能清单


支持ios系统和macOS系统

支持运算符函数式操作

支持多种模式数据源 UIImage, CIImage, CGImage, CMSampleBuffer, CVPixelBuffer.

支持快速设计滤镜

支持合并多种滤镜效果

支持输出源的快速扩展

支持相机采集特效

支持视频添加滤镜特效

支持矩阵卷积

支持使用系统 MetalPerformanceShaders.

支持兼容 CoreImage.

滤镜部分大致分为以下几个模块:

Blend:图像融合技术

Blur:模糊效果

Pixel:图像的基本像素颜色处理

Effect:效果处理

Lookup:查找表过滤器

Matrix: 矩阵卷积滤波器

Shape:图像形状大小相关

Visual: 视觉动态特效

MPS: 系统 MetalPerformanceShaders.


最后


关于3x3矩阵卷积效果滤镜介绍与设计到此为止吧。

慢慢再补充其他相关滤镜,喜欢就给我点个星🌟吧。

滤镜Demo地址,目前包含100+种滤镜,同时也支持CoreImage混合使用。

再附上一个开发加速库KJCategoriesDemo地址

再附上一个网络基础库RxNetworksDemo地址

喜欢的老板们可以点个星🌟,谢谢各位老板!!!✌️.

相关文章
|
5天前
|
算法 图形学
【计算机图形学】实验四 二维图形的缩放、旋转,平移,组合变换
【计算机图形学】实验四 二维图形的缩放、旋转,平移,组合变换
10 2
|
5天前
|
算法
halcon系列之直线拟合和圆形拟合算子
halcon系列之直线拟合和圆形拟合算子
|
5天前
|
计算机视觉
OpenCV(二十七):图像距离变换
OpenCV(二十七):图像距离变换
23 0
|
机器学习/深度学习 存储 计算机视觉
CV9 2D卷积与图像滤波
终于有一天,老板忍无可忍了,以0.5秒的间隔开始不间断的扇你的过程,这样问题就来了,第一次扇你鼓起来的包还没消肿,第二个巴掌就来了,你脸上的包就可能鼓起来两倍高,老板不断扇你,脉冲不断作用在你脸上,包鼓起的程度就在不断叠加了,这样这些效果就可以求和了,如果老板扇你的频率越来越高以至于你感受不到了时间的流逝,那么求和就可以变成积分了。这个积分(实际上是一条函数),就是卷积。
58 0
|
并行计算 iOS开发 MacOS
Metal每日分享,4x4颜色矩阵滤镜效果
Metal每日分享,4x4颜色矩阵滤镜效果
Metal每日分享,4x4颜色矩阵滤镜效果
|
并行计算 iOS开发 MacOS
Metal每日分享,四维向量偏移滤镜效果
Metal每日分享,四维向量偏移滤镜效果
Metal每日分享,四维向量偏移滤镜效果
|
并行计算 iOS开发 计算机视觉
Metal每日分享,均值模糊滤镜效果
Metal每日分享,均值模糊滤镜效果
Metal每日分享,均值模糊滤镜效果
|
并行计算 iOS开发 MacOS
Metal每日分享,高斯双边模糊滤镜效果
Metal每日分享,高斯双边模糊滤镜效果
Metal每日分享,高斯双边模糊滤镜效果
|
并行计算 iOS开发 MacOS
Metal每日分享,图像单色滤镜效果
Metal每日分享,图像单色滤镜效果
Metal每日分享,图像单色滤镜效果
|
传感器 计算机视觉
CV学习笔记-相机模型(欧式变换及仿射变换)
CV学习笔记-相机模型(欧式变换及仿射变换)
412 0
CV学习笔记-相机模型(欧式变换及仿射变换)