<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dave Gordon</title>
    <description>The latest articles on DEV Community by Dave Gordon (@dave_gordon).</description>
    <link>https://dev.to/dave_gordon</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4082771%2Fcf7b9dda-01ec-46e2-be67-3aff920fc302.png</url>
      <title>DEV Community: Dave Gordon</title>
      <link>https://dev.to/dave_gordon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dave_gordon"/>
    <language>en</language>
    <item>
      <title>6 Background Removal API Tests I Run Before Shipping Video</title>
      <dc:creator>Dave Gordon</dc:creator>
      <pubDate>Fri, 21 Aug 2026 13:33:50 +0000</pubDate>
      <link>https://dev.to/dave_gordon/6-background-removal-api-tests-i-run-before-shipping-video-a4a</link>
      <guid>https://dev.to/dave_gordon/6-background-removal-api-tests-i-run-before-shipping-video-a4a</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR:
&lt;/h2&gt;

&lt;p&gt;Real-time background removal is a frame budget problem before it is a quality problem. &lt;/p&gt;

&lt;p&gt;Six tests, in this order: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;- does it fit the budget on your worst supported phone, &lt;/li&gt;
&lt;li&gt;- did the run actually use the accelerator you think it did, &lt;/li&gt;
&lt;li&gt;- what happens at the hair line, &lt;/li&gt;
&lt;li&gt;- does the mask flicker between frames, &lt;/li&gt;
&lt;li&gt;- what breaks when the lighting is real, &lt;/li&gt;
&lt;li&gt;- and what does it cost you once you count platforms instead of license fees. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most shortlists lose half their candidates on tests 1 and 5.&lt;/p&gt;

&lt;p&gt;I have swapped segmentation backends three times across two camera apps, and every swap started the same way: the library was fine in the sample project and came apart on a real device with a real user in front of it. So I stopped evaluating these things by looking at output stills.&lt;/p&gt;

&lt;p&gt;This is the harness, not the results table. I am not publishing my numbers here, because my numbers are about my pipeline and my support matrix, and neither of those is yours. A frame budget is derived from your capture rate and your effects stack, and a mid range phone in one support range is a flagship in another. &lt;/p&gt;

&lt;p&gt;What transfers is the setup and the order the tests run in. Copy the rig, run your own candidates through it, and the numbers you get will actually apply to the thing you are shipping.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rig
&lt;/h2&gt;

&lt;p&gt;Before the first test, fix these so the results mean something across candidates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Three device tiers, named, not averaged
&lt;/h3&gt;

&lt;p&gt;A current or previous cycle flagship, a mid range phone around three years old, and the cheapest device with real share in your own analytics. Record the model, OS version and chipset for each. Score every test per device. An average across the matrix hides the phone your median user is holding, which is the only phone the decision actually turns on.&lt;/p&gt;

&lt;h3&gt;
  
  
  One app state, held constant
&lt;/h3&gt;

&lt;p&gt;Camera open, your effects stack running, screen at full brightness, no other app in the foreground. Candidates measured in different app states are not comparable, and the tempting shortcut of benchmarking inference in isolation produces the number that looks best and matters least.&lt;/p&gt;

&lt;h3&gt;
  
  
  A warm device, and a stated warm-up
&lt;/h3&gt;

&lt;p&gt;Decide how long the app records before you take the sustained reading, write that duration down, and use the same one every time. Thermal state is the variable that quietly decides half of these comparisons.&lt;/p&gt;

&lt;h3&gt;
  
  
  One fixed clip set
&lt;/h3&gt;

&lt;p&gt;The same recordings, reused for every candidate: a plain wall clip, a still subject clip, and the lighting cases from Test 5. New footage per candidate means you are scoring the footage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pinned versions
&lt;/h3&gt;

&lt;p&gt;SDK or model version for every candidate, written next to every result. Vendors ship model updates that move these numbers, and a result without a version cannot be reproduced or re-checked later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does the frame budget decide this before quality does?
&lt;/h2&gt;

&lt;p&gt;Most background removal comparisons open with mask quality. That is the wrong end. If inference does not fit inside a frame, quality is irrelevant, because you are already dropping frames and the preview stutters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Work out your own budget before you look at a single demo:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Take your target capture rate. 30 fps for most social and editor video, 60 fps if the feature has to feel like the native camera.&lt;/li&gt;
&lt;li&gt;Divide 1,000 ms by that rate. 30 fps gives you 33.3 ms per frame. 60 fps gives you 16.7 ms.&lt;/li&gt;
&lt;li&gt;Subtract the rest of the pipeline. Camera capture, color space conversion, your own filters or effects, the render pass, and encoding all take a slice.&lt;/li&gt;
&lt;li&gt;What is left is your segmentation budget. Run that subtraction rather than assuming you have the whole frame, because in the pipelines I have worked in, the remainder has never been more than half.&lt;/li&gt;
&lt;li&gt;Compare that number against measured inference time on your slowest supported device, not your average one and not the phone on your desk.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Write that number down. Every test below is scored against it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test 1: Does it hold the budget on your worst target device?
&lt;/h2&gt;

&lt;p&gt;Run the candidate on the cheapest and oldest tier, in the app state fixed above.&lt;/p&gt;

&lt;p&gt;Three things to capture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cold inference time
&lt;/h3&gt;

&lt;p&gt;The first frames after model load are always slower. If your feature is triggered mid session, users feel this directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sustained inference time after thermal throttling
&lt;/h3&gt;

&lt;p&gt;This is the number that matters and the one almost nobody publishes. Measure it rather than extrapolating from the cold figure, and expect the two to be far enough apart that the cold figure is useless for capacity planning. If a vendor publishes one inference time without saying whether the device was warm, assume it is the cold one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory ceiling
&lt;/h3&gt;

&lt;p&gt;Segmentation model plus video buffers plus encoder is where low RAM Android devices start getting killed in the background. Watch dumpsys meminfo for your process across a long recording rather than reading a peak off a profiler graph once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test 2: Did the run you just measured actually use the accelerator you think it did?
&lt;/h2&gt;

&lt;p&gt;This test exists because Test 1 produces a plausible looking number whether or not it measured the pipeline you intended.&lt;/p&gt;

&lt;p&gt;The GPU delegate fails to initialise, the library falls back to CPU without raising anything to your code, the run completes, and you write down a figure that describes a completely different execution path. Nothing in the output tells you. The mask still looks correct, the app does not crash, and the number is wrong in a direction that will not reproduce on the next build.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: Failed to apply GPU delegate.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that line is in your logs, every timing you collected in that session is CPU timing. Two habits fix this permanently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assert the execution path per run. Log which delegate or backend was actually created, and discard any run where the assertion is missing rather than any run that looks slow. Slow runs are data. Runs of unknown provenance are not.&lt;/li&gt;
&lt;li&gt;Fail closed. A benchmark harness that silently completes when initialisation failed is worse than one that crashes, because it produces numbers you will act on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same applies to any library that quietly reduces input resolution or frame stride under load. If the candidate has an adaptive mode, either turn it off for the measurement or record what it chose, because otherwise you are comparing two vendors that each decided independently what to sacrifice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test 3: What happens at the hair line?
&lt;/h2&gt;

&lt;p&gt;Every segmentation model produces a clean silhouette. The difference between them lives in about forty pixels around the edge.&lt;/p&gt;

&lt;p&gt;Use the plain wall clip and score four cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loose or curly hair. Naive segmentation cuts a helmet shape and drops the strands entirely.&lt;/li&gt;
&lt;li&gt;Glasses frames, especially thin metal ones, which models frequently classify as background and slice through.&lt;/li&gt;
&lt;li&gt;An object held in hand, like a mug or a phone. Person only models will amputate it. Whether that counts as a bug depends on your use case, so decide deliberately rather than finding out in review.&lt;/li&gt;
&lt;li&gt;The gap between arm and torso when someone puts a hand on their hip. Soft mask models tend to fill it in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Score these against a hard edged replacement background, not a blur. Blur hides everything, which is exactly why so many demos use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test 4: Does the mask flicker between frames?
&lt;/h2&gt;

&lt;p&gt;This is the failure mode that gets caught in QA rather than in evaluation, and it is the most expensive one to fix late.&lt;/p&gt;

&lt;p&gt;A per frame image segmentation model has no memory of the previous frame. Feed it video and the mask boundary jitters frame to frame even when the subject is completely still. The stills look perfect. The clip looks cheap.&lt;/p&gt;

&lt;p&gt;Two ways a library can address it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Temporal architecture
&lt;/h3&gt;

&lt;p&gt;Robust Video Matting is the reference example, in that it carries state between frames with a recurrent design instead of treating each one independently. Check the license before you get attached to it: the code was re released under GPL-3.0 in September 2021, which for most proprietary mobile apps means you are studying the architecture rather than shipping the repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your own smoothing
&lt;/h3&gt;

&lt;p&gt;You can apply temporal filtering to a per frame mask yourself, but you are trading latency for stability, and that trade comes straight out of the budget you calculated above.&lt;/p&gt;

&lt;p&gt;The test is simple. Take the still subject clip, thirty seconds of someone barely moving, then step through the export frame by frame and watch the boundary. If it breathes, you either fix it or you ship it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test 5: What breaks when the room does?
&lt;/h2&gt;

&lt;p&gt;Evaluation footage is always shot in good light. Your users are on a couch at 9pm with a window behind them.&lt;/p&gt;

&lt;p&gt;Run each candidate through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backlight, with a bright window behind the subject. Classic failure: the person is segmented as background.&lt;/li&gt;
&lt;li&gt;Low light, where sensor noise destabilises the edge and Test 4 gets much worse.&lt;/li&gt;
&lt;li&gt;A busy background with furniture, a doorway, or a poster of a person on the wall.&lt;/li&gt;
&lt;li&gt;Two people in frame, where some models pick one, some merge both, and some alternate between the two across frames.&lt;/li&gt;
&lt;li&gt;Movement toward and away from the camera, which changes subject scale and is where fixed input resolutions show their limits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you automate one part of this harness, automate this one. It is the section that regresses silently when a vendor ships a model update.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test 6: What does it actually cost you per platform?
&lt;/h2&gt;

&lt;p&gt;The license fee is the visible number and usually not the deciding one. Count the real cost like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List every platform you ship on. iOS, Android, web, desktop, and any framework layer such as React Native or Flutter.&lt;/li&gt;
&lt;li&gt;Check what the candidate covers natively. Apple's Vision framework offers person segmentation with selectable quality levels, and it is iOS and macOS only, so an Android build needs a second solution and a second set of edge case behaviour to test.&lt;/li&gt;
&lt;li&gt;Multiply the platforms you have to solve separately by the integration and QA time for one platform. That multiplier, not the sticker price, is where the budget goes.&lt;/li&gt;
&lt;li&gt;Add the ongoing cost. Two segmentation backends means two model update cycles, two sets of device regressions, and visible quality differences between your iOS and Android users.&lt;/li&gt;
&lt;li&gt;Check the deployment model, which is where the arithmetic gets decisive.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Can you use a cloud background removal API for live video?
&lt;/h3&gt;

&lt;p&gt;Not realistically. Cloud background removal is priced per image and needs an upload per frame, so a 30 fps preview is 30 requests per second per user with a network round trip on every one. That is the right tool for batch photo processing and the wrong one for a live camera preview, and it fails on arithmetic before you get to quality.&lt;/p&gt;

&lt;p&gt;For the free, on device end of the shortlist, &lt;a href="https://ai.google.dev/edge/mediapipe/solutions/vision/image_segmenter" rel="noopener noreferrer"&gt;MediaPipe Image Segmenter&lt;/a&gt; is Apache 2.0, and &lt;a href="https://developers.google.com/ml-kit/vision/selfie-segmentation" rel="noopener noreferrer"&gt;ML Kit selfie segmentation&lt;/a&gt; is a closed source SDK that is free to use. Both run on device across iOS and Android, which makes them the honest baseline. Anything commercial has to beat them on the tests above by enough to justify the line item, and often it does, because Tests 1, 4 and 5 are where a general purpose model gives ground to one tuned for video.&lt;/p&gt;

&lt;h2&gt;
  
  
  The scorecard
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;What it measures&lt;/th&gt;
&lt;th&gt;Pass condition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1. Frame budget on worst device&lt;/td&gt;
&lt;td&gt;Cold and sustained inference time, memory ceiling&lt;/td&gt;
&lt;td&gt;Sustained time fits the segmentation budget on the slowest supported device, not the average one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Execution path&lt;/td&gt;
&lt;td&gt;Whether the accelerator you configured is the one that ran&lt;/td&gt;
&lt;td&gt;Delegate asserted in the log on every run, runs without the assertion discarded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. Hair line&lt;/td&gt;
&lt;td&gt;Edge quality on hair, thin frames, held objects, arm to torso gaps&lt;/td&gt;
&lt;td&gt;Scored against a hard edged background, not a blur&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. Temporal stability&lt;/td&gt;
&lt;td&gt;Mask jitter across frames on a still subject&lt;/td&gt;
&lt;td&gt;Boundary holds frame to frame, or the smoothing cost is inside the budget&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5. Real conditions&lt;/td&gt;
&lt;td&gt;Backlight, low light, busy background, two people, scale change&lt;/td&gt;
&lt;td&gt;No condition drops below your quality floor, and the suite is automated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6. Cost per platform&lt;/td&gt;
&lt;td&gt;Platforms solved separately, integration and QA multiplier, deployment model&lt;/td&gt;
&lt;td&gt;One integration covers your matrix, or the second one is budgeted&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pick your slowest supported device and your worst lighting scenario first, then evaluate. Run these six in order and you will usually eliminate half the shortlist before pricing comes up, and you will eliminate it for reasons you can put in front of a product manager.&lt;/p&gt;

&lt;p&gt;If you run this harness on something I have not tried, I would like to hear what broke.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>android</category>
      <category>ios</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
