diff --git a/CheapRetouch/Features/Editor/EditorViewModel.swift b/CheapRetouch/Features/Editor/EditorViewModel.swift index 95fb91b..f7bf3a2 100644 --- a/CheapRetouch/Features/Editor/EditorViewModel.swift +++ b/CheapRetouch/Features/Editor/EditorViewModel.swift @@ -241,15 +241,23 @@ final class EditorViewModel { } private func handleObjectTap(at point: CGPoint, in image: CGImage) async throws { + DebugLogger.log("handleObjectTap at \(point)") + DebugLogger.log("Calling maskingService.generateForegroundMask...") + let mask = try await maskingService.generateForegroundMask(at: point, in: image) + DebugLogger.log("generateForegroundMask returned") + DebugLogger.imageInfo("Object mask", image: mask) + guard let mask = mask else { + DebugLogger.log("No mask returned - prompting for brush tool") errorMessage = "Couldn't detect object. Try the brush tool to select manually." return } maskPreview = mask showingMaskConfirmation = true + DebugLogger.state("Object mask preview set, showing confirmation") } private func handleWireTap(at point: CGPoint, in image: CGImage) async throws { diff --git a/CheapRetouch/Services/MaskingService.swift b/CheapRetouch/Services/MaskingService.swift index 6a3a875..3cffc44 100644 --- a/CheapRetouch/Services/MaskingService.swift +++ b/CheapRetouch/Services/MaskingService.swift @@ -157,35 +157,48 @@ actor MaskingService { } func generateForegroundMask(at point: CGPoint, in image: CGImage) async throws -> CGImage? { + DebugLogger.processing("generateForegroundMask at \(point)") + DebugLogger.log("Image: \(image.width)x\(image.height)") + let request = VNGenerateForegroundInstanceMaskRequest() + DebugLogger.log("Created VNGenerateForegroundInstanceMaskRequest") let handler = VNImageRequestHandler(cgImage: image, options: [:]) + DebugLogger.log("Created VNImageRequestHandler, performing request...") do { try handler.perform([request]) + DebugLogger.log("Vision request completed successfully") } catch { + DebugLogger.error("Vision request failed", error: error) throw MaskingError.requestFailed(error) } guard let result = request.results?.first else { + DebugLogger.log("No results from Vision request") return nil } + DebugLogger.log("Got Vision result with \(result.allInstances.count) instances") + // Normalize point to Vision coordinates (0-1, origin bottom-left) let visionPoint = CGPoint( x: point.x / CGFloat(image.width), y: 1.0 - point.y / CGFloat(image.height) ) + DebugLogger.log("Vision point: \(visionPoint)") // Find instance at tap point let instances = result.allInstances var targetInstance: IndexSet? + DebugLogger.log("Checking \(instances.count) instances for tap point...") for instance in instances { let indexSet = IndexSet(integer: instance) if let maskPixelBuffer = try? result.generateScaledMaskForImage(forInstances: indexSet, from: handler) { // Check if point is within this instance's mask if isPoint(visionPoint, inMask: maskPixelBuffer, imageSize: CGSize(width: image.width, height: image.height)) { + DebugLogger.log("Found instance \(instance) at tap point") targetInstance = indexSet break } @@ -193,11 +206,16 @@ actor MaskingService { } guard let instance = targetInstance else { + DebugLogger.log("No instance found at tap point") return nil } + DebugLogger.log("Generating mask for selected instance...") let maskPixelBuffer = try result.generateScaledMaskForImage(forInstances: instance, from: handler) - return convertPixelBufferToCGImage(maskPixelBuffer) + DebugLogger.log("Mask generated, converting to CGImage...") + let cgImage = convertPixelBufferToCGImage(maskPixelBuffer) + DebugLogger.imageInfo("Generated mask", image: cgImage) + return cgImage } func generateAllForegroundMasks(in image: CGImage) async throws -> CGImage? {