美文网首页
Accelerate框架详细解析(三) —— 基于Acceler

Accelerate框架详细解析(三) —— 基于Acceler

作者: 刀客传奇 | 来源:发表于2021-03-14 17:55 被阅读0次

    版本记录

    版本号 时间
    V1.0 2021.03.14 星期日

    前言

    Accelerate框架进行大规模的数学计算和图像计算,针对高性能进行优化。接下来几篇我们就一起看一下这个框架。感兴趣的可以看上面几篇。
    1. Accelerate框架详细解析(一) —— 基本概览(一)
    2. Accelerate框架详细解析(二) —— 基于Accelerate 和 vImage的SwiftUI程序图像处理(一)

    源码

    1. Swift

    首先看下工程组织结构

    下面就是源码了

    1. AppMain.swift
    
    import SwiftUI
    
    @main
    struct AppMain: App {
        var body: some Scene {
            WindowGroup {
                ContentView()
            }
        }
    }
    
    2. ContentView.swift
    
    import SwiftUI
    import Accelerate
    
    struct ContentView: View {
        @State private var originalImage = UIImage()
        @State private var originalHistogram: HistogramLevels?
        @State private var showImagePicker = false
        @State private var processedImage: UIImage?
        @State private var processedHistogram: HistogramLevels?
    
        var body: some View {
            ScrollView {
                VStack {
                    Button(
                        action: {
                            showImagePicker = true
                        }, label: {
                            Label("Select Photo", systemImage: "photo")
                        })
                        .sheet(
                            isPresented: $showImagePicker,
                            onDismiss: {
                                let imageWrapper = VImageWrapper(uiImage: originalImage)
                                processedImage = nil
                                processedHistogram = nil
                                originalHistogram = imageWrapper.getHistogram(.original)
                            }, content: {
                                ImagePicker(
                                    selectedImage: $originalImage,
                                    showSheet: $showImagePicker
                                )
                            }
                        )
                    ImageView(
                        title: "Original",
                        image: originalImage,
                        histogram: originalHistogram
                    )
                    HStack {
                        Button("Equalize Histogram") {
                            var imageWrapper = VImageWrapper(uiImage: originalImage)
                            imageWrapper.equalizeHistogram()
                            processedImage = imageWrapper.processedImage
                            processedHistogram = imageWrapper.getHistogram(.processed)
                        }
                        Spacer()
                        Button("Reflect") {
                            var imageWrapper = VImageWrapper(uiImage: originalImage)
                            imageWrapper.reflectImage()
                            processedImage = imageWrapper.processedImage
                            processedHistogram = imageWrapper.getHistogram(.processed)
                        }
                    }.disabled(originalImage.cgImage == nil)
                    if let image = processedImage {
                        ImageView(
                            title: "Processed",
                            image: image,
                            histogram: processedHistogram
                        ).padding(3)
                    }
                }.padding()
            }
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    
    3. HistogramView.swift
    
    import SwiftUI
    
    struct HistogramLevels {
        var red: [UInt]
        var green: [UInt]
        var blue: [UInt]
        var alpha: [UInt]
    }
    
    struct HistogramLine: View {
        var channel: [UInt]
        var color: Color
        var proxy: GeometryProxy
        var maxValue: UInt
    
        func xForBin(_ bin: Int, proxy: GeometryProxy) -> CGFloat {
            let widthOfBin = proxy.size.width / CGFloat(channel.count)
            return CGFloat(bin) * widthOfBin
        }
    
        func yForCount(_ count: UInt, proxy: GeometryProxy) -> CGFloat {
            let heightOfLevel = proxy.size.height / CGFloat(maxValue)
            return proxy.size.height - CGFloat(count) * heightOfLevel
        }
    
        var body: some View {
            Path { path in
                for bin in 0..<channel.count {
                    let newPoint = CGPoint(
                        x: xForBin(bin, proxy: proxy),
                        y: yForCount(channel[bin], proxy: proxy)
                    )
                    if bin == 0 {
                        path.move(to: newPoint)
                    } else {
                        path.addLine(to: newPoint)
                    }
                }
            }.stroke(color)
        }
    }
    
    struct HistogramView: View {
        var histogram: HistogramLevels
    
        var binCount: Int {
            histogram.red.count
        }
    
        var body: some View {
            GeometryReader { proxy in
                HistogramLine(
                    channel: histogram.red,
                    color: Color.red,
                    proxy: proxy,
                    maxValue: histogram.red.max() ?? 1
                )
                HistogramLine(
                    channel: histogram.green,
                    color: Color.green,
                    proxy: proxy,
                    maxValue: histogram.green.max() ?? 1
                )
                HistogramLine(
                    channel: histogram.blue,
                    color: Color.blue,
                    proxy: proxy,
                    maxValue: histogram.blue.max() ?? 1
                )
            }
        }
    }
    
    struct HistogramView_Previews: PreviewProvider {
        static var previews: some View {
            HistogramView(
                histogram: HistogramLevels(
                    red: [0, 1, 2, 3, 4],
                    green: [3, 4, 4, 6, 7],
                    blue: [10, 9, 8, 7, 6],
                    alpha: [2, 8, 4, 6, 5]
                )
            )
            .frame(width: 375, height: 275)
            .border(Color.gray)
        }
    }
    
    4. ImagePicker.swift
    
    import SwiftUI
    
    struct ImagePicker: UIViewControllerRepresentable {
        var sourceType: UIImagePickerController.SourceType = .photoLibrary
        @Binding var selectedImage: UIImage
        @Binding var showSheet: Bool
    
        func makeUIViewController(
            context: UIViewControllerRepresentableContext<ImagePicker>
        ) -> UIImagePickerController {
            let imagePicker = UIImagePickerController()
            imagePicker.allowsEditing = false
            imagePicker.sourceType = sourceType
            imagePicker.delegate = context.coordinator
            return imagePicker
        }
    
        func updateUIViewController(
            _ uiViewController: UIImagePickerController,
            context: UIViewControllerRepresentableContext<ImagePicker>
        ) {
        }
    
        func makeCoordinator() -> ImagePickerCoordinator {
            Coordinator(self)
        }
    }
    
    class ImagePickerCoordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        var parent: ImagePicker
    
        init(_ parent: ImagePicker) {
            self.parent = parent
        }
    
        func imagePickerController(
            _ picker: UIImagePickerController,
            didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]
        ) {
            if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
                parent.selectedImage = image
                parent.showSheet = false
            }
        }
    }
    
    5. ImageView.swift
    
    import SwiftUI
    
    struct ImageView: View {
        var title: String
        var image: UIImage
        var histogram: HistogramLevels?
        @State private var showHistogram = false
    
        var body: some View {
            Text(title)
            ZStack(alignment: .bottomTrailing) {
                Image(uiImage: image)
                    .resizable()
                    .frame(width: 355, height: 250)
                    .scaledToFit()
                    .border(Color.black)
                    .onTapGesture {
                        showHistogram.toggle()
                    }
                if let histogram = histogram {
                    if showHistogram {
                        HistogramView(histogram: histogram)
                            .background(Color.white)
                            .frame(width: 150, height: 113)
                            .padding(5)
                    }
                }
            }
        }
    }
    
    6. VImageWrapper.swift
    
    import UIKit
    import Accelerate
    
    enum WrappedImage {
        case original
        case processed
    }
    
    struct VImageWrapper {
        var uiImage: UIImage
        var processedImage: UIImage?
        let vNoFlags = vImage_Flags(kvImageNoFlags)
    
      init(uiImage: UIImage) {
        self.uiImage = uiImage
        if let buffer = createVImage(image: uiImage),
          let converted = convertToUIImage(buffer: buffer) {
            processedImage = converted
        }
      }
    
        func createVImage(image: UIImage) -> vImage_Buffer? {
            guard
                let cgImage = uiImage.cgImage,
                let imageBuffer = try? vImage_Buffer(cgImage: cgImage)
            else {
                return nil
            }
            return imageBuffer
        }
    
        func convertToUIImage(buffer: vImage_Buffer) -> UIImage? {
            guard
                let originalCgImage = uiImage.cgImage,
                let format = vImage_CGImageFormat(cgImage: originalCgImage),
                let cgImage = try? buffer.createCGImage(format: format)
            else {
                return nil
            }
    
            let image = UIImage(
                cgImage: cgImage,
                scale: 1.0,
                orientation: uiImage.imageOrientation)
            return image
        }
    
      mutating func equalizeHistogram() {
        guard
          let image = uiImage.cgImage,
          var imageBuffer = createVImage(image: uiImage),
          var destinationBuffer = try? vImage_Buffer(
            width: image.width,
            height: image.height,
            bitsPerPixel: UInt32(image.bitsPerPixel))
        else {
          print("Error creating image buffers.")
          processedImage = nil
          return
        }
        defer {
          imageBuffer.free()
          destinationBuffer.free()
        }
    
        let error = vImageEqualization_ARGB8888(
          &imageBuffer,
          &destinationBuffer,
          vNoFlags)
    
        guard error == kvImageNoError else {
          printVImageError(error: error)
          processedImage = nil
          return
        }
        processedImage = convertToUIImage(buffer: destinationBuffer)
      }
    
      mutating func reflectImage() {
        guard
          let image = uiImage.cgImage,
          var imageBuffer = createVImage(image: uiImage),
          var destinationBuffer = try? vImage_Buffer(
            width: image.width,
            height: image.height,
            bitsPerPixel: UInt32(image.bitsPerPixel))
        else {
          print("Error creating image buffers.")
          processedImage = nil
          return
        }
        defer {
          imageBuffer.free()
          destinationBuffer.free()
        }
    
        let error = vImageHorizontalReflect_ARGB8888(
          &imageBuffer,
          &destinationBuffer,
          vNoFlags)
    
        guard error == kvImageNoError else {
          printVImageError(error: error)
          processedImage = nil
          return
        }
        processedImage = convertToUIImage(buffer: destinationBuffer)
      }
    
        func getHistogram(_ image: WrappedImage) -> HistogramLevels? {
            guard
                let cgImage = image == .original ? uiImage.cgImage : processedImage?.cgImage,
                var imageBuffer = try? vImage_Buffer(cgImage: cgImage)
            else {
                return nil
            }
            defer {
                imageBuffer.free()
            }
    
            var redArray: [vImagePixelCount] = Array(repeating: 0, count: 256)
            var greenArray: [vImagePixelCount] = Array(repeating: 0, count: 256)
            var blueArray: [vImagePixelCount] = Array(repeating: 0, count: 256)
            var alphaArray: [vImagePixelCount] = Array(repeating: 0, count: 256)
            var error: vImage_Error = kvImageNoError
    
            redArray.withUnsafeMutableBufferPointer { rPointer in
                greenArray.withUnsafeMutableBufferPointer { gPointer in
                    blueArray.withUnsafeMutableBufferPointer { bPointer in
                        alphaArray.withUnsafeMutableBufferPointer { aPointer in
                            var histogram = [
                                rPointer.baseAddress, gPointer.baseAddress,
                                bPointer.baseAddress, aPointer.baseAddress
                            ]
                            histogram.withUnsafeMutableBufferPointer { hPointer in
                                if let hBaseAddress = hPointer.baseAddress {
                                    error = vImageHistogramCalculation_ARGB8888(
                                        &imageBuffer,
                                        hBaseAddress,
                                        vNoFlags
                                    )
                                }
                            }
                        }
                    }
                }
            }
    
            guard error == kvImageNoError else {
                printVImageError(error: error)
                return nil
            }
            let histogramData = HistogramLevels(
                red: redArray,
                green: greenArray,
                blue: blueArray,
                alpha: alphaArray
            )
            return histogramData
        }
    }
    
    extension VImageWrapper {
        func printVImageError(error: vImage_Error) {
            let errDescription = vImage.Error(vImageError: error).localizedDescription
            print("vImage Error: \(errDescription)")
        }
    }
    

    后记

    本篇主要讲述了基于AcceleratevImageSwiftUI程序图像处理,感兴趣的给个赞或者关注~~~

    相关文章

      网友评论

          本文标题:Accelerate框架详细解析(三) —— 基于Acceler

          本文链接:https://www.haomeiwen.com/subject/xexccltx.html