Lift
something lifted off the background, two ways, with a segmenter parameter switching between them: people composited over a drawn, drifting gradient (background replacement in a few lines, with the tinted matte serving as the drop shadow), or the salient subject in a spotlight, where the frame is dimmed and the cutout stays at full color with a halo from the tinted matte (PersonSegmenter, SubjectSegmenter, matte, cutout, count)
- Run
swift run --package-path Examples Example-Vision-Lift- Imports
OllinVisionOllinSamplePhotos- Source
- Examples/Vision/Lift/Sketch.swift
import Ollin
import OllinSamplePhotos
import OllinVision
/// Something lifted off the background, two ways. A `PersonSegmenter` turns the
/// camera frame into a soft people matte and the cutout it makes; a
/// `SubjectSegmenter` finds whatever stands out instead (the thing you hold up,
/// or you yourself). The `segmenter` parameter (in the inspector, press ⌘/) switches
/// between them, and each keeps its own composite: people land over a drifting
/// gradient with the matte, tinted dark and nudged, as the drop shadow
/// underneath (background replacement in a few lines), while subjects glow at
/// full color over the dimmed room, with a warm halo from the tinted matte.
///
/// The matte and cutout are plain `Image`s, so anything a sketch can draw can
/// sit behind (or in front of) whatever was lifted.
@main
final class Lift: Sketch {
enum Segmenter: String, CaseIterable, ParamOption { case person, subject }
// A camera where this Mac has one, and a bundled photograph where it does
// not, so there is always a person to cut out. `--photo` takes the picture even
// where a camera would have worked, which is how a still of this sketch is made.
let feed = Camera.orStill(SamplePhoto.dancer.load())
lazy var people = PersonSegmenter(feed)
lazy var subjects = SubjectSegmenter(feed)
let backdrop = Ramp(stops: [(0.0, Color(hex: 0x16275B)),
(0.5, Color(hex: 0x3C6DD0)),
(1.0, Color(hex: 0xF7B267))], in: .oklch)
@Param var segmenter = Segmenter.person
override func draw() {
switch segmenter {
case .person: drawPeople()
case .subject: drawSubjects()
}
}
/// The person mode: the camera's people composited over a drawn background,
/// with the matte doubling as their drop shadow.
func drawPeople() {
// The replacement background: a slowly swinging linear gradient.
let swing = sin(time * 0.3) * width * 0.35
noStroke()
fill(.linear(from: Vector2(width / 2 - swing, 0),
to: Vector2(width / 2 + swing, height), backdrop))
drawRect(0, 0, width, height)
// The raw frame never draws here (only its matte and cutout do), so this
// takes the typed path: `fittedRectangle` for the rectangle and
// `drawStatus` for the waiting notice, instead of `drawFrame`.
guard let rect = feed.fittedRectangle(in: bounds) else {
return drawStatus(feed.waitingMessage)
}
// If the segmentation model can't run on this Mac (no compute device),
// say so on the canvas instead of silently showing only the gradient.
if let reason = people.unavailableReason {
return drawStatus(reason, style: .warning)
}
// Drop shadow: the matte tinted translucent black, nudged down and right.
if let matte = people.matte {
tint(Color(white: 0, alpha: 0.4))
drawImage(matte, in: Rectangle(x: rect.x + 14 * scale, y: rect.y + 18 * scale,
width: rect.width, height: rect.height))
noTint()
}
// The cutout: you, over whatever the sketch painted.
if let cutout = people.cutout {
drawImage(cutout, in: rect)
}
drawCaption("Lift: the feed's people over a drawn background")
}
/// The subject mode: whatever stands out to the feed, lifted into a
/// spotlight against the dimmed room.
func drawSubjects() {
background(Color(white: 0.04))
// The room, dimmed to a murmur.
tint(Color(white: 0.3))
guard let rect = drawFrame(feed) else { return noTint() }
noTint()
// The same guard as the person mode: name the reason on the canvas.
if let reason = subjects.unavailableReason {
return drawStatus(reason, style: .warning)
}
// A warm halo: the matte tinted and drawn slightly enlarged behind the
// cutout, so the subjects glow against the dimmed room.
if let matte = subjects.matte {
let grow = 8 * scale
tint(Color(red: 1.0, green: 0.78, blue: 0.3, alpha: 0.5))
drawImage(matte, in: Rectangle(x: rect.x - grow, y: rect.y - grow,
width: rect.width + grow * 2,
height: rect.height + grow * 2))
noTint()
}
// The lifted subjects at full color.
if let cutout = subjects.cutout {
drawImage(cutout, in: rect)
}
let n = subjects.count
drawCaption("Lift: \(n) \(n == 1 ? "subject" : "subjects") in the spotlight")
}
}