Performance 核心图片:在真实的iPhone中,通过滑块改变亮度时,照片效果反应/变化缓慢

Performance 核心图片:在真实的iPhone中,通过滑块改变亮度时,照片效果反应/变化缓慢,performance,swiftui,core-image,Performance,Swiftui,Core Image,下面的代码应用一个图像选择器并在contentView中显示过滤后的图像,在本例中我使用了SepiaTone效果。当我在滑块中移动时,更改过滤器强度。照片反应延迟,滑动不流畅 但当使用AppStore中的一些应用程序(如Afterlight)及其照片过滤器时,当用户移动滑块时,照片效果的变化是平稳的 那么有什么不同呢,这些应用程序使用的是其他框架吗?如果我们仍然使用核心图像,我们如何提高连续变化的过滤器强度的性能 图工具 内容视图 它有助于在后台线程中执行图像过滤,以避免图像处理阻塞主队列。此外

下面的代码应用一个图像选择器并在contentView中显示过滤后的图像,在本例中我使用了SepiaTone效果。当我在滑块中移动时,更改过滤器强度。照片反应延迟,滑动不流畅

但当使用AppStore中的一些应用程序(如Afterlight)及其照片过滤器时,当用户移动滑块时,照片效果的变化是平稳的

那么有什么不同呢,这些应用程序使用的是其他框架吗?如果我们仍然使用核心图像,我们如何提高连续变化的过滤器强度的性能

图工具 内容视图
它有助于在后台线程中执行图像过滤,以避免图像处理阻塞主队列。此外,不要对每一个变化都做出反应,而是对变化进行去抖动处理是有意义的:在实际计算完成之前,等待一小段时间,比如50毫秒的无变化时间——如果过滤器参数经常在短时间内变化,例如当用户拖动滑块时,这可以防止大量图像计算发生。这两种方法都可以使用Combine框架实现,下面是一个基于代码的示例。该效果在单独的类ImageEffect中分离,该类使用Combine在输入图像/过滤器强度更改时在后台执行图像处理,并使用以下选项进行优化:

导入CoreImage 导入CoreImage.CIFilterBuiltins 导入快捷键 进口联合收割机 类ImageEffect:ObservableObject{ @已发布的变量筛选强度=0.5 @已发布的var inputImage:UIImage? @已发布的var输出图像:UIImage? @已发布的var currentFilter=CIFilter.sepiaTone 让context=CIContext var订阅=设置 let queue=DispatchQueuelabel:图像处理 初始化{ self.$inputImage .map{inputImage->CIImage?在 guard let inputImage=inputImage else{return nil} 返回CIImageimage:inputImage } .CombineRelatestSelf.$filterIntensity .debouncefor:。毫秒50,计划程序:队列 .map{inputImage,filteringtensitity->UIImage?在 guard let inputImage=inputImage else{return nil} self.currentFilter.inputImage=inputImage self.currentFilter.intensity=FloatfilterIntensity guard let outputImage=self.currentFilter.outputImage else{return nil} guard let cgImage=self.context.createCGImageoutputImage,from:outputImage.extent-else{return nil} 返回UIImagecgImage:cgImage } .receiveon:RunLoop.main .sink{image in self.outputImage=图像 } .storein:&self.subscriptions } } 结构ImageEffectView:视图{ @观察对象变量imageEffect=imageEffect @状态私有变量showingImagePicker=false var body:一些观点{ 导航视图{ VStack{ ZStack{ 长方形 .fillColor.secondary 如果imageEffect.outputImage!=nil{ ImageuiImage:imageEffect.outputImage! .可调整大小 .scaledofit }否则{ text点击以选择图片 .foregroundColor.white .font.headline } } .ontapsigne{ self.showingImagePicker=true } HStack{ 文本强度 Slidervalue:$imageEffect.filterIntensity } .垂直 HStack{ 按钮改变过滤器{ //更换过滤器 } 间隔棒 纽扣{ //保存图片 } } } .填充[.水平,.底部] .导航BartitleInstafilter .Sheets显示:$showingImagePicker{ ImagePickServiceImage:self.$imageEffect.inputImage } } } }
谢谢你的回复。我会试试的,很快会给你回复!抱歉耽搁了。我已经应用了你的代码,现在滑块的移动比以前更平稳,不再有滞后。很高兴知道您在这里使用Combine,我曾经使用Combine处理一些JSON API请求。我可以看到ImageEffect中的inputImage与ImagePicker的image连接/绑定,所以它们将一起更改。而去抖动会延迟50毫秒的图像处理,然后滑块移动,滤光片强度的变化效果不会立即生效。谢谢,我在这里学到了一个新东西。为了更好的用户体验,我想要
强度变化立即生效。但是如果我去掉延迟的去抖动,那么滑块的运动就会像以前一样回到滞后状态。相反,我可以看到像AfterLight这样的应用程序可以处理滑块的快速变化,例如,即使我一次又一次地快速向左然后向右滑动。滤镜强度效果立即反映在显示的图像上,非常好的性能和用户体验,我想知道他们使用的是什么技术/框架?50毫秒几乎不明显,间隔可能更短。你也可以通过在图像上使用贴图操作将图像缩小到屏幕大小一次,然后将图像效果应用到该图像来更快。是的,也许我可以让滑块连续发送更改,我发现当滑块移动完成时会应用图像过滤效果。因此,它可能只是在用户移动完成后才这样做。不确定默认情况下滑块将如何发送值更改,但我也将研究它。
import SwiftUI

struct ImagePicker: UIViewControllerRepresentable {

    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        let parent: ImagePicker

        init(_ parent: ImagePicker) {
            self.parent = parent
        }

        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            if let uiImage = info[.originalImage] as? UIImage {
                parent.image = uiImage
            }

            picker.dismiss(animated: true, completion: nil)
        }
    }

    @Binding var image: UIImage?

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        return picker
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {

    }
}
import CoreImage
import CoreImage.CIFilterBuiltins
import SwiftUI

struct ContentView: View {

    @State private var image: Image?
    @State private var filterIntensity = 0.5

    @State private var showingImagePicker = false
    @State private var inputImage: UIImage?

    @State private var currentFilter = CIFilter.sepiaTone()
    let context = CIContext()

    var body: some View {
        let intensity = Binding<Double>(
            get: {
                self.filterIntensity
        },
            set: {
                self.filterIntensity = $0
                self.applyProcessing()
        })

        return NavigationView {
            VStack {
                ZStack {
                    Rectangle()
                        .fill(Color.secondary)

                    if image != nil {
                        image?
                            .resizable()
                            .scaledToFit()
                    } else {
                        Text("Tap to select a picture")
                            .foregroundColor(.white)
                            .font(.headline)
                    }
                }
                .onTapGesture {
                    self.showingImagePicker = true
                }

                HStack {
                    Text("Intensity")
                    Slider(value: intensity)
                }
                .padding(.vertical)

                HStack {
                    Button("Change Filter") {
                        // change filter
                    }

                    Spacer()

                    Button("Save") {
                        // save the picture
                    }
                }
            }
            .padding([.horizontal, .bottom])
            .navigationBarTitle("Instafilter")
            .sheet(isPresented: $showingImagePicker, onDismiss: loadImage) {
                ImagePicker(image: self.$inputImage)
            }
        }
    }

    func loadImage() {
        guard let inputImage = inputImage else { return }

        let beginImage = CIImage(image: inputImage)
        currentFilter.setValue(beginImage, forKey: kCIInputImageKey)
        applyProcessing()
    }

    func applyProcessing() {
        currentFilter.intensity = Float(filterIntensity)
        guard let outputImage = currentFilter.outputImage else { return }

        if let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
            let uiImage = UIImage(cgImage: cgImage)
            image = Image(uiImage: uiImage)
        }
    }
}