<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Computer Vision |</title><link>https://changshenghsun.com/tags/computer-vision/</link><atom:link href="https://changshenghsun.com/tags/computer-vision/index.xml" rel="self" type="application/rss+xml"/><description>Computer Vision</description><generator>HugoBlox Kit (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Sat, 20 Jun 2026 00:00:00 +0000</lastBuildDate><image><url>https://changshenghsun.com/media/icon_hu_d41247041d33f7ba.png</url><title>Computer Vision</title><link>https://changshenghsun.com/tags/computer-vision/</link></image><item><title>GridStage</title><link>https://changshenghsun.com/projects/gridstage/</link><pubDate>Sat, 20 Jun 2026 00:00:00 +0000</pubDate><guid>https://changshenghsun.com/projects/gridstage/</guid><description>&lt;p&gt;&lt;strong&gt;2026 — in active development&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="why-i-built-this"&gt;Why I built this&lt;/h2&gt;
&lt;p&gt;As a dancer, formations were always the part nobody wanted to handle. How do you
place everyone so the stage actually looks right? Will two people run into each
other halfway through a transition? Those questions bothered me every time we
put a piece together. I went looking for an open-source tool and there was
none — and I was not about to pay a subscription for the commercial ones. So I
built &lt;strong&gt;GridStage&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="what-it-is"&gt;What it is&lt;/h2&gt;
&lt;p&gt;A top-down 2D stage where you place performers, add formations along a timeline
synced to the music, and preview the walk-in transitions in 2D or 3D. It exports
walk-chart PDFs (one page per dancer), videos, and GIFs. Several people can edit
the same piece at once — live cursors, presence, and undo that only reverts your
own changes — over a CRDT, a data structure where concurrent edits merge without
a central referee.
The part I care most about is the vision pipeline: pause a rehearsal video on a
formation, click the four stage corners once, and the app detects the dancers,
maps their foot positions onto the stage floor, and drops them into the editor
as a normal editable formation. All of it runs in the browser.&lt;/p&gt;
&lt;h2 id="where-it-stands"&gt;Where it stands&lt;/h2&gt;
&lt;p&gt;v0.8.6 · 130 commits · ~16k lines of strict-mode TypeScript in a pnpm monorepo ·
24 unit-test files plus a Playwright end-to-end suite. Three ways to run it, none
requiring an account: a Windows/macOS desktop app, an installable PWA that works
offline on a phone, and a Docker self-host stack.&lt;/p&gt;
&lt;p&gt;Measured, not claimed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;60 performers × 4 formations&lt;/strong&gt; in a permanent end-to-end stress test —
dragging stays accurate and playback holds ~57 fps (CI floor 20).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Video → formation capture&lt;/strong&gt; runs entirely client-side (YOLOX-nano through
onnxruntime-web, WebGPU with a wasm fallback): 6 of 7 dancers recovered from a
real six-dancer rehearsal photo, ~1.8 s per frame on CPU alone. Zero server
cost, and no one&amp;rsquo;s rehearsal footage leaves their laptop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Against &lt;strong&gt;ArrangeUs&lt;/strong&gt;, the category leader, and the other commercial formation
apps I audited before starting, these are the things they do not do at all
(checked July 2026; their feature lists do move):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;GridStage&lt;/th&gt;
&lt;th&gt;ArrangeUs &amp;amp; other commercial apps&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Real-time multi-user editing&lt;/td&gt;
&lt;td&gt;CRDT (Yjs) — live cursors, presence, per-user undo&lt;/td&gt;
&lt;td&gt;one editor at a time, files passed around&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formations captured from a rehearsal video&lt;/td&gt;
&lt;td&gt;on-device detection + homography&lt;/td&gt;
&lt;td&gt;nothing on the market does this&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rehearsal-vs-plan review&lt;/td&gt;
&lt;td&gt;per-dancer drift, C-position centering, left–right asymmetry&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transition collision warnings&lt;/td&gt;
&lt;td&gt;Hungarian matching + sweep-line crossing detection&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3D preview&lt;/td&gt;
&lt;td&gt;camera presets + follow-a-performer&lt;/td&gt;
&lt;td&gt;limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Video / GIF export with music&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Price and licence&lt;/td&gt;
&lt;td&gt;free, MIT, self-hostable&lt;/td&gt;
&lt;td&gt;subscription, closed source&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The place I am clearly behind is distribution, not capability: ArrangeUs has
457k+ users and years of tutorials, GridStage has an install page.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Building a user community.&lt;/strong&gt; The capability gap is closed; the distribution
one is not. Demo videos and an in-app guide are already in progress, and the
next step is getting the editor in front of real dance teams and cheer squads
and running on their feedback — the work that decides whether GridStage stays
a portfolio piece or becomes a tool people actually choreograph with.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accounts and cloud sync.&lt;/strong&gt; Today a piece lives on one machine and travels as
a &lt;code&gt;.gridstage&lt;/code&gt; file. This is the largest remaining gap against commercial
tools, and the one architectural decision I still owe the project — self-host
or a hosted backend.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Full rehearsal-vs-plan deviation report.&lt;/strong&gt; The first cut compares one paused
frame against the plan. Scanning a whole rehearsal into a per-formation error
timeline is the version that closes the plan → rehearse → correct loop, and
it is the hardest part for anyone to copy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stage lighting and cue sheets.&lt;/strong&gt; Colored lights on the 2D stage and a 3D
wash, cues on the timeline, exported as a printable cue sheet — the other half
of what a stage manager actually needs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plugins and a community template library&lt;/strong&gt;, so formation libraries can grow
without me writing every one of them.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>MakeNTU</title><link>https://changshenghsun.com/projects/makentu/</link><pubDate>Fri, 08 May 2026 00:00:00 +0000</pubDate><guid>https://changshenghsun.com/projects/makentu/</guid><description>&lt;p&gt;&lt;strong&gt;May 8–10, 2026&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="the-brief"&gt;The brief&lt;/h2&gt;
&lt;p&gt;AUO makes display panels, and the sponsor prompt came down to a single
question: if the windshield itself becomes the display, what is actually worth
putting on it? The easy answer is a bigger speedometer. We treated it as an
interface problem instead — the glass should show what the driver&amp;rsquo;s current
state calls for, and stay out of the way otherwise.&lt;/p&gt;
&lt;h2 id="what-we-built"&gt;What we built&lt;/h2&gt;
&lt;p&gt;A first-person driving simulator that runs entirely in the browser, built on
real map data: the Xinyi district road network pulled live from OpenStreetMap
Overpass, routes from OSRM, and procedurally generated buildings filling the
blocks in between. Four scenarios ship — city, highway, coastal drive, and a
parked rest stop.&lt;/p&gt;
&lt;p&gt;The windshield is composed in four layers, so the result reads as glass rather
than as a game overlay:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What is in it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;4 · Glass&lt;/td&gt;
&lt;td&gt;Vignette, film grain, top tint, A-pillars and mirror silhouettes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 · HUD&lt;/td&gt;
&lt;td&gt;Frosted DOM/SVG panels — speed, compass, nav card, ETA, climate, now playing, minimap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 · AR&lt;/td&gt;
&lt;td&gt;POI cards projected into screen space onto building facades, fading when occluded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 · World&lt;/td&gt;
&lt;td&gt;Three.js — roads, buildings, cabin, traffic agents, route arrows, destination pillar&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;And the subsystems that make it more than a renderer:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Subsystem&lt;/th&gt;
&lt;th&gt;How it works&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Driver monitoring&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MediaPipe FaceLandmarker blendshapes feed one state machine with two outputs. Hard warning: both eyes shut past 1.2 s, cooldown-gated so it does not machine-gun at a driver who is genuinely asleep. Continuous 0–1 fatigue score: rolling PERCLOS (a one-minute EMA of eyelid closure) combined with yawn rate — a jaw held open past 0.55 for 800 ms counts as a yawn, over a two-minute window.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fatigue-driven HUD&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The score is the interesting half. As fatigue climbs the HUD raises contrast on rear traffic, sheds non-essential chrome, and drifts wake-up particles across the glass — the display responds to the driver instead of shouting at them.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Head-pose lane-change intent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Looking more than 18° to one side for 800 ms reads as a shoulder check, and the panel surfaces rear traffic in that adjacent lane. Releases at a looser 8° with a 500 ms cooldown — hysteresis, so a brief glance forward does not flicker the panel on and off.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Door-opening safety gate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;In rest mode the car is parked roadside with traffic flowing past. Asking to get out forward-simulates the traffic schedule: if any vehicle will come within 12 m of the door in the next 4 s the gate stays red, otherwise a tap confirms.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Voice + AI reservation call&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Speech recognition is always listening. Say 「找餐廳」and five nearby POIs come back from Overpass; pick one by number and the in-car assistant dials it, speaking a reservation script over TTS while the overlay renders the transcript as chat bubbles. Phone numbers carry a separate spoken form so Chinese TTS reads them digit by digit instead of turning 09 into a place value.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Four-screen cabin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Each screen runs the same app with &lt;code&gt;?role=driver / passenger / left / right&lt;/code&gt;. The driver publishes state snapshots to a small WebSocket relay that fans them out unparsed; each role applies its own camera offset — passenger 0.45 m inboard of the right door, side windows yawed ±90° — and keeps its own mouse-look on top, so a passenger can still turn their head independently.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Physical steering wheel&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You drive it with an actual wheel. Steering angle comes off a six-axis IMU (three-axis accelerometer plus gyroscope) mounted on the wheel itself, so the rotation is read directly from how the wheel moves — no shaft encoder or mechanical end-stops to fabricate in three days. An Arduino Nano packages the reading as one JSON line per packet at 115200 baud, roughly 20 packets a second, which the browser reads through Web Serial. Every value is clamped on arrival so a noisy reading cannot push the vehicle physics past its own caps, and the 60 fps render interpolates between packets rather than stepping.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;
&lt;figure id="figure-demoing-to-the-judges--hands-on-the-laser-cut-control-rig-the-simulator-running-on-the-screen-in-front-of-me"&gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="Demoing the simulator to the judges with hands on the laser-cut control rig"
srcset="https://changshenghsun.com/projects/makentu/judging-demo_hu_ba6de5e9835c4b8.webp 320w, https://changshenghsun.com/projects/makentu/judging-demo_hu_769bc8dca1b8db81.webp 480w, https://changshenghsun.com/projects/makentu/judging-demo_hu_cbdeeb749b3a6359.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://changshenghsun.com/projects/makentu/judging-demo_hu_ba6de5e9835c4b8.webp"
width="760"
height="507"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;figcaption&gt;
Demoing to the judges — hands on the laser-cut control rig, the simulator running on the screen in front of me.
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id="my-role"&gt;My role&lt;/h2&gt;
&lt;p&gt;I was the team lead. The sponsor prompt named a display, not a product, so most
of the work up front was deciding what a windshield should actually do — I ran
that ideation with the team, and then led the software side through to a working
demo. My own commits landed the highway, coastal and rest-stop scenarios, the
driver-monitoring pipeline and everything the fatigue score drives, collision
handling, the head-pose lane-change intent, and the exit gate.&lt;/p&gt;
&lt;p&gt;Roughly 20k lines of TypeScript went in over the three days, with
194 unit tests across 16 files covering the pure logic — the state machines,
geometry and traffic simulation are all written as pure functions precisely so
they could be tested at hackathon speed instead of debugged through the UI.&lt;/p&gt;
&lt;p&gt;
&lt;figure id="figure-team-37-nothingunique--劉允中-賴泓安-鄭竣陽-胡軒綸-張勝勛--at-our-bench-on-the-auo-track"&gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="The five-person team at our bench during the hackathon"
srcset="https://changshenghsun.com/projects/makentu/team-bench_hu_c3d440bc2ec8763f.webp 320w, https://changshenghsun.com/projects/makentu/team-bench_hu_c0affe0bea0380a6.webp 480w, https://changshenghsun.com/projects/makentu/team-bench_hu_1461225f7144b0f0.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://changshenghsun.com/projects/makentu/team-bench_hu_c3d440bc2ec8763f.webp"
width="760"
height="507"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;figcaption&gt;
Team 37, NoThingUnique — 劉允中, 賴泓安, 鄭竣陽, 胡軒綸, 張勝勛 — at our bench on the AUO track.
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id="results"&gt;Results&lt;/h2&gt;
&lt;p&gt;First place, AUO corporate award.&lt;/p&gt;</description></item></channel></rss>