<?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: Artemis</title>
    <description>The latest articles on DEV Community by Artemis (@artemis-aep).</description>
    <link>https://dev.to/artemis-aep</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%2F4105297%2F4ccf3b52-8ecc-452a-a6b9-077b78e72380.png</url>
      <title>DEV Community: Artemis</title>
      <link>https://dev.to/artemis-aep</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/artemis-aep"/>
    <language>en</language>
    <item>
      <title>Making Computer Vision Pipelines Inspectable</title>
      <dc:creator>Artemis</dc:creator>
      <pubDate>Thu, 03 Sep 2026 11:57:32 +0000</pubDate>
      <link>https://dev.to/artemis-aep/making-computer-vision-pipelines-inspectable-49jj</link>
      <guid>https://dev.to/artemis-aep/making-computer-vision-pipelines-inspectable-49jj</guid>
      <description>&lt;p&gt;Computer vision pipelines often start simple; You load an image, run a model, get some detections, and draw the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Image → Model → Detections → Annotation → Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But real computer vision applications rarely stay this simple. Soon you have preprocessing, resizing, tiling, multiple inference steps, filtering, tracking, coordinate transformations, annotations, application-specific logic, and so on.&lt;/p&gt;

&lt;p&gt;So at the end the pipeline would look more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                      ┌→ Model
                      │
Image → Resize → Tile ─→ Model → Merge → Filter → Track → Annotate → ...
                      │
                      └→ ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, &lt;strong&gt;the individual steps are understandable, but the pipeline becomes difficult to understand as a whole&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal of our experiment is to make the &lt;strong&gt;data flowing through a computer vision pipeline explicit and inspectable&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem: the final image doesn't look&amp;nbsp;right
&lt;/h2&gt;

&lt;p&gt;Consider a typical detection workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;supervision&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;sv&lt;/span&gt;

&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;detections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Detections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_inference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;detections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detections&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])]&lt;/span&gt;
&lt;span class="n"&gt;detections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detections&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;detections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confidence&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;detections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_with_detections&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detections&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;box_annotator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;scene&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;detections&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;detections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is perfectly reasonable code, but imagine that the final output doesn't look right, perhaps some objects are missing…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2n6zsba8pm8hey8foyfm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2n6zsba8pm8hey8foyfm.jpg" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where did they disappear?&lt;/li&gt;
&lt;li&gt;Did the model fail to detect them?&lt;/li&gt;
&lt;li&gt;Did a confidence threshold remove them?&lt;/li&gt;
&lt;li&gt;Did tracking discard them?&lt;/li&gt;
&lt;li&gt;Did a coordinate transformation move them?&lt;/li&gt;
&lt;li&gt;Did an annotation step simply fail to display them?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The final image can't tell you…&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You have to reconstruct the execution mentally - or start adding print statements, saving intermediate images, and inserting debugging code throughout the pipeline.&lt;/p&gt;

&lt;p&gt;This becomes even more painful as the number of operations increases.&lt;/p&gt;




&lt;h2&gt;
  
  
  Making the data flow&amp;nbsp;explicit
&lt;/h2&gt;

&lt;p&gt;The idea behind ml-pipes is simple, the author compose the pipeline as sequence of steps, the framework run the steps.&lt;br&gt;
Instead of thinking about a pipeline as a sequence of Python statements, we can represent it explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ml_pipes.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoadFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Image not found: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_bytes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nc"&gt;LoadImage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nc"&gt;Resize&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nc"&gt;Infer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nc"&gt;DecodeDetections&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nc"&gt;FilterDetections&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nc"&gt;Track&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nc"&gt;Annotate&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each operation becomes a node in the data flow.&lt;/p&gt;

&lt;p&gt;That gives us something that ordinary sequential code doesn't naturally provide:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a representation of what the pipeline actually is.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Combining ml-pipes with Supervision
&lt;/h2&gt;

&lt;p&gt;This is where Supervision fits particularly well.&lt;br&gt;
Supervision already provides many useful computer vision primitives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;detections, tracking, annotators, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than rebuilding those operations, we can simply use them as a step in our pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ml_pipes.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;supervision&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;sv&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_my_model&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ByteTrack&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="p"&gt;...,&lt;/span&gt;
      &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(...),&lt;/span&gt;
      &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Detections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_inference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update_with_detections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;...,&lt;/span&gt;
  &lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While ml-pipes accepts any callable as a step, I went ahead and created my own operator package. Using that, a detection and annotation workflow can be represented as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ml_pipes.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ml_pipes.standard&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Recall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Store&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ml_pipes.supervision&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;sv&lt;/span&gt;

&lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nc"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_frame&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RoboflowInference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Detections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FromInference&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nc"&gt;Recall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_frame&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ByteTrack&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nc"&gt;Recall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_frame&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prepend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TraceAnnotator&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BoxAnnotator&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LabelAnnotator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;show_tracker_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;show_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two different responsibilities here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supervision provides the computer-vision operations.&lt;/li&gt;
&lt;li&gt;ml-pipes provides the pipeline composition and execution model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That distinction is important. The goal isn't to create yet another computer-vision library.&lt;/p&gt;

&lt;p&gt;It's to provide a way to compose existing operations while retaining visibility into what happens between them.&lt;/p&gt;




&lt;h2&gt;
  
  
  What does "inspectable" actually&amp;nbsp;mean?
&lt;/h2&gt;

&lt;p&gt;Once the pipeline is represented explicitly, we can run inspection:&lt;br&gt;
from ml_pipes.inspection import PipelineInspector&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;PipelineInspector&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of only seeing the final output, now we can inspect the intermediate results:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz2ipudyx43twwu305wuo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz2ipudyx43twwu305wuo.png" alt="Pipeline Inspection Report" width="799" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For computer vision, this is particularly useful because the intermediate state is often something visual.&lt;/p&gt;




&lt;h2&gt;
  
  
  A more interesting example
&lt;/h2&gt;

&lt;p&gt;A more interesting example is small-object detection, where tiling introduces additional transformations:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://requiem4machines.github.io/ml-pipes-supervision/assets/detect_small_objects/inspection.html" rel="noopener noreferrer"&gt;An Interactive Pipeline Inspection Report&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This kind of pipeline is where the approach becomes particularly useful.&lt;br&gt;
When an object can disappear somewhere between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Image → Resize → Tile ─→ Model → Merge → Filter → Track → Annotate → ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;, and you want to know which step caused it.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Computer vision pipelines are data-flow problems
&lt;/h3&gt;




&lt;p&gt;One thing this experiment has made increasingly clear to me is that many CV pipelines are easier to reason about as &lt;strong&gt;data-flow graphs&lt;/strong&gt; than as collections of function calls.&lt;br&gt;
For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  ┌──────────────┐
                  │ Source Image │
                  └──────┬───────┘
                    ┌────▼────┐
                    │ Resize  │
                    └────┬────┘
                    ┌────▼────┐
                    │  Tile   │
                    └────┬────┘
              ┌──────────┴──────────┐
         ┌────▼────┐           ┌────▼────┐
         │ Model 1 │           │ Model 2 │
         └────┬────┘           └────┬────┘
              └──────────┬──────────┘
                    ┌────▼────┐
                    │  Merge  │
                    └────┬────┘
                    ┌────▼────┐
                    │ Filter  │
                    └────┬────┘
                    ┌────▼────┐
                    │ Tracker │
                    └────┬────┘
                    ┌────▼────┐
                    │Annotate │
                    └─────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you think about the system this way, inspection becomes a natural capability of the pipeline rather than something bolted on afterwards.&lt;/p&gt;




&lt;h2&gt;
  
  
  This also makes debugging more interesting
&lt;/h2&gt;

&lt;p&gt;Suppose the final output contains no detections.&lt;/p&gt;

&lt;p&gt;Without pipeline inspection, you might start debugging the model, But the problem could actually be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Image
  ✓
Resize
  ✓
Inference
  ✓  42 detections
Filter
  ✗  0 detections
Tracker
  ✗
Annotation
  ✗
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model was fine, the bug was in the filtering step.&lt;/p&gt;

&lt;p&gt;This distinction matters because computer vision systems often have many places where information can be lost.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why use Supervision?
&lt;/h2&gt;

&lt;p&gt;This experiment started from a practical observation. There are already good libraries for computer vision operations.&lt;/p&gt;

&lt;p&gt;Supervision is one example.&lt;/p&gt;

&lt;p&gt;It provides a useful collection of reusable building blocks instead of requiring every application to implement detection visualization, tracking, filtering, and related functionality from scratch.&lt;/p&gt;

&lt;p&gt;The relationship is roughly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
┌──────────────────────────────────────┐
│             Application              │
├──────────────────────────────────────┤
│              ml-pipes                │
│     composition / execution /        │
│           inspection                 │
├──────────────────────────────────────┤
│            Supervision               │
│   CV operations / tracking /         │
│      annotations / utilities         │
├──────────────────────────────────────┤
│        Models / OpenCV / etc.        │
└──────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also means that the same idea doesn't have to be tied to Supervision.&lt;/p&gt;

&lt;p&gt;The underlying concept is broader:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;make the computation graph explicit, that makes the data flowing through it inspectable.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The project is open&amp;nbsp;source
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;Supervision&lt;/em&gt; x &lt;em&gt;ml-pipes&lt;/em&gt; integration/examples are here:&lt;br&gt;
&lt;a href="https://github.com/requiem4machines/ml-pipes-supervision" rel="noopener noreferrer"&gt;https://github.com/requiem4machines/ml-pipes-supervision&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd be interested in hearing how other computer vision engineers currently debug complex pipelines.&lt;/p&gt;

&lt;p&gt;Do you save intermediate images? Use notebooks? Add custom visualization code? Rely on logs and breakpoints? Or have you found a better approach?&lt;/p&gt;

</description>
      <category>computervision</category>
      <category>python</category>
      <category>machinelearning</category>
      <category>mlops</category>
    </item>
  </channel>
</rss>
