如何将MLMultiArray/CGImage导入PKCanvasView供用户编辑?
解决PKCanvasView用MLMultiArray/CGImage初始化可编辑绘图的问题
先处理你遇到的直接错误
你调用PKDrawing.init(from: dataOut)报错,是因为这个方法的参数要求是Decoder类型(比如JSONDecoder),而非直接传入原始二进制Data。更关键的是,你的dataOut是MLMultiArray的原始数值数据,根本不是PKDrawing的归档数据,所以这个方法从一开始就不适用,得换实现思路。
正确的实现思路
你的核心需求是把语义分割的掩码(0/1的MLMultiArray)转换成PKCanvasView上可编辑的内容,以下是两种可行方案:
方案1:将掩码转为CGImage作为画布背景
把MLMultiArray转换成带高亮/透明效果的CGImage,设置为PKCanvasView的背景,用户可以在背景上直接绘制修正掩码。
代码示例:
import SwiftUI import PencilKit struct PencilView: UIViewRepresentable { var inputImage: UIImage var rawArray: MLMultiArray var colorModel: ColorModel @Binding var drawing: PKDrawing func makeUIView(context: Context) -> PKCanvasView { let canvasView = PKCanvasView() canvasView.drawingPolicy = .pencilOnly canvasView.tool = PKInkingTool(.pen, color: UIColor(colorModel.overlayColor), width: 15) // 将MLMultiArray转换为掩码CGImage并设置为背景 if let maskImage = convertMLMultiArrayToMaskImage() { canvasView.backgroundColor = .clear let imageView = UIImageView(image: maskImage) imageView.frame = canvasView.bounds imageView.contentMode = .scaleAspectFit canvasView.addSubview(imageView) canvasView.sendSubviewToBack(imageView) } canvasView.drawing = drawing return canvasView } func updateUIView(_ uiView: PKCanvasView, context: Context) { uiView.drawing = drawing } // MLMultiArray转掩码CGImage核心方法 private func convertMLMultiArrayToMaskImage() -> UIImage? { guard let cgImage = inputImage.cgImage else { return nil } let width = cgImage.width let height = cgImage.height guard rawArray.count == width * height else { return nil } let dblPtr = rawArray.dataPointer.bindMemory(to: Double.self, capacity: width * height) let buffer = UnsafeBufferPointer(start: dblPtr, count: width * height) guard let colorSpace = CGColorSpace(name: CGColorSpace.sRGB) else { return nil } let bytesPerPixel = 4 let bytesPerRow = width * bytesPerPixel let bitsPerComponent = 8 guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue) else { return nil } // 遍历像素,为1的区域设置半透明高亮色,0的区域透明 for y in 0..<height { for x in 0..<width { let index = y * width + x let value = buffer[index] let color = value == 1 ? UIColor(colorModel.overlayColor).withAlphaComponent(0.5) : UIColor.clear var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 color.getRed(&red, green: &green, blue: &blue, alpha: &alpha) let pixel: [UInt8] = [ UInt8(alpha * 255), UInt8(red * 255), UInt8(green * 255), UInt8(blue * 255) ] context.data?.advanced(by: y * bytesPerRow + x * bytesPerPixel).copyMemory(from: pixel, count: bytesPerPixel) } } guard let maskCGImage = context.makeImage() else { return nil } return UIImage(cgImage: maskCGImage) } } // 假设你的ColorModel定义 struct ColorModel { let overlayColor: Color }
方案2:将掩码区域转为PKStroke添加到PKDrawing
如果需要让掩码本身成为可编辑的笔触(用户可直接擦除/修改掩码笔触),可以把MLMultiArray中1的区域转换成PKStroke,添加到PKDrawing中。
简化代码示例(实际需优化轮廓提取逻辑):
private func convertMLMultiArrayToPKDrawing() -> PKDrawing { guard let cgImage = inputImage.cgImage else { return PKDrawing() } let width = cgImage.width let height = cgImage.height guard rawArray.count == width * height else { return PKDrawing() } let dblPtr = rawArray.dataPointer.bindMemory(to: Double.self, capacity: width * height) let buffer = UnsafeBufferPointer(start: dblPtr, count: width * height) var strokes = [PKStroke]() let tool = PKInkingTool(.pen, color: UIColor(colorModel.overlayColor), width: 1) // 简化处理:为每个1的像素生成微小笔触(建议替换为轮廓提取逻辑提升性能) for y in 0..<height { for x in 0..<width { let index = y * width + x if buffer[index] == 1 { let point = CGPoint(x: x, y: y) let path = PKStrokePath(controlPoints: [PKStrokePoint(location: point, timeOffset: 0, size: CGSize(width: 1, height: 1), opacity: 1, force: 1, azimuth: 0, altitude: 0)], creationDate: Date()) let stroke = PKStroke(tool: tool, path: path) strokes.append(stroke) } } } return PKDrawing(strokes: strokes) }
在makeUIView中调用该方法:
canvasView.drawing = convertMLMultiArrayToPKDrawing()
注意事项
- 不要直接绑定
PKCanvasView实例到@Binding,应绑定PKDrawing对象,UIViewRepresentable更适合通过drawing绑定同步编辑状态。 - 方案1适合作为背景参考,用户在其上绘制修正;方案2让掩码成为可编辑笔触,但大量小笔触会影响性能,建议使用Core Graphics的轮廓检测优化。
内容的提问来源于stack exchange,提问作者stuvx
相关产品推荐
相关产品推荐

