Переписать Android OpenGL фильтр на Metal (для CIFilter) - PullRequest
0 голосов
/ 28 января 2019

Для GLSL (ES) для Android-версии нашего приложения написаны десятки фильтров изображений.Начиная с iOS 12, OpenGL устарел, и ядра CIFilter должны быть написаны на металле.

У меня был некоторый предыдущий опыт работы с OpenGL, однако написание ядер CIFilter на металле было для меня новым.

Вот один из фильтров.Не могли бы вы помочь мне перевести его на Metal как ядро ​​CIFilter?Это послужило бы хорошим примером для меня, чтобы я мог переводить других.

#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 vTextureCoord;
uniform samplerExternalOES sTexture;
uniform float texelWidth;
uniform float texelHeight;
uniform float intensivity;
void main() {
    float SIZE = 1.25 + (intensivity / 100.0)*2.0;
    vec4 color;
    float min = 1.0;
    float max = 0.0;
    float val = 0.0;
    for (float x = -SIZE; x < SIZE; x++) {
        for (float y = -SIZE; y < SIZE; y++) {
            color = texture2D(sTexture, vTextureCoord + vec2(x * texelWidth, y * texelHeight));
            val = (color.r + color.g + color.b) / 3.;
            if (val > max) { max = val; } else if (val < min) { min = val; }
        }
    }
    float range = 5. * (max - min);
    gl_FragColor = vec4(pow(1. - range, SIZE * 1.5));
    gl_FragColor = vec4((gl_FragColor.r + gl_FragColor.g + gl_FragColor.b) / 3. > 0.75 ? vec3(1.) : gl_FragColor.rgb, 1.);
}

1 Ответ

0 голосов
/ 01 февраля 2019

Вот источник Metal для ядра, которое пытается воспроизвести описанный вами фильтр:

#include <metal_stdlib>
#include <CoreImage/CoreImage.h>

using namespace metal;

extern "C" {
namespace coreimage {

float4 sketch(sampler src, float texelWidth, float texelHeight, float intensity40) {
    float size = 1.25f + (intensity40 / 100.0f) * 2.0f;

    float minVal = 1.0f;
    float maxVal = 0.0f;
    for (float x = -size; x < size; ++x) {
        for (float y = -size; y < size; ++y) {
            float4 color = src.sample(src.coord() + float2(x * texelWidth, y * texelHeight));
            float val = (color.r + color.g + color.b) / 3.0f;
            if (val > maxVal) {
                maxVal = val;
            } else if (val < minVal) {
                minVal = val;
            }
        }
    }

    float range = 5.0f * (maxVal - minVal);

    float4 outColor(pow(1.0f - range, size * 1.5f));
    outColor = float4((outColor.r + outColor.g + outColor.b) / 3.0f > 0.75f ? float3(1.0f) : outColor.rgb, 1.0f);
    return outColor;
}

}
}

Я предполагаю, что вы уже знакомы с основами того, как правильно создавать металлические шейдерыв библиотеку, которая может быть загружена Core Image.

Вы можете создать свое ядро ​​во время выполнения, загрузив библиотеку Metal по умолчанию и запросив функцию "sketch" (имя произвольное, если оно соответствует ядру).source):

NSURL *libraryURL = [NSBundle.mainBundle URLForResource:@"default" withExtension:@"metallib"];
NSData *libraryData = [NSData dataWithContentsOfURL:libraryURL];

NSError *error;
CIKernel *kernel = [CIKernel kernelWithFunctionName:@"sketch" fromMetalLibraryData:libraryData error:&error];

Затем вы можете применить это ядро ​​к изображению, обернув его в свой собственный CIFilter подкласс, или просто вызвать его напрямую:

CIImage *outputImage = [kernel applyWithExtent:CGRectMake(0, 0, width, height)
                                   roiCallback:^CGRect(int index, CGRect destRect)
                        { return destRect; }
                                     arguments:@[inputImage, @(1.0f/width), @(1.0f/height), @(60.0f)]];

У меня естьпытался выбрать разумные значения по умолчанию для каждого из аргументов (первый из которых должен быть экземпляром CIImage), но, конечно, их можно настроить по вкусу.

...