You are an expert swift ios developer. Carefully think through the user's query and relevant code files provided to generate high-quality, reliable swift code which works with the rest of their application. provide code changes with their explicit locations for maximum accuracy and communication to the user. Context of the IOS app: the app you're working with is supposed to work like this UX: user opens app>user selects image from their camera roll>image is loaded into Image Viewer to display to the user but also encode into an Computer vision model called 'SAM2'>User taps to on the image to add a point> SAM2 takes in this point and generates an image mask on the image given the point>user presses "search" button and the image mask is exported as a .png to firebase. Given this context, please accomplish the task given in the user's query:
User query:"Given my app's file structure and contents, how can I ensure that the coreml computer vision models are being called and used correctly? For context, when i currently tap on an image to include a point, no mask is generated on the UI. i'm not sure if this is a UI issue with displaying the mask or if it's a model issue where the mask isn't ever being created. Here are the logs from my app when I loaded the app and added a point:
Failed to get the home directory when checking model path.
Failed to get the home directory when checking model path.
Failed to get the home directory when checking model path.
E5RT encountered an STL exception. msg = tensor_buffer has known strides while the model has FlexibleShapeInfo. Strides must be unknown on all dimensions..
E5RT: tensor_buffer has known strides while the model has FlexibleShapeInfo. Strides must be unknown on all dimensions. (11)
Warning: deconvolution is specifying manual output size via deconv_out_width/deconv_out_height and that is not compatible with multple resolutions. Ignoring that deconv_out_width/deconv_out_height
Warning: deconvolution is specifying manual output size via deconv_out_width/deconv_out_height and that is not compatible with multple resolutions. Ignoring that deconv_out_width/deconv_out_height
Models loaded successfully in 0.23 seconds
Starting image encoding
Image encoding completed
Tapped at location: (2076.715805403149, 1490.5706375835407), imageSize: (4288.0, 2848.0)
Normalized point: (0.4843087232749881, 0.5233745216234342)
Including point
Adding include point: (0.4843087232749881, 0.5233745216234342)
Running the model with points: 1 includes, 0 excludes.
Model run completed successfully.

The logs suggest that the models weren't found with this message 'Failed to get the home directory when checking model path.' being displayed 3 times for the 3 models. Keep in mind that I haven't modified anything in my app's xcode settings for coreml tools. Given this query, plese use the context providied below to identify possible issues, add extra debugging if needed, and generate a solution to the problem."
Here's the relevant app files for loading an image and generating an image mask:
file structure:
.
├── Assets.xcassets
│   ├── AccentColor.colorset
│   │   └── Contents.json
│   ├── AppIcon.appiconset
│   │   ├── Contents.json
│   │   ├── android-chrome-512x512 1.png
│   │   ├── android-chrome-512x512.png
│   │   ├── final-grid-shirt 1.jpg
│   │   └── final-grid-shirt.jpg
│   └── Contents.json
├── Common
│   ├── CGImage+Extension.swift
│   ├── CGImage+RawBytes.swift
│   ├── CoreImageExtensions.swift
│   ├── DirectoryDocument.swift
│   ├── MLMultiArray+Image.swift
│   ├── Models.swift
│   ├── SAM2.swift
│   └── UIImage+Extension.swift
├── ContentView.swift
├── CoreMLModels
│   ├── SAM2SmallImageEncoderFLOAT16.mlpackage
│   │   ├── Data
│   │   │   └── com.apple.CoreML
│   │   │       ├── model.mlmodel
│   │   │       └── weights
│   │   │           └── weight.bin
│   │   └── Manifest.json
│   ├── SAM2SmallMaskDecoderFLOAT16.mlpackage
│   │   ├── Data
│   │   │   └── com.apple.CoreML
│   │   │       ├── model.mlmodel
│   │   │       └── weights
│   │   │           └── weight.bin
│   │   └── Manifest.json
│   └── SAM2SmallPromptEncoderFLOAT16.mlpackage
│       ├── Data
│       │   └── com.apple.CoreML
│       │       ├── model.mlmodel
│       │       └── weights
│       │           └── weight.bin
│       └── Manifest.json
├── GoogleService-Info.plist
├── ImagePageModel.swift
├── Info.plist
├── Managers
│   ├── CloudinaryHelper.swift
│   └── ImageUploader.swift
├── Models
│   └── SearchResponse.swift
├── Package
│   └── SegmentAnyThing
├── Page.swift
├── Preview Content
│   └── Preview Assets.xcassets
│       └── Contents.json
├── SegmentAnythingDemo.entitlements
├── SegmentAnythingDemoApp.swift
├── Views
│   ├── ImageView.swift
│   └── SearchResults.swift
├── demo.jpg
└── detail
    ├── DetailCompositor.metal
    ├── DetailCompositorFilter.swift
    ├── DetailView.swift
    ├── DetailViewModel.swift
    └── LoadingView.swift


file:'detail/DetailView.swift':```
import SwiftUI
import CIImageViewer

struct DetailView: View {
    @Binding var imagePageModel: ImagePageModel
    let onDismiss: () -> Void

    @StateObject var viewModel: DetailViewModel
    @State private var currentTool: Tool = .includePoint
    @State private var isResetActive = false
    @State private var backgroundDisplayAmount: Double = 0.3
    @State private var showingSearchResults = false
    @State private var isLoading = false

    // State variable to control overlay visibility
    @State private var showTapToSelectOverlay = false

    private enum Tool {
        case includePoint, excludePoint
    }

    init(imagePageModel: Binding<ImagePageModel>, onDismiss: @escaping () -> Void) {
        self._imagePageModel = imagePageModel
        self.onDismiss = onDismiss
        self._viewModel = StateObject(
            wrappedValue: DetailViewModel(
                initialCiImage: imagePageModel.wrappedValue.inputImage,
                backgroundPercent: 0.3
            )
        )
    }

    private func onImageTap(location: CGPoint) {
        print("Tapped at location: \(location), imageSize: \(viewModel.imageSize)")
        let normalizedPoint = CGPoint(
            x: location.x / viewModel.imageSize.width,
            y: location.y / viewModel.imageSize.height
        )
        print("Normalized point: \(normalizedPoint)")
        switch currentTool {
        case .includePoint:
            print("Including point")
            viewModel.includePoint(cgPoint: normalizedPoint)
        case .excludePoint:
            print("Excluding point")
            viewModel.excludePoint(cgPoint: normalizedPoint)
        }
    }

    var body: some View {
        ZStack {
            Color.black.edgesIgnoringSafeArea(.all)

            VStack {
                HStack {
                    Button(action: onDismiss) {
                        Image(systemName: "chevron.left")
                            .foregroundColor(.white)
                            .padding()
                    }
                    Spacer()
                }

                Spacer()

                ZStack {
                    CIImageViewer(image: viewModel.renderingCiImage, onTap: { location in
                        onImageTap(location: location)
                    })
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .background(Color.black)
                    .edgesIgnoringSafeArea(.all)

                    // Overlay displaying "Tap to Select" text over the entire image view
                    if showTapToSelectOverlay {
                        Color.black.opacity(0.5)
                            .edgesIgnoringSafeArea(.all)
                            .overlay(
                                Text("Tap to Select")
                                    .foregroundColor(.white)
                                    .font(.system(size: 24, weight: .semibold))
                                    .padding()
                            )
                    }
                }

                Spacer()

                HStack {
                    Button(action: {
                        isResetActive = true
                        viewModel.reset()
                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                            isResetActive = false
                        }
                    }) {
                        Image(systemName: "arrow.counterclockwise")
                            .foregroundColor(isResetActive ? .black : .white)
                            .frame(width: 44, height: 44)
                    }
                    .buttonStyle(CustomButtonStyle(isActive: isResetActive))

                    Spacer()

                    Button(action: { currentTool = .includePoint }) {
                        Image(systemName: "plus")
                            .foregroundColor(currentTool == .includePoint ? .black : .white)
                            .frame(width: 44, height: 44)
                    }
                    .buttonStyle(CustomButtonStyle(isActive: currentTool == .includePoint))

                    Button(action: { currentTool = .excludePoint }) {
                        Image(systemName: "minus")
                            .foregroundColor(currentTool == .excludePoint ? .black : .white)
                            .frame(width: 44, height: 44)
                    }
                    .buttonStyle(CustomButtonStyle(isActive: currentTool == .excludePoint))

                    Spacer()

                    Button(action: {
                        Task { @MainActor in
                            self.isLoading = true
                            if let _ = await viewModel.saveMaskAsPNG() {
                                await viewModel.uploadMaskToCloudinary()
                                await viewModel.performGoogleLensSearch()
                                showingSearchResults = true
                            }
                            self.isLoading = false
                        }
                    }) {
                        Text("Search")
                            .foregroundColor(.white)
                            .frame(width: 100, height: 44)
                    }
                    .buttonStyle(CustomButtonStyle(isActive: false))
                }
                .padding(.horizontal)
                .padding(.bottom, 30)
            }
        }
        .onAppear {
            // Show the overlay when the view appears
            self.showTapToSelectOverlay = true
            // Hide the overlay after 1 second
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                self.showTapToSelectOverlay = false
            }
        }
        .onChange(of: backgroundDisplayAmount) { newValue in
            viewModel.backgroundPercent = newValue
        }
        .sheet(isPresented: $showingSearchResults) {
            SearchResultView(searchResult: $viewModel.searchResponse)
        }
        .overlay() {
            if self.isLoading {
                LoadingView()
            }
        }
    }
}

struct CustomButtonStyle: ButtonStyle {
    var isActive: Bool

    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .background(isActive ? Color.white : Color.clear)
            .cornerRadius(22)
            .overlay(
                RoundedRectangle(cornerRadius: 22)
                    .stroke(Color.white, lineWidth: 2)
            )
    }
}
```
file:'detail/DetailViewModel.swift':```
import Foundation
import SwiftUI
import CoreML
import CoreImage
import CoreImage.CIFilterBuiltins
import Combine

@MainActor
class DetailViewModel: ObservableObject {
    @Published var renderingCiImage: CIImage
    @Published var fullColorMask: UIImage?
    @Published var cdnUrl: String?
    @Published var searchResponse: SearchResponse

    private let originalCiImage: CIImage
    private let resizedCiImage: CIImage
    private let sam2 = SAM2()
    private var cancellables = Set<AnyCancellable>()

    private var includePoints: [SAMPoint] = []
    private var excludePoints: [SAMPoint] = []

    @Published var isProcessing: Bool = false

    var backgroundPercent: Double = 0.3 {
        didSet {
            self.runModel()
        }
    }

    // Add the imageSize property here
    var imageSize: CGSize {
        return renderingCiImage.extent.size
    }

    init(initialCiImage: CIImage, backgroundPercent: Double) {
        self.originalCiImage = initialCiImage
        self.resizedCiImage = initialCiImage.resized(to: CGSize(width: 1024, height: 1024))
        self.renderingCiImage = initialCiImage
        self.backgroundPercent = backgroundPercent
        self.searchResponse = SearchResponse(searchMetadata: nil, searchParameters: nil, visualMatches: [], imageSourcesSearch: nil)

        // Observe when SAM2 models are initialized
        sam2.$initialized
            .compactMap { $0 }
            .filter { $0 }
            .receive(on: RunLoop.main)
            .sink { [weak self] _ in
                self?.startImageEncoding()
            }
            .store(in: &cancellables)
    }

    func reset() {
        self.renderingCiImage = originalCiImage
        self.includePoints = []
        self.excludePoints = []
        self.fullColorMask = nil
    }

    // DetailViewModel.swift
    func includePoint(cgPoint: CGPoint) {
        print("Adding include point: \(cgPoint)")
        let samPoint = SAMPoint(coordinates: cgPoint, category: .foreground)
        self.includePoints.append(samPoint)
        self.runModel()
    }

    func excludePoint(cgPoint: CGPoint) {
        print("Adding exclude point: \(cgPoint)")
        let samPoint = SAMPoint(coordinates: cgPoint, category: .background)
        self.excludePoints.append(samPoint)
        self.runModel()
    }


    func saveMaskAsPNG() async -> UIImage? {
        do {
            if let maskCIImage = try await sam2.getMask(for: originalCiImage.extent.size) {
                let multiplyFilter = DetailCompositorFilter()
                multiplyFilter.inputImage = originalCiImage
                multiplyFilter.segmentAnythingMask = maskCIImage
                multiplyFilter.backgroundPercent = 0
                if let outputImage = multiplyFilter.outputImage {
                    let context = CIContext()
                    guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else {
                        return nil
                    }

                    let uiImage = UIImage(cgImage: cgImage)
                    self.fullColorMask = uiImage
                    return uiImage
                }
            }
        } catch {
            print("Error fetching mask:", error)
        }
        return nil
    }

    func uploadMaskToCloudinary() async {
        guard let maskImage = self.fullColorMask else { return }

        self.cdnUrl = await withCheckedContinuation { continuation in
            ImageUploader.shared.uploadImage(image: maskImage) { url in
                continuation.resume(returning: url)
            }
        }
    }

    func performGoogleLensSearch() async {
        guard let cdnUrl = self.cdnUrl else { return }

        self.searchResponse = await withCheckedContinuation { continuation in
            ImageUploader.shared.searchImages(url: cdnUrl) { response, error in
                if let response = response {
                    continuation.resume(returning: response)
                } else if let error = error {
                    print("Error performing Google Lens search: \(error.localizedDescription)")
                    continuation.resume(returning: self.searchResponse) // Return existing response on error
                }
            }
        }
    }

    // DetailViewModel.swift
    private func startImageEncoding() {
        Task {
            print("Starting image encoding")
            if let pixelBuffer = self.resizedCiImage.toPixelBuffer(width: Int(1024), height: Int(1024)) {
                do {
                    try await self.sam2.getImageEncoding(from: pixelBuffer)
                    print("Image encoding completed")
                } catch {
                    print("Error getting image encoding:", error)
                }
            } else {
                print("Failed to create pixel buffer for image encoding")
            }
        }
    }



    private func runModel() {
        guard sam2.initialized == true else {
            print("SAM2 models not initialized yet")
            return
        }
        Task {
            guard !includePoints.isEmpty || !excludePoints.isEmpty else {
                self.renderingCiImage = self.originalCiImage
                return
            }
            do {
                self.isProcessing = true
                print("Running the model with points: \(includePoints.count) includes, \(excludePoints.count) excludes.")

                // Combine includePoints and excludePoints
                let allPoints = self.includePoints + self.excludePoints

                // Get prompt encoding from points
                try await self.sam2.getPromptEncoding(from: allPoints, with: originalCiImage.extent.size)

                // Get mask from SAM2
                if let maskCiImage = try await self.sam2.getMask(for: originalCiImage.extent.size) {

                    // Process the mask as before
                    let multiplyFilter = DetailCompositorFilter()
                    multiplyFilter.inputImage = originalCiImage
                    multiplyFilter.segmentAnythingMask = maskCiImage
                    multiplyFilter.backgroundPercent = backgroundPercent

                    if let outputImage = multiplyFilter.outputImage {
                        self.renderingCiImage = outputImage
                    }
                }

                self.isProcessing = false
                print("Model run completed successfully.")
            } catch {
                print("Error rendering:", error)
                self.isProcessing = false
            }
        }
    }
}
```
file:'detail/DetailCompositor.metal':```
#include <CoreImage/CoreImage.h>
using namespace metal;

[[stitchable]] float4 DetailCompositor(coreimage::sample_t input, coreimage::sample_t mask, float backgroundPercent) {
    float alpha = mask.a;
    float3 inputValue = input.rgb;
    if (alpha > 0.9) {
        return float4(inputValue, 1);
    } else {
        return float4(inputValue, backgroundPercent);
    }
}
```
file:'detail/DetailCompositorFilter.swift':```
import Foundation
import CoreImage

// DetailCompositorFilter.swift

class DetailCompositorFilter: CIFilter {
    var inputImage: CIImage?
    var segmentAnythingMask: CIImage?
    var backgroundPercent: CGFloat = 0.3

    override var outputImage: CIImage? {
        guard let inputImage = inputImage,
              let maskImage = segmentAnythingMask else {
            return nil
        }

        // Create a background image with desired transparency
        let backgroundColor = CIColor(red: 0, green: 0, blue: 0, alpha: backgroundPercent)
        let backgroundImage = CIImage(color: backgroundColor).cropped(to: inputImage.extent)

        // Blend input image over background using the mask
        let filter = CIFilter(name: "CIBlendWithMask")
        filter?.setValue(inputImage, forKey: kCIInputImageKey)
        filter?.setValue(backgroundImage, forKey: kCIInputBackgroundImageKey)
        filter?.setValue(maskImage, forKey: kCIInputMaskImageKey)

        return filter?.outputImage
    }
}
```
file:'ContentView.swift':```
import SwiftUI
import PhotosUI
import CoreImage

#if canImport(UIKit)
extension UIImage.Orientation {
    var exifOrientation: Int32 {
        switch self {
            case .up: return 1
            case .down: return 3
            case .left: return 8
            case .right: return 6
            case .upMirrored: return 2
            case .downMirrored: return 4
            case .leftMirrored: return 5
            case .rightMirrored: return 7
        @unknown default:
            return 1
        }
    }
}
#endif

struct ContentView: View {
    @State private var imageSelection: PhotosPickerItem?
    @State private var currentImage: ImagePageModel?
    @State private var showingDetailView = false
    @State private var isLoading = false
    @State private var showingPhotoPicker = false // State to control photo picker display

    var body: some View {
        ZStack {
            Color.black.edgesIgnoringSafeArea(.all)

            if isLoading {
                // Show loading view while the image is being processed
                LoadingView()
            } else {
                if let currentImage = currentImage, showingDetailView {
                    DetailView(imagePageModel: Binding(
                        get: { currentImage },
                        set: { self.currentImage = $0 }
                    ), onDismiss: {
                        showingDetailView = false
                    })
                } else {
                    VStack {
                        // Button to allow user to reopen photo picker manually if needed
                        PhotosPicker(selection: $imageSelection, matching: .images) {
                            Label("Select Image from Camera Roll", systemImage: "photo.fill")
                                .foregroundColor(.white)
                        }
                        .padding()

                        // Automatically open photo picker on launch
                        .onAppear {
                            showingPhotoPicker = true
                        }
                        .photosPicker(isPresented: $showingPhotoPicker, selection: $imageSelection, matching: .images)
                    }
                }
            }
        }
        .onChange(of: imageSelection) { newValue in
            if let imageSelection = newValue {
                loadImage(imageSelection: imageSelection)
            }
        }
    }

    private func loadImage(imageSelection: PhotosPickerItem) {
        // Show loading indicator
        self.isLoading = true

        imageSelection.loadTransferable(type: Data.self) { result in
            switch result {
            case .success(let data):
                if let data = data {
                    #if canImport(UIKit)
                    if let uiImage = UIImage(data: data) {
                        let ciImage = CIImage(image: uiImage, options: [.applyOrientationProperty: true])?.oriented(forExifOrientation: uiImage.imageOrientation.exifOrientation)
                        if let ciImage = ciImage {
                            DispatchQueue.main.async {
                                self.currentImage = ImagePageModel(inputImage: ciImage, name: UUID().uuidString)
                                self.showingDetailView = true
                            }
                        }
                    }
                    #else
                    if let ciImage = CIImage(data: data) {
                        DispatchQueue.main.async {
                            self.currentImage = ImagePageModel(inputImage: ciImage, name: UUID().uuidString)
                            self.showingDetailView = true
                        }
                    }
                    #endif
                }
            case .failure(let error):
                print("Error loading image: \(error)")
            }
            // Hide loading indicator
            self.isLoading = false
            self.imageSelection = nil
        }
    }
}

#Preview {
    ContentView()
}
```
file:'Common/CGImage+Extension.swift':```
import CoreImage
import VideoToolbox

extension CIImage {
    func toPixelBuffer(width: Int, height: Int) -> CVPixelBuffer? {
        let pixelFormat = kCVPixelFormatType_32BGRA
        let options: [CIImageOption: Any] = [CIImageOption.colorSpace: CGColorSpaceCreateDeviceRGB()]
        var pxbuffer: CVPixelBuffer?
        let attrs = [kCVPixelBufferCGImageCompatibilityKey: true,
                     kCVPixelBufferCGBitmapContextCompatibilityKey: true] as CFDictionary
        let status = CVPixelBufferCreate(kCFAllocatorDefault, width, height, pixelFormat, attrs, &pxbuffer)
        guard status == kCVReturnSuccess, let buffer = pxbuffer else {
            return nil
        }
        let context = CIContext()
        context.render(self, to: buffer, bounds: CGRect(x: 0, y: 0, width: CGFloat(width), height: CGFloat(height)), colorSpace: CGColorSpaceCreateDeviceRGB())
        return buffer
    }
}
```
file:'Common/CGImage+RawBytes.swift':```
import CoreGraphics

extension CGImage {
  /**
    Converts the image into an array of RGBA bytes.
  */
  @nonobjc public func toByteArrayRGBA() -> [UInt8] {
    var bytes = [UInt8](repeating: 0, count: width * height * 4)
    bytes.withUnsafeMutableBytes { ptr in
      if let colorSpace = colorSpace,
         let context = CGContext(
                    data: ptr.baseAddress,
                    width: width,
                    height: height,
                    bitsPerComponent: bitsPerComponent,
                    bytesPerRow: bytesPerRow,
                    space: colorSpace,
                    bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) {
        let rect = CGRect(x: 0, y: 0, width: width, height: height)
        context.draw(self, in: rect)
      }
    }
    return bytes
  }

  /**
    Creates a new CGImage from an array of RGBA bytes.
  */
  @nonobjc public class func fromByteArrayRGBA(_ bytes: [UInt8],
                                               width: Int,
                                               height: Int) -> CGImage? {
    return fromByteArray(bytes, width: width, height: height,
                         bytesPerRow: width * 4,
                         colorSpace: CGColorSpaceCreateDeviceRGB(),
                         alphaInfo: .premultipliedLast)
  }

  /**
    Creates a new CGImage from an array of grayscale bytes.
  */
  @nonobjc public class func fromByteArrayGray(_ bytes: [UInt8],
                                               width: Int,
                                               height: Int) -> CGImage? {
    return fromByteArray(bytes, width: width, height: height,
                         bytesPerRow: width,
                         colorSpace: CGColorSpaceCreateDeviceGray(),
                         alphaInfo: .none)
  }

  @nonobjc class func fromByteArray(_ bytes: [UInt8],
                                    width: Int,
                                    height: Int,
                                    bytesPerRow: Int,
                                    colorSpace: CGColorSpace,
                                    alphaInfo: CGImageAlphaInfo) -> CGImage? {
    return bytes.withUnsafeBytes { ptr in
      let context = CGContext(data: UnsafeMutableRawPointer(mutating: ptr.baseAddress!),
                              width: width,
                              height: height,
                              bitsPerComponent: 8,
                              bytesPerRow: bytesPerRow,
                              space: colorSpace,
                              bitmapInfo: alphaInfo.rawValue)
      return context?.makeImage()
    }
  }
}
```
file:'Common/CoreImageExtensions.swift':```
import CoreImage
import CoreImage.CIFilterBuiltins
import ImageIO
import UniformTypeIdentifiers

extension CIImage {
    /// Returns a resized image.
    func resized(to size: CGSize) -> CIImage {
        let outputScaleX = size.width / extent.width
        let outputScaleY = size.height / extent.height
        var outputImage = self.transformed(by: CGAffineTransform(scaleX: outputScaleX, y: outputScaleY))
        outputImage = outputImage.transformed(
            by: CGAffineTransform(translationX: -outputImage.extent.origin.x, y: -outputImage.extent.origin.y)
        )
        return outputImage
    }

    public func withAlpha<T: BinaryFloatingPoint>(_ alpha: T) -> CIImage? {
        guard alpha != 1 else { return self }

        let filter = CIFilter.colorMatrix()
        filter.inputImage = self
        filter.aVector = CIVector(x: 0, y: 0, z: 0, w: CGFloat(alpha))

        return filter.outputImage
    }

//    public func applyingThreshold(_ threshold: Float) -> CIImage? {
//        let filter = CIFilter.colorThreshold()
//        filter.inputImage = self
//        filter.threshold = threshold
//        return filter.outputImage
//    }
}

extension CIContext {
    /// Renders an image to a new pixel buffer.
    func render(_ image: CIImage, pixelFormat: OSType) -> CVPixelBuffer? {
        var output: CVPixelBuffer!
        let status = CVPixelBufferCreate(
            kCFAllocatorDefault,
            Int(image.extent.width),
            Int(image.extent.height),
            pixelFormat,
            nil,
            &output
        )
        guard status == kCVReturnSuccess else {
            return nil
        }
        render(image, to: output, bounds: image.extent, colorSpace: nil)
        return output
    }

    /// Writes the image as a PNG.
    func writePNG(_ image: CIImage, to url: URL) {
        let outputCGImage = createCGImage(image, from: image.extent, format: .BGRA8, colorSpace: nil)!
        guard let destination = CGImageDestinationCreateWithURL(url as CFURL, UTType.png.identifier as CFString, 1, nil) else {
            fatalError("Failed to create an image destination.")
        }
        CGImageDestinationAddImage(destination, outputCGImage, nil)
        CGImageDestinationFinalize(destination)
    }
}
```
file:'Common/DirectoryDocument.swift':```
import SwiftUI
import UniformTypeIdentifiers

struct DirectoryDocument: FileDocument {
    static var readableContentTypes: [UTType] { [.folder] }

    init(initialContentType: UTType = .folder) {
        // Initialize if needed
    }

    init(configuration: ReadConfiguration) throws {
        // Initialize if needed
    }

    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        return FileWrapper(directoryWithFileWrappers: [:])
    }
}
```
file:'Common/MLMultiArray+Image.swift':```
import Accelerate
import CoreML

public func clamp<T: Comparable>(_ x: T, min: T, max: T) -> T {
  if x < min { return min }
  if x > max { return max }
  return x
}

public protocol MultiArrayType: Comparable {
  static var multiArrayDataType: MLMultiArrayDataType { get }
  static func +(lhs: Self, rhs: Self) -> Self
  static func -(lhs: Self, rhs: Self) -> Self
  static func *(lhs: Self, rhs: Self) -> Self
  static func /(lhs: Self, rhs: Self) -> Self
  init(_: Int)
  var toUInt8: UInt8 { get }
}

extension Double: MultiArrayType {
  public static var multiArrayDataType: MLMultiArrayDataType { return .double }
  public var toUInt8: UInt8 { return UInt8(self) }
}

extension Float: MultiArrayType {
  public static var multiArrayDataType: MLMultiArrayDataType { return .float32 }
  public var toUInt8: UInt8 { return UInt8(self) }
}

extension Int32: MultiArrayType {
  public static var multiArrayDataType: MLMultiArrayDataType { return .int32 }
  public var toUInt8: UInt8 { return UInt8(self) }
}

extension Float16: MultiArrayType {
    public static var multiArrayDataType: MLMultiArrayDataType { return .float16 }
    public var toUInt8: UInt8 { return UInt8(self) }
}

extension MLMultiArray {
  /**
    Converts the multi-array to a CGImage.

    The multi-array must have at least 2 dimensions for a grayscale image, or
    at least 3 dimensions for a color image.

    The default expected shape is (height, width) or (channels, height, width).
    However, you can change this using the `axes` parameter. For example, if
    the array shape is (1, height, width, channels), use `axes: (3, 1, 2)`.

    If `channel` is not nil, only converts that channel to a grayscale image.
    This lets you visualize individual channels from a multi-array with more
    than 4 channels.

    Otherwise, converts all channels. In this case, the number of channels in
    the multi-array must be 1 for grayscale, 3 for RGB, or 4 for RGBA.

    Use the `min` and `max` parameters to put the values from the array into
    the range [0, 255], if not already:

    - `min`: should be the smallest value in the data; this will be mapped to 0.
    - `max`: should be the largest value in the data; will be mapped to 255.

    For example, if the range of the data in the multi-array is [-1, 1], use
    `min: -1, max: 1`. If the range is already [0, 255], then use the defaults.
  */
  public func cgImage(min: Double = 0,
                      max: Double = 255,
                      channel: Int? = nil,
                      axes: (Int, Int, Int)? = nil) -> CGImage? {
    switch self.dataType {
    case .double:
      return _image(min: min, max: max, channel: channel, axes: axes)
    case .float32:
      return _image(min: Float(min), max: Float(max), channel: channel, axes: axes)
    case .int32:
      return _image(min: Int32(min), max: Int32(max), channel: channel, axes: axes)
    case .float16:
        return _image(min: Float16(min), max: Float16(max), channel: channel, axes: axes)
    @unknown default:
      fatalError("Unsupported data type \(dataType.rawValue)")
    }
  }

  /**
    Helper function that allows us to use generics. The type of `min` and `max`
    is also the dataType of the MLMultiArray.
  */
  private func _image<T: MultiArrayType>(min: T,
                                         max: T,
                                         channel: Int?,
                                         axes: (Int, Int, Int)?) -> CGImage? {
    if let (b, w, h, c) = toRawBytes(min: min, max: max, channel: channel, axes: axes) {
      if c == 1 {
        return CGImage.fromByteArrayGray(b, width: w, height: h)
      } else {
        return CGImage.fromByteArrayRGBA(b, width: w, height: h)
      }
    }
    return nil
  }

  /**
    Converts the multi-array into an array of RGBA or grayscale pixels.

    - Note: This is not particularly fast, but it is flexible. You can change
            the loops to convert the multi-array whichever way you please.

    - Note: The type of `min` and `max` must match the dataType of the
            MLMultiArray object.

    - Returns: tuple containing the RGBA bytes, the dimensions of the image,
               and the number of channels in the image (1, 3, or 4).
  */
  public func toRawBytes<T: MultiArrayType>(min: T,
                                            max: T,
                                            channel: Int? = nil,
                                            axes: (Int, Int, Int)? = nil)
                  -> (bytes: [UInt8], width: Int, height: Int, channels: Int)? {
    // MLMultiArray with unsupported shape?
    if shape.count < 2 {
      print("Cannot convert MLMultiArray of shape \(shape) to image")
      return nil
    }

    // Figure out which dimensions to use for the channels, height, and width.
    let channelAxis: Int
    let heightAxis: Int
    let widthAxis: Int
    if let axes = axes {
      channelAxis = axes.0
      heightAxis = axes.1
      widthAxis = axes.2
      guard channelAxis >= 0 && channelAxis < shape.count &&
            heightAxis >= 0 && heightAxis < shape.count &&
            widthAxis >= 0 && widthAxis < shape.count else {
        print("Invalid axes \(axes) for shape \(shape)")
        return nil
      }
    } else if shape.count == 2 {
      // Expected shape for grayscale is (height, width)
      heightAxis = 0
      widthAxis = 1
      channelAxis = -1 // Never be used
    } else {
      // Expected shape for color is (channels, height, width)
      channelAxis = 0
      heightAxis = 1
      widthAxis = 2
    }

    let height = self.shape[heightAxis].intValue
    let width = self.shape[widthAxis].intValue
    let yStride = self.strides[heightAxis].intValue
    let xStride = self.strides[widthAxis].intValue

    let channels: Int
    let cStride: Int
    let bytesPerPixel: Int
    let channelOffset: Int

    // MLMultiArray with just two dimensions is always grayscale. (We ignore
    // the value of channelAxis here.)
    if shape.count == 2 {
      channels = 1
      cStride = 0
      bytesPerPixel = 1
      channelOffset = 0

    // MLMultiArray with more than two dimensions can be color or grayscale.
    } else {
      let channelDim = self.shape[channelAxis].intValue
      if let channel = channel {
        if channel < 0 || channel >= channelDim {
          print("Channel must be -1, or between 0 and \(channelDim - 1)")
          return nil
        }
        channels = 1
        bytesPerPixel = 1
        channelOffset = channel
      } else if channelDim == 1 {
        channels = 1
        bytesPerPixel = 1
        channelOffset = 0
      } else {
        if channelDim != 3 && channelDim != 4 {
          print("Expected channel dimension to have 1, 3, or 4 channels, got \(channelDim)")
          return nil
        }
        channels = channelDim
        bytesPerPixel = 4
        channelOffset = 0
      }
      cStride = self.strides[channelAxis].intValue
    }

    // Allocate storage for the RGBA or grayscale pixels. Set everything to
    // 255 so that alpha channel is filled in if only 3 channels.
    let count = height * width * bytesPerPixel
    var pixels = [UInt8](repeating: 255, count: count)

    // Grab the pointer to MLMultiArray's memory.
    var ptr = UnsafeMutablePointer<T>(OpaquePointer(self.dataPointer))
    ptr = ptr.advanced(by: channelOffset * cStride)

    // Loop through all the pixels and all the channels and copy them over.
    for c in 0..<channels {
      for y in 0..<height {
        for x in 0..<width {
          let value = ptr[c*cStride + y*yStride + x*xStride]
          let scaled = (value - min) * T(255) / (max - min)
          let pixel = clamp(scaled, min: T(0), max: T(255)).toUInt8
          pixels[(y*width + x)*bytesPerPixel + c] = pixel
        }
      }
    }
    return (pixels, width, height, channels)
  }
}

/**
  Fast conversion from MLMultiArray to CGImage using the vImage framework.

  - Parameters:
    - features: A multi-array with data type FLOAT32 and three dimensions
                (3, height, width).
    - min: The smallest value in the multi-array. This value, as well as any
           smaller values, will be mapped to 0 in the output image.
    - max: The largest value in the multi-array. This and any larger values
           will be will be mapped to 255 in the output image.

  - Returns: a new CGImage or nil if the conversion fails
*/
public func createCGImage(fromFloatArray features: MLMultiArray,
                          min: Float = 0,
                          max: Float = 255) -> CGImage? {
  assert(features.dataType == .float32)
  assert(features.shape.count == 3)

  let ptr = UnsafeMutablePointer<Float>(OpaquePointer(features.dataPointer))

  let height = features.shape[1].intValue
  let width = features.shape[2].intValue
  let channelStride = features.strides[0].intValue
  let rowStride = features.strides[1].intValue
  let srcRowBytes = rowStride * MemoryLayout<Float>.stride

  var blueBuffer = vImage_Buffer(data: ptr,
                                 height: vImagePixelCount(height),
                                 width: vImagePixelCount(width),
                                 rowBytes: srcRowBytes)
  var greenBuffer = vImage_Buffer(data: ptr.advanced(by: channelStride),
                                  height: vImagePixelCount(height),
                                  width: vImagePixelCount(width),
                                  rowBytes: srcRowBytes)
  var redBuffer = vImage_Buffer(data: ptr.advanced(by: channelStride * 2),
                                height: vImagePixelCount(height),
                                width: vImagePixelCount(width),
                                rowBytes: srcRowBytes)

  let destRowBytes = width * 4

  var error: vImage_Error = 0
  var pixels = [UInt8](repeating: 0, count: height * destRowBytes)

  pixels.withUnsafeMutableBufferPointer { ptr in
    var destBuffer = vImage_Buffer(data: ptr.baseAddress!,
                                   height: vImagePixelCount(height),
                                   width: vImagePixelCount(width),
                                   rowBytes: destRowBytes)

    error = vImageConvert_PlanarFToBGRX8888(&blueBuffer,
                                            &greenBuffer,
                                            &redBuffer,
                                            Pixel_8(255),
                                            &destBuffer,
                                            [max, max, max],
                                            [min, min, min],
                                            vImage_Flags(0))
  }

  if error == kvImageNoError {
    return CGImage.fromByteArrayRGBA(pixels, width: width, height: height)
  } else {
    return nil
  }
}
```
file:'Common/Models.swift':```
import Foundation
import SwiftUI

enum SAMCategoryType: Int {
    case background = 0
    case foreground = 1
    case boxOrigin = 2
    case boxEnd = 3

    var description: String {
        switch self {
        case .foreground:
            return "Foreground"
        case .background:
            return "Background"
        case .boxOrigin:
            return "Box Origin"
        case .boxEnd:
            return "Box End"
        }
    }
}

struct SAMCategory: Hashable {
    let id: UUID = UUID()
    let type: SAMCategoryType
    let name: String
    let iconName: String
    let color: Color

    var typeDescription: String {
        type.description
    }

    static let foreground = SAMCategory(
        type: .foreground,
        name: "Foreground",
        iconName: "square.on.square.dashed",
        color: .pink
    )

    static let background = SAMCategory(
        type: .background,
        name: "Background",
        iconName: "square.on.square.intersection.dashed",
        color: .purple
    )

    static let boxOrigin = SAMCategory(
        type: .boxOrigin,
        name: "Box Origin",
        iconName: "",
        color: .white
    )

    static let boxEnd = SAMCategory(
        type: .boxEnd,
        name: "Box End",
        iconName: "",
        color: .white
    )
}


struct SAMPoint: Hashable {
    let id = UUID()
    let coordinates: CGPoint
    let category: SAMCategory
    let dateAdded = Date()

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }

    static func ==(lhs: SAMPoint, rhs: SAMPoint) -> Bool {
        return lhs.id == rhs.id
    }
}


struct SAMBox: Hashable, Identifiable {
    let id = UUID()
    var startPoint: CGPoint
    var endPoint: CGPoint
    let category: SAMCategory
    let dateAdded = Date()

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }

    static func ==(lhs: SAMBox, rhs: SAMBox) -> Bool {
        return lhs.id == rhs.id
    }
    var midpoint: CGPoint {
        return CGPoint(
            x: (startPoint.x + endPoint.x) / 2,
            y: (startPoint.y + endPoint.y) / 2
        )
    }
}

extension SAMBox {
    var points: [SAMPoint] {
        [SAMPoint(coordinates: startPoint, category: .boxOrigin), SAMPoint(coordinates: endPoint, category: .boxEnd)]
    }
}

struct SAMSegmentation: Hashable, Identifiable {
    let id = UUID()
    var image: CIImage
    var tintColor: Color {
        didSet {
            updateTintedImage()
        }
    }
    var title: String = ""
    var firstAppearance: Int?
    var isHidden: Bool = false

    private var tintedImage: CIImage?

    init(image: CIImage, tintColor: Color = Color(.sRGB, red: 30/255, green: 144/255, blue: 1), title: String = "", firstAppearance: Int? = nil, isHidden: Bool = false) {
        self.image = image
        self.tintColor = tintColor
        self.title = title
        self.firstAppearance = firstAppearance
        self.isHidden = isHidden
        updateTintedImage()
    }

    private mutating func updateTintedImage() {
        #if canImport(UIKit)
        // For iOS
        let uiColor = UIColor(tintColor)
        let ciColor = CIColor(color: uiColor)
        #elseif canImport(AppKit)
        // For macOS
        let nsColor = NSColor(tintColor)
        let ciColor = CIColor(color: nsColor)
        #endif

        let monochromeFilter = CIFilter.colorMonochrome()
        monochromeFilter.inputImage = image
        monochromeFilter.color = ciColor
        monochromeFilter.intensity = 1.0
        tintedImage = monochromeFilter.outputImage
    }


    var cgImage: CGImage {
        let context = CIContext()
        return context.createCGImage(tintedImage ?? image, from: (tintedImage ?? image).extent)!
    }
}

struct SAMTool: Hashable {
    let id: UUID = UUID()
    let name: String
    let iconName: String
}

// Tools
let pointTool: SAMTool = SAMTool(name: "Point", iconName: "hand.point.up.left")
let boundingBoxTool: SAMTool = SAMTool(name: "Bounding Box", iconName: "rectangle.dashed")
```
file:'Common/SAM2.swift':```
import SwiftUI
import CoreML
import CoreImage
import CoreImage.CIFilterBuiltins
import Combine
import UniformTypeIdentifiers

@MainActor
class SAM2: ObservableObject {

    @Published var imageEncodings: SAM2SmallImageEncoderFLOAT16Output?
    @Published var promptEncodings: SAM2SmallPromptEncoderFLOAT16Output?

    @Published private(set) var initializationTime: TimeInterval?
    @Published private(set) var initialized: Bool?

    private var imageEncoderModel: SAM2SmallImageEncoderFLOAT16?
    private var promptEncoderModel: SAM2SmallPromptEncoderFLOAT16?
    private var maskDecoderModel: SAM2SmallMaskDecoderFLOAT16?

    var inputSize: CGSize { CGSize(width: 1024, height: 1024) }
    var width: CGFloat { inputSize.width }
    var height: CGFloat { inputSize.height }

    init() {
        Task {
            await loadModels()
        }
    }

    private func loadModels() async {
        let startTime = CFAbsoluteTimeGetCurrent()
        do {
            // In SAM2.swift, inside the loadModels() function
            let configuration = MLModelConfiguration()
            configuration.computeUnits = .cpuOnly

            let (imageEncoder, promptEncoder, maskDecoder) = try await Task.detached(priority: .userInitiated) {
                let imageEncoder = try SAM2SmallImageEncoderFLOAT16(configuration: configuration)
                let promptEncoder = try SAM2SmallPromptEncoderFLOAT16(configuration: configuration)
                let maskDecoder = try SAM2SmallMaskDecoderFLOAT16(configuration: configuration)
                return (imageEncoder, promptEncoder, maskDecoder)
            }.value

            self.imageEncoderModel = imageEncoder
            self.promptEncoderModel = promptEncoder
            self.maskDecoderModel = maskDecoder

            let endTime = CFAbsoluteTimeGetCurrent()
            self.initializationTime = endTime - startTime
            self.initialized = true
            print("Models loaded successfully in \(String(format: "%.2f", self.initializationTime!)) seconds")
        } catch {
            print("Failed to initialize models: \(error)")
            self.initializationTime = nil
            self.initialized = false
        }
    }


    func getImageEncoding(from pixelBuffer: CVPixelBuffer) async throws {
        guard let model = imageEncoderModel else {
            throw SAM2Error.modelNotLoaded
        }

        let encoding = try model.prediction(image: pixelBuffer)
        self.imageEncodings = encoding
    }

    func getPromptEncoding(from allPoints: [SAMPoint], with size: CGSize) async throws {
        guard let model = promptEncoderModel else {
            throw SAM2Error.modelNotLoaded
        }

        let transformedCoords = try transformCoords(allPoints.map { $0.coordinates }, normalize: false, origHW: size)

        // Create MLFeatureProvider with the required input format
        let pointsMultiArray = try MLMultiArray(shape: [1, NSNumber(value: allPoints.count), 2], dataType: .float32)
        let labelsMultiArray = try MLMultiArray(shape: [1, NSNumber(value: allPoints.count)], dataType: .int32)

        for (index, point) in transformedCoords.enumerated() {
            pointsMultiArray[[NSNumber(value: 0), NSNumber(value: index), NSNumber(value: 0)]] = NSNumber(value: Float(point.x))
            pointsMultiArray[[NSNumber(value: 0), NSNumber(value: index), NSNumber(value: 1)]] = NSNumber(value: Float(point.y))
            labelsMultiArray[[NSNumber(value: 0), NSNumber(value: index)]] = NSNumber(value: allPoints[index].category.type.rawValue)
        }

        let encoding = try model.prediction(points: pointsMultiArray, labels: labelsMultiArray)
        self.promptEncodings = encoding
    }

    func getMask(for original_size: CGSize) async throws -> CIImage? {
        guard let model = maskDecoderModel else {
            throw SAM2Error.modelNotLoaded
        }

        guard let image_embedding = self.imageEncodings?.image_embedding,
              let feats0 = self.imageEncodings?.feats_s0,
              let feats1 = self.imageEncodings?.feats_s1,
              let sparse_embedding = self.promptEncodings?.sparse_embeddings,
              let dense_embedding = self.promptEncodings?.dense_embeddings else {
            throw SAM2Error.missingEncodings
        }

        do {
            let output = try model.prediction(
                image_embedding: image_embedding,
                sparse_embedding: sparse_embedding,
                dense_embedding: dense_embedding,
                feats_s0: feats0,
                feats_s1: feats1
            )

        // Extract best mask and ignore the others
        let lowFeatureMask = bestMask(for: output)

        // Access scalar values of the mask using .scalars
        let scalars = lowFeatureMask.scalars
        let minValue = scalars.min() ?? Float16(0.0)
        let maxValue = scalars.max() ?? Float16(1.0)

        // Convert Float16 to Double for arithmetic operations
        let minValueDouble = Double(minValue)
        let maxValueDouble = Double(maxValue)
        let denominator = maxValueDouble - minValueDouble
        guard denominator != 0 else {
            // Handle division by zero
            return nil
        }
        let threshold = -minValueDouble / denominator

        // Resize first, then threshold
        if let maskcgImage = lowFeatureMask.cgImage(min: minValueDouble, max: maxValueDouble) {
            let ciImage = CIImage(cgImage: maskcgImage, options: [.colorSpace: NSNull()])
            let resizedImage = try resizeImage(ciImage, to: original_size, applyingThreshold: Float(threshold))
            return resizedImage?.maskedToAlpha()?.samTinted()
        }
        return nil
        } catch {
            print("Error during mask prediction: \(error)")
            throw error
        }
    }


    private func bestMask(for output: SAM2SmallMaskDecoderFLOAT16Output) -> MLShapedArray<Float16> {
        let scores = output.scoresShapedArray[0]
        let scoresArray = scores.scalars
        let argmax = scoresArray.enumerated().max(by: { $0.element < $1.element })?.offset ?? 0
        let maskSlice = output.low_res_masksShapedArray[0, argmax]
        let maskArray = MLShapedArray<Float16>(converting: maskSlice)
        return maskArray
    }


    private func transformCoords(_ coords: [CGPoint], normalize: Bool = false, origHW: CGSize) throws -> [CGPoint] {
        if normalize {
            let w = origHW.width
            let h = origHW.height

            return coords.map { coord in
                let normalizedX = coord.x / w
                let normalizedY = coord.y / h
                return CGPoint(x: normalizedX * width, y: normalizedY * height)
            }
        } else {
            return coords.map { CGPoint(x: $0.x * width, y: $0.y * height) }
        }
    }

    private func resizeImage(_ image: CIImage, to size: CGSize, applyingThreshold threshold: Float = 1) throws -> CIImage? {
        let scale = CGAffineTransform(scaleX: size.width / image.extent.width,
                                      y: size.height / image.extent.height)
        return image.transformed(by: scale).applyingThreshold(threshold)
    }
}

extension CIImage {
    func maskedToAlpha() -> CIImage? {
        let filter = CIFilter.maskToAlpha()
        filter.inputImage = self
        return filter.outputImage
    }

    func samTinted() -> CIImage? {
        let filter = CIFilter.colorMatrix()
        filter.rVector = CIVector(x: 30/255, y: 0, z: 0, w: 1)
        filter.gVector = CIVector(x: 0, y: 144/255, z: 0, w: 1)
        filter.bVector = CIVector(x: 0, y: 0, z: 1, w: 1)
        filter.biasVector = CIVector(x: -1, y: -1, z: -1, w: 0)
        filter.inputImage = self
        return filter.outputImage?.cropped(to: self.extent)
    }

    func applyingThreshold(_ threshold: Float) -> CIImage? {
        let thresholdKernelString = """
        kernel vec4 thresholdKernel(__sample image, float threshold) {
            float luma = dot(image.rgb, vec3(0.299, 0.587, 0.114));
            float mask = luma > threshold ? 1.0 : 0.0;
            return vec4(mask, mask, mask, 1.0);
        }
        """
        guard let thresholdKernel = CIColorKernel(source: thresholdKernelString) else {
            return nil
        }
        return thresholdKernel.apply(extent: self.extent, arguments: [self, threshold])
    }
}

extension MLShapedArrayProtocol where Scalar == Float16 {
    func cgImage(min: Double, max: Double) -> CGImage? {
        let shape = self.shape
        guard shape.count == 2 else { return nil }
        let height = shape[0]
        let width = shape[1]

        let byteCount = width * height
        var buffer = [UInt8](repeating: 0, count: byteCount)

        for y in 0..<height {
            for x in 0..<width {
                let value = Double(self[scalarAt: [y, x]])
                let normalized = (value - min) / (max - min)
                let pixelValue = UInt8(clamping: Int(normalized * 255))
                buffer[y * width + x] = pixelValue
            }
        }

        let data = Data(buffer)
        guard let providerRef = CGDataProvider(data: data as CFData) else {
            return nil
        }

        let colorSpace = CGColorSpaceCreateDeviceGray()
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)

        let cgImage = CGImage(
            width: width,
            height: height,
            bitsPerComponent: 8,
            bitsPerPixel: 8,
            bytesPerRow: width,
            space: colorSpace,
            bitmapInfo: bitmapInfo,
            provider: providerRef,
            decode: nil,
            shouldInterpolate: false,
            intent: .defaultIntent
        )
        return cgImage
    }
}


enum SAM2Error: Error {
    case modelNotLoaded
    case pixelBufferCreationFailed
    case imageResizingFailed
    case missingEncodings
}
```
file;'Common/UIImage+Extension.swift':```
import UIKit
import VideoToolbox

extension UIImage {
    func pixelBuffer() -> CVPixelBuffer? {
        return pixelBuffer(width: Int(size.width), height: Int(size.height))
    }

    func pixelBuffer(width: Int, height: Int) -> CVPixelBuffer? {
        return pixelBuffer(width: width, height: height,
                           pixelFormatType: kCVPixelFormatType_32BGRA,
                           colorSpace: CGColorSpaceCreateDeviceRGB(),
                           alphaInfo: .premultipliedFirst)
    }

    func pixelBuffer(width: Int, height: Int,
                     pixelFormatType: OSType,
                     colorSpace: CGColorSpace,
                     alphaInfo: CGImageAlphaInfo) -> CVPixelBuffer? {
        var pixelBuffer: CVPixelBuffer?
        let attrs = [
            kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue!,
            kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue!
        ] as CFDictionary
        CVPixelBufferCreate(kCFAllocatorDefault, width, height, pixelFormatType, attrs, &pixelBuffer)

        guard let buffer = pixelBuffer else { return nil }

        CVPixelBufferLockBaseAddress(buffer, [])
        let context = CGContext(data: CVPixelBufferGetBaseAddress(buffer),
                                width: width,
                                height: height,
                                bitsPerComponent: 8,
                                bytesPerRow: CVPixelBufferGetBytesPerRow(buffer),
                                space: colorSpace,
                                bitmapInfo: alphaInfo.rawValue)
        guard let context = context else {
            CVPixelBufferUnlockBaseAddress(buffer, [])
            return nil
        }

        UIGraphicsPushContext(context)
        self.draw(in: CGRect(x: 0, y: 0, width: CGFloat(width), height: CGFloat(height)))
        UIGraphicsPopContext()
        CVPixelBufferUnlockBaseAddress(buffer, [])

        return buffer
    }
}
```
file:'ImagePageModel.swift':```
import Foundation
import CoreImage

public struct ImagePageModel : Hashable, Identifiable {
    public let inputImage: CIImage
    public let name: String

    public var id: String {
        return self.name
    }
}
```
Swift Package: 'CIImageViewer':
file:'CIImageViewer.swift':```
import SwiftUI
import UIKit

public struct CIImageViewer: UIViewRepresentable {
    private let image: CIImage
    private let onTap: ((CGPoint) -> Void)?

    public init(image: CIImage, onTap: ((CGPoint) -> Void)? = nil) {
        self.image = image
        self.onTap = onTap
    }

    public func makeUIView(context: Context) -> UIScrollView {
        let scrollView = UIScrollView()
        scrollView.delegate = context.coordinator
        scrollView.maximumZoomScale = 5.0
        scrollView.minimumZoomScale = 1.0
        scrollView.showsVerticalScrollIndicator = false
        scrollView.showsHorizontalScrollIndicator = false
        scrollView.isScrollEnabled = false // Disable panning

        guard let uiImage = image.toUIImage() else {
            print("Failed to convert CIImage to UIImage")
            return scrollView
        }

        let imageView = UIImageView(image: uiImage)
        imageView.contentMode = UIView.ContentMode.scaleAspectFit
        imageView.isUserInteractionEnabled = true
        imageView.frame = scrollView.bounds
        imageView.autoresizingMask = [UIView.AutoresizingMask.flexibleWidth, UIView.AutoresizingMask.flexibleHeight]


        let tapGesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleTap(_:)))
        imageView.addGestureRecognizer(tapGesture)

        scrollView.addSubview(imageView)

        // Store references
        context.coordinator.scrollView = scrollView
        context.coordinator.imageView = imageView

        // Set initial zoom to fit the image
        context.coordinator.updateMinZoomScaleForSize()
        context.coordinator.centerImage()

        return scrollView
    }

    public func updateUIView(_ scrollView: UIScrollView, context: Context) {
        guard let imageView = context.coordinator.imageView else { return }

        let oldImageSize = imageView.image?.size ?? .zero
        guard let newImage = image.toUIImage() else {
            print("Failed to convert CIImage to UIImage in updateUIView")
            return
        }
        let newImageSize = newImage.size

        // Store current zoom scale and content offset
        let previousZoomScale = scrollView.zoomScale
        let previousContentOffset = scrollView.contentOffset

        imageView.image = newImage
        // imageView.frame will adjust automatically due to autoresizingMask

        // If image size changed, update min zoom scale
        if oldImageSize != newImageSize {
            context.coordinator.updateMinZoomScaleForSize()
        }

        // Restore zoom scale and content offset
        scrollView.zoomScale = previousZoomScale
        scrollView.contentOffset = previousContentOffset

        context.coordinator.centerImage()
    }

    public func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    public class Coordinator: NSObject, UIScrollViewDelegate {
        var parent: CIImageViewer
        weak var scrollView: UIScrollView?
        weak var imageView: UIImageView?

        init(_ parent: CIImageViewer) {
            self.parent = parent
        }

        public func viewForZooming(in scrollView: UIScrollView) -> UIView? {
            return imageView
        }

        public func scrollViewDidZoom(_ scrollView: UIScrollView) {
            centerImage()
        }

        @objc func handleTap(_ gesture: UITapGestureRecognizer) {
            guard let imageView = gesture.view as? UIImageView else { return }

            let locationInImageView = gesture.location(in: imageView)

            // Adjust for imageView's contentMode and possible scaling
            let imageSize = imageView.image?.size ?? .zero
            let imageViewSize = imageView.bounds.size

            let scaleX = imageSize.width / imageViewSize.width
            let scaleY = imageSize.height / imageViewSize.height

            let pointInImage = CGPoint(
                x: locationInImageView.x * scaleX,
                y: locationInImageView.y * scaleY
            )

            parent.onTap?(pointInImage)
        }

        func updateMinZoomScaleForSize() {
            guard let scrollView = scrollView, let imageView = imageView else { return }
            let scrollViewSize = scrollView.bounds.size
            let imageSize = imageView.image?.size ?? .zero

            guard imageSize.width > 0 && imageSize.height > 0 else { return }

            let widthScale = scrollViewSize.width / imageSize.width
            let heightScale = scrollViewSize.height / imageSize.height
            let minScale = min(widthScale, heightScale)

            // Ensure minScale is not zero or negative
            let minScaleAdjusted = max(minScale, 1.0)

            scrollView.minimumZoomScale = minScaleAdjusted
            if scrollView.zoomScale < minScaleAdjusted {
                scrollView.zoomScale = minScaleAdjusted
            }
        }

        func centerImage() {
            guard let scrollView = scrollView, let imageView = imageView else { return }

            let imageViewSize = imageView.frame.size
            let scrollViewSize = scrollView.bounds.size

            let verticalPadding = imageViewSize.height * scrollView.zoomScale < scrollViewSize.height
                ? (scrollViewSize.height - imageViewSize.height * scrollView.zoomScale) / 2
                : 0
            let horizontalPadding = imageViewSize.width * scrollView.zoomScale < scrollViewSize.width
                ? (scrollViewSize.width - imageViewSize.width * scrollView.zoomScale) / 2
                : 0

            scrollView.contentInset = UIEdgeInsets(
                top: verticalPadding,
                left: horizontalPadding,
                bottom: verticalPadding,
                right: horizontalPadding
            )
        }
    }
}
```
file:'':```
import CoreImage
import UIKit

extension CIImage {
    func toUIImage() -> UIImage? {
        let context = CIContext(options: nil)
        if let cgImage = context.createCGImage(self, from: self.extent) {
            return UIImage(cgImage: cgImage)
        }
        return nil
    }
}
```
file:'CoreMLModels/SAM2SmallImageEncoderFLOAT16.mlpackage/Manifest.json':```
{
    "fileFormatVersion": "1.0.0",
    "itemInfoEntries": {
        "CC3C435B-6B7D-4BE9-A190-E52F681DDA07": {
            "author": "com.apple.CoreML",
            "description": "CoreML Model Weights",
            "name": "weights",
            "path": "com.apple.CoreML/weights"
        },
        "F1DD83A0-F7F9-473B-94B4-63743B6BC92D": {
            "author": "com.apple.CoreML",
            "description": "CoreML Model Specification",
            "name": "model.mlmodel",
            "path": "com.apple.CoreML/model.mlmodel"
        }
    },
    "rootModelIdentifier": "F1DD83A0-F7F9-473B-94B4-63743B6BC92D"
}
```
note: the rest of the manifest.json files for the coreml packages are very similar just with different model identifiers.
Carefully analyze my app and the demo app files above to think through how my app should be modified to correctly accomplish the task given in the user's query at the beginning of this message:

Think step by step thorugh the user's task given all of the context: