Hier ist ein Bild des Problems, das ich sehe:

Hier ist mein Code für die Verarbeitung der endgültigen Vorschau:
Code: Select all
func processFrameForFinalImage(
pixelBuffer: CVPixelBuffer,
orientation: CGImagePropertyOrientation,
quality: VNGeneratePersonSegmentationRequest.QualityLevel,
showInPreview: Bool,
completion: @escaping (UIImage?) ->  Void
) {
let request = VNGeneratePersonSegmentationRequest()
request.qualityLevel = quality
request.outputPixelFormat = kCVPixelFormatType_OneComponent8
let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer,
orientation: .up,
options: [:])
DispatchQueue.global(qos: .userInitiated).async {
do {
try handler.perform([request])
guard let maskBuffer = request.results?.first?.pixelBuffer else {
DispatchQueue.main.async { completion(nil) }
return
}
// Convert buffers to CIImage and apply consistent orientation
let cameraImage = CIImage(cvPixelBuffer: pixelBuffer).oriented(orientation)
var maskImage = CIImage(cvPixelBuffer: maskBuffer).oriented(orientation)
maskImage = maskImage.applyingFilter("CIGaussianBlur", parameters: [
kCIInputRadiusKey: 2.0
])
if let erode = CIFilter(name: "CIMorphologyMinimum",
parameters: [kCIInputImageKey: maskImage,
"inputRadius": 1.0]),
let output = erode.outputImage {
maskImage = output
}
// Increase contrast to push mask values toward 0 or 1
if let controls = CIFilter(name: "CIColorControls") {
controls.setValue(maskImage, forKey: kCIInputImageKey)
controls.setValue(1.2, forKey: kCIInputContrastKey)
controls.setValue(0.0, forKey: kCIInputSaturationKey)
if let output = controls.outputImage {
maskImage = output
}
}
// Clamp to [0, 1]
if let clamp = CIFilter(name: "CIColorClamp") {
clamp.setValue(maskImage, forKey: kCIInputImageKey)
clamp.setValue(CIVector(x: 0, y: 0, z: 0, w: 0), forKey: "inputMinComponents")
clamp.setValue(CIVector(x: 1, y: 1, z: 1, w: 1), forKey: "inputMaxComponents")
if let output = clamp.outputImage {
maskImage = output
}
}
var composited = cameraImage
if self.isBackgroundReplacementEnabled,
let bgImage = self.backgroundImage {
// Scale background to match camera image size
var bg = bgImage.transformed(by: CGAffineTransform(
scaleX: cameraImage.extent.width / bgImage.extent.width,
y: cameraImage.extent.height / bgImage.extent.height
))
// Align background origin
let dx = cameraImage.extent.origin.x - bg.extent.origin.x
let dy = cameraImage.extent.origin.y - bg.extent.origin.y
bg = bg.transformed(by: CGAffineTransform(translationX: dx, y: dy))
// Blend the person (camera) over background using mask
composited = cameraImage.applyingFilter("CIBlendWithMask", parameters: [
kCIInputBackgroundImageKey: bg,
kCIInputMaskImageKey: maskImage
])
}
// Prepare for preview output
let finalImage = self.aspectFillImage(composited, targetSize: self.previewLayer.bounds.size)
guard let cgImage = self.ciContext.createCGImage(finalImage, from: finalImage.extent) else {
DispatchQueue.main.async { completion(nil) }
return
}
let uiImage = UIImage(cgImage: cgImage)
DispatchQueue.main.async {
if showInPreview {
self.previewLayer.contents = cgImage
}
completion(uiImage)
}
} catch {
print("Segmentation error: \(error)")
DispatchQueue.main.async { completion(nil) }
}
}
}
Code: Select all
func setupCamera() {
session.beginConfiguration()
session.sessionPreset = .medium
guard let device = AVCaptureDevice.default(.builtInWideAngleCamera, for:  .video, position: .front),
let input = try? AVCaptureDeviceInput(device: device) else {
print("⚠️ Could not create camera input")
return
}
if session.canAddInput(input) {
session.addInput(input)
}
let output = AVCaptureVideoDataOutput()
output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "camera.frame.queue"))
if session.canAddOutput(output) {
session.addOutput(output)
}
session.commitConfiguration()
}
Eine Sache, die mir auffällt, ist, dass ich mit VNImageRequestHandler nur mit der Ausrichtung nach oben gute Ergebnisse erzielen kann. Ursprünglich habe ich versucht, den gleichen Wert zu übergeben wie den, den ich „processFrameForFinalImage“ nenne, nämlich leftMirrored, aber das führte zu einer falschen Position der Maske.
 Mobile version
 Mobile version