首页 > 解决方案 > 使用 CIfilter 的误差扩散抖动图像

问题描述

我正在尝试抖动图像。我制作了一些应用 floyd steinberg 抖动的快速代码,但处理图像需要很长时间,因为它没有包装在 cifilter 中,它只是快速代码。我在想,如果我可以制作一个自定义 cifilter,它将在 gpu 上进行处理并加快处理速度。但是我不是 CIfilter 语言的专家。

这是我的快速代码。为了清楚起见,我已经完整地写出了误差分布矩阵的计算。

    internal struct color {
    let r: Int
    let g: Int
    let b: Int
    }

    func ditherImage2(){
    let image = UIImage(named: "image")
    let width = Int(image!.size.width)
    let height = Int(image!.size.height)
    let pixelArray = pixelarray(image)

    func offset(row: Int, column: Int) -> Int {
     return row * width + column
    }

    for y in 0 ..< height {
    for x in 0 ..< width {
    let currentOffset = offset(row: y, column: x)
    let currentColor = pixelArray![currentOffset]
    // get current colour of pixel
    let oldR = currentColor.r
    let oldG = currentColor.g
    let oldB = currentColor.b
        // quantize / reduce the colours to pallet of 6 colours
    let factor = 1;
    let newR = round(factor * oldR / 255) * (255/factor)
    let newG = round(factor * oldG / 255) * (255/factor)
    let newB = round(factor * oldB / 255) * (255/factor)
        pixelArray[currentOffset] = color(r:newR, g:newG, b:newB)

    let errR = oldR - newR;
    let errG = oldG - newG;
    let errB = oldB - newB;

    // distribute the error to the surrounding pixels using floyd stenberg matrix
    let index = offset(row:x+1, column:y)
    let c = pixelArray[index]
    let r = c.r
    let g = c.g
    let b = c.b
    r = r + errR * 7/16.0;
    g = g + errG * 7/16.0;
    b = b + errB * 7/16.0;
        pixelArray[index] = color(r:r, g:g, b:b);

    let index2 = offset(row:x-1, column:y+1  );
    let c2 = pixelArray[index2]
    let r2 = c.r
    let g2 = c.g
    let b2 = c.b
    r2 = r2 + errR * 3/16.0;
    g2 = g2 + errG * 3/16.0;
    b2 = b2 + errB * 3/16.0;
        pixelArray[index] = color(r:r2, g:g2, b:b2);

    let index3 = offset(row:x, column:y+1);
    let c3 = pixelArray[index3]
    let r3 = c.r
    let g3 = c.g
    let b3 = c.b
    r3 = r3 + errR * 5/16.0;
    g3 = g3 + errG * 5/16.0;
    b3 = b3 + errB * 5/16.0;
        pixelArray[index] = color(r:r3, g:g3, b:b3);


    let index4 = offset(row:x+1, column:y+1);
    let c4 = pixelArray[index]
    let r4 = c.r
    let g4 = c.g
    let b4 = c.b
    r4 = r4 + errR * 1/16.0;
    g4 = g4 + errG * 1/16.0;
    b4 = b4 + errB * 1/16.0;
        pixelArray[index] = color(r:r4, g:g4, b:b4);
    }
    }
    }

我发现了这个https://github.com/rhoeper/Filterpedia-Swift4,它包括一个用于有序抖动的自定义过滤器,我可以将其用作基础并尝试适应错误扩散抖动。在开始学习 CIfilter 语言之前,我更愿意找到一个现有的自定义内核来完成这项工作。所以我想知道是否有人有一个现有的内核或一个链接?

有序抖动代码

  1. float orderedDither2x2(float colorin, float bx, float by, float errorIntensity)
    {
    float error = 0.0;
    int px = int(bx);
    int py = int(by);
    if (py == 0) {
    if (px == 0) { error = 1.0 / 4.0; }
    if (px == 1) { error = 3.0 / 4.0; }
    }
    if (py == 1) {
    if (px == 0) { error = 4.0 / 4.0; }
    if (px == 1) { error = 2.0 / 4.0; }
    }
    return colorin * (error *  errorIntensity);
    }     
    
    kernel vec4 ditherBayer(sampler image, float intensity, float matrix, float palette)
    {
    vec4 pixel = sample(image, samplerCoord(image));
    int msize = int(matrix);
    
    float px = mod(pixel.x, msize >= 5 ? float(4.0) : float(msize));
    float py = mod(pixel.y, msize >= 5 ? float(4.0) : float(msize));
    
    float red = pixel.r;
    float green = pixel.g;
    float blue = pixel.b;
    
    if (msize == 2) {
    pixel.r = orderedDither2x2(red, px, py, intensity);
    pixel.g = orderedDither2x2(green, px, py, intensity);
    pixel.b = orderedDither2x2(blue, px, py, intensity);
    }
    
    if (msize == 3) {
    pixel.r = orderedDither3x3(red, px, py, intensity);
    pixel.g = orderedDither3x3(green, px, py, intensity);
    pixel.b = orderedDither3x3(blue, px, py, intensity);
    }
    
    if (msize == 4) {
    pixel.r = orderedDither4x4(red, px, py, intensity);
    pixel.g = orderedDither4x4(green, px, py, intensity);
    pixel.b = orderedDither4x4(blue, px, py, intensity);
    }
    if (msize >= 5) {
    pixel.r = orderedDither8x8(red, px, py, intensity);
    pixel.g = orderedDither8x8(green, px, py, intensity);
    pixel.b = orderedDither8x8(blue, px, py, intensity);
    }
    
    if (int(palette) == 0) { return vec4(binary(vec3(pixel.r, pixel.g, pixel.b)), pixel.a);                 }
    if (int(palette) == 1) { return vec4(commodore64(vec3(pixel.r, pixel.g, pixel.b)),         pixel.a); }
    if (int(palette) == 2) { return vec4(vic20(vec3(pixel.r, pixel.g, pixel.b)), pixel.a); }
    if (int(palette) == 3) { return vec4(appleII(vec3(pixel.r, pixel.g, pixel.b)), pixel.a); }
    if (int(palette) == 4) { return vec4(zxSpectrumBright(vec3(pixel.r, pixel.g, pixel.b)), pixel.a); }
    if (int(palette) == 5) { return vec4(zxSpectrumDim(vec3(pixel.r, pixel.g, pixel.b)), pixel.a); }
    
    return pixel;
    }
    

标签: swiftgpucifilterdithering

解决方案


Floyd-Steinberg 抖动的问题在于它是一种串行算法——结果像素的颜色值取决于先前计算的像素。Core Image(以及任何类型的 SIMD 并行化技术)不太适合这类问题。它们旨在同时对所有像素执行相同的任务。

但是,我发现了一些方法可以部分并行化GPU 上独立像素的计算,甚至是一种有趣的CPU-GPU-hybrid 方法

不幸的是,Core Image 可能不是实现这些技术的最佳框架,因为CIFilters它们可以利用的 GPU 资源有限(例如,无法访问全局内存)。您可以直接使用 Metal 计算着色器(而不是通过 Core Image),但这需要更多的支持代码。

如果您不一定需要误差扩散,您仍然可以使用有序抖动(可以高度并行化)来获得类似的结果。我还找到了一篇很好的文章。内置CIDither过滤器可能也在使用这种方法。


推荐阅读