图片 brightness/contrast - Xamarin iOS

Image brightness/contrast - Xamarin iOS

Xamarin 提供了一些示例代码,用于对 iOS 中的图像进行简单调整:

https://github.com/xamarin/recipes/blob/master/ios/media/coreimage/adjust_contrast_and_brightness_of_an_image/color_controls_pro/ImageViewController.cs

此代码仅在用户松开滑块旋钮时更新图像 - 而不是我们通常期望的连续更新。

但是,当我进行以下更改时,我可靠地在硬件上收到 SIGSEGV 故障。

    //sliderC.TouchUpInside += HandleValueChanged;
    //sliderS.TouchUpInside += HandleValueChanged;
    //sliderB.TouchUpInside += HandleValueChanged;

    sliderC.ValueChanged += HandleValueChanged;
    sliderS.ValueChanged += HandleValueChanged;
    sliderB.ValueChanged += HandleValueChanged;

我希望这是 "overloading" 某种程度上的代码。您将如何实施避免此问题的图像调整?是否有较低级别的方法,或者其他应用程序是否只是使用图像的低分辨率版本进行调整?

这是我做的一个快速版本,它是 'more' 实时的(这是来自 sim 的记录,设备 (6s) 是平滑的,具体取决于初始图像大小。

从模板创建一个 viewcontroller iOS 应用程序,并向故事板添加一个 UIImageView 和三个滑块,使其看起来像动画 gif。

我创建了一个简单的 class 来存储 ColorCtrl 值(亮度、对比度、饱和度:

public class ColorCtrl
{
    public float s;
    public float b;
    public float c;
}

ViewDidLoad 方法,做一些设置:

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();

    string filePath = Path.Combine (NSBundle.MainBundle.BundlePath, "hero.jpg");
    originalImage = new CIImage (new NSUrl (filePath, false));

    colorCtrls = new CIColorControls ();
    colorCtrls.Image = originalImage;

    // Create the context only once, and re-use it
    var contextOptions = new CIContextOptions ();
    contextOptions.UseSoftwareRenderer = false; // gpu vs. cpu
    // On save of the image, create a new context with highqual attributes and re-apply the filter... 
    contextOptions.HighQualityDownsample = false;
    contextOptions.PriorityRequestLow = false; // high queue order it
    contextOptions.CIImageFormat = (int)CIFormat.ARGB8; // use 32bpp, vs. 64|128bpp 
    context = CIContext.FromOptions (contextOptions);
}

然后是三个滑块的更改处理程序。

在那些我 'hack' 忙标志中,以便在最后一次转换未完成时跳过图像转换。如果我们不忙,那么对我们的异步转换方法进行等待调用。

注意:我说'hack',我是认真的,以最佳实践方式,这应该将转换请求泵送到队列,队列处理程序会总结所有队列中的未决项目,刷新它们并进行转换。

注意:我将 "async" 添加到生成的事件处理程序中,因此我可以 await 图像转换。

注意:除了分配的值外,三个滑块的处理程序都是相同的; colorCtrlV.b | colorCtrlV.s | colorCtrlV.c

注意:您可以在用户按下时对大图像进行下采样,对其执行变换,然后在触摸时变换原始全尺寸图像...

async partial void brightnessChange (UISlider sender)
{
    if (!busy) {
        busy = true;
        colorCtrlV.b = sender.Value;
        this.imageView.Image = await FilterImageAsync (colorCtrlV);
        busy = false;
    }
}

好的,现在进行实际转换,一个相当简单的 Run.Task,这样我们就可以从主线程中完成这项工作而不阻塞 UI。这样滑块就不会在用户滑动手指时卡顿,但是由于滑块处理程序中的 'busy' flag/hack 我们可能会跳过其中一些事件(我们 应该 为触摸拖动退出添加一个处理程序,以便我们处理 'last' 用户请求的值....)

// async Task.Run() - not best practice - just a demo
async Task<UIImage> FilterImageAsync (ColorCtrl value)
{
    if (transformImage == null)
        transformImage = new Func<UIImage>(() => {
            colorCtrls.Brightness = colorCtrlV.b;
            colorCtrls.Saturation = colorCtrlV.s;
            colorCtrls.Contrast = colorCtrlV.c;
            var output = colorCtrls.OutputImage;
            var cgImage = context.CreateCGImage (output, originalImage.Extent);
            var filteredImage = new UIImage (cgImage);
            return filteredImage;
        });
    UIImage image = await Task.Run<UIImage>(transformImage);
    return image;
}

个人 对于这种类型的实时图像转换,我更喜欢使用 GPUImage 通过 OpenGL-ES 进行,因为屏幕在 60z 刷新率下的交互同样流畅作为黄油,但它比使用 CoreImage 过滤器要多得多。