BBMetalImage

repository·master·Indexed 21 days ago

https://github.com/silence-github/bbmetalimage

A high-performance Swift library for GPU-accelerated image and video processing using Metal. It provides a consumer-based architecture for building processing pipelines with sources (such as BBMetalCamera, BBMetalVideoSource, and BBMetalUISource) and consumers (including over 80 built-in filters, BBMetalView, and BBMetalVideoWriter). The library supports complex filter chains, synchronous and asynchronous image processing, and real-time camera capture and recording.

Tokens
2.8K
Snippets
9
Records
9
Agent score
27%

What's inside BBMetalImage

  1. How filter chains and consumers work together

    master

    BBMetalImage uses a consumer-based architecture to build processing pipelines.

    1. Sources: Objects like BBMetalCamera, BBMetalVideoSource, BBMetalStaticImageSource, or BBMetalUISource act as the origin of data (textures/audio).
    2. Consumers: Filters (e.g., BBMetalContrastFilter) and output targets (e.g., BBMetalView, BBMetalVideoWriter) act as consumers.
    3. Chaining: You connect them using the .add(consumer:) method. A source can feed a filter, which then feeds another filter, eventually reaching a final consumer like a view or a video writer.
    4. Audio: For video-related tasks, you must explicitly set the audioConsumer on the source (e.g., camera.audioConsumer = videoWriter) to ensure audio is recorded alongside the processed video.
    // Example of a chain: Camera -> Filter 1 -> Filter 2 -> MetalView
    camera.add(consumer: filter1)
        .add(consumer: filter2)
        .add(consumer: metalView)
  2. Capture photos from the camera

    master

    There are two primary ways to capture a photo using BBMetalCamera:

    This method is faster and provides the original frame texture. If you have filters in your chain, you can retrieve the filtered result by using addCompletedHandler(_:) on the filter.

    2. Using takePhoto() (Via Delegate)

    This method is slower and provides the original (unfiltered) frame texture. To use it, you must set camera.canTakePhoto = true and assign a photoDelegate conforming to BBMetalCameraPhotoDelegate.

    // Delegate method for takePhoto()
    func camera(_ camera: BBMetalCamera, didOutput texture: MTLTexture) {
        // Note: this texture is the original photo, not filtered
    }
    // Example using capturePhoto with a filter handler
    filter.addCompletedHandler { [weak self] info in
        guard info.isCameraPhoto else { return }
        switch info.result {
        case let .success(texture):
            let image = texture.bb_image
            // Use image
        case let .failure(error):
            // Handle error
        }
    }
  3. Capture and record video with a camera

    master

    To capture, preview, and record video simultaneously, you need to set up a BBMetalCamera, a chain of filters, a BBMetalView for preview, and a BBMetalVideoWriter for recording.

    Key steps:

    1. Initialize BBMetalCamera and BBMetalVideoWriter.
    2. Connect the camera's audio to the video writer: camera.audioConsumer = videoWriter.
    3. Build the chain: camera.add(consumer: filter).add(consumer: metalView).
    4. Connect the last filter in the chain to the video writer: lastFilter.add(consumer: videoWriter).
    5. Call camera.start() and videoWriter.start().
    // Hold camera and video writer
    var camera: BBMetalCamera!
    var videoWriter: BBMetalVideoWriter!
    
    func setup() {
        camera = BBMetalCamera(sessionPreset: .hd1920x1080)
    
        let contrastFilter = BBMetalContrastFilter(contrast: 3)
        let lookupFilter = BBMetalLookupFilter(lookupTable: UIImage(named: "test_lookup")!.bb_metalTexture!)
        let sharpenFilter = BBMetalSharpenFilter(sharpeness: 1)
    
        let metalView = BBMetalView(frame: frame)
        view.addSubview(metalView)
    
        let filePath = NSTemporaryDirectory() + "test.mp4"
        let url = URL(fileURLWithPath: filePath)
        videoWriter = BBMetalVideoWriter(url: url, frameSize: camera.textureSize)
    
        camera.audioConsumer = videoWriter
    
        camera.add(consumer: contrastFilter)
            .add(consumer: lookupFilter)
            .add(consumer: sharpenFilter)
            .add(consumer: metalView)
    
        sharpenFilter.add(consumer: videoWriter)
    
        camera.start()
        videoWriter.start()
    }
    
    func finishRecording() {
        videoWriter.finish { /* callback */ }
    }
  4. Record a UIView animation

    master

    Use BBMetalUISource to capture snapshots of a UIView and transmit them as a video stream.

    1. Initialize BBMetalUISource(view: animationView).
    2. Set up a BBMetalVideoWriter using the uiSource.renderPixelSize.
    3. Build the chain: uiSource.add(consumer: filter).add(consumer: videoWriter).
    4. Start the writer: videoWriter.start().
    5. In a loop (e.g., using CADisplayLink), call uiSource.transmitTexture(with: sampleTime) to capture each frame of the animation.
    // Inside a CADisplayLink selector
    @objc func refreshDisplayLink(_ link: CADisplayLink) {
        // Update UI animation...
        uiSource.transmitTexture(with: sampleTime)
    }
  5. Process a static image synchronously or asynchronously

    master

    Use BBMetalStaticImageSource to process a single image.

    Synchronous Processing

    Set imageSource.runSynchronously = true and ensure the last filter in the chain is the one you want to extract the result from. After calling imageSource.transmitTexture(), you can access the result via filter.outputTexture?.bb_image.

    Asynchronous Processing

    Add a completion handler to the last filter in the chain using addCompletedHandler { ... }. Call imageSource.transmitTexture() to begin processing.

    // Synchronous example
    let imageSource = BBMetalStaticImageSource(image: image)
    imageSource.add(consumer: filter1)
        .add(consumer: filter2)
        .runSynchronously = true
    
    imageSource.transmitTexture()
    let filteredImage = filter2.outputTexture?.bb_image
  6. Process a video file

    master

    To process an existing video file, use BBMetalVideoSource and BBMetalVideoWriter.

    1. Initialize BBMetalVideoSource with the file URL.
    2. Initialize BBMetalVideoWriter with an output URL and frame size.
    3. Connect the source's audio to the writer: videoSource.audioConsumer = videoWriter.
    4. Build the filter chain starting from the source and ending at the writer.
    5. Call videoWriter.start() and then videoSource.start { ... }.
    6. The completion handler of videoSource.start is called when all video data is processed; call videoWriter.finish inside it.
    // Set up video source and writer
    videoSource = BBMetalVideoSource(url: sourceURL)
    videoSource.audioConsumer = videoWriter
    
    // Set up filter chain
    videoSource.add(consumer: contrastFilter)
        .add(consumer: lookupFilter)
        .add(consumer: sharpenFilter)
        .add(consumer: videoWriter)
    
    videoWriter.start()
    
    videoSource.start { [weak self] (_) in
        self?.videoWriter.finish { /* callback */ }
    }
  7. Get a filtered image synchronously with a single filter

    master

    The simplest way to apply a filter to an existing UIImage is to call the filteredImage(with:) method directly on a filter instance. This operation is synchronous.

    let filteredImage = BBMetalContrastFilter(contrast: 3).filteredImage(with: image)
  8. Reference: Built-in Filters

    master

    BBMetalImage includes over 80 built-in filters for various effects including brightness, color adjustments, blurs, transformations, and artistic styles.

    // Supported filter categories include:
    // - Brightness, Exposure, Contrast, Saturation, Gamma, Levels, Color Matrix, RGBA, Hue, Vibrance, White Balance
    // - Highlight Shadow, Highlight Shadow Tint, Lookup, Color Inversion, Monochrome, False Color, Haze, Luminance
    // - Erosion, Dilation, Chroma Key, Crop, Resize, Rotate, Flip, Transform, Sharpen, Unsharp Mask
    // - Blurs: Gaussian, Box, Zoom, Motion, Tilt Shift
    // - Blend Modes: Normal, Chroma Key, Dissolve, Add, Subtract, Multiply, Divide, Overlay, Darken, Lighten, Color, Color Burn, Color Dodge, Screen, Exclusion, Difference, Hard Light, Soft Light, Alpha, Source Over, Hue, Saturation, Luminosity, Linear Burn, Mask
    // - Artistic: Pixellate, Polar Pixellate, Polka Dot, Halftone, Crosshatch, Sketch, Threshold Sketch, Toon, Posterize, Vignette, Kuwahara, Swirl, Bulge, Pinch
    // - Convolution: 3x3, Emboss, Sobel Edge Detection, Bilateral Blur, Beauty