版本记录
版本号 | 时间 |
---|---|
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)")
}
}
后记
本篇主要讲述了基于
Accelerate
和vImage
的SwiftUI
程序图像处理,感兴趣的给个赞或者关注~~~