Skip to content

Instantly share code, notes, and snippets.

@jsburklund
Created November 26, 2017 21:28
Show Gist options
  • Save jsburklund/45e7bb3c2763ba6b4c910b1c7a340a4d to your computer and use it in GitHub Desktop.
Save jsburklund/45e7bb3c2763ba6b4c910b1c7a340a4d to your computer and use it in GitHub Desktop.
Pipeline for finding cubes in the 2018 FTC game.
This is a sample image processing pipeline to extract cubes (glyphs) from an image
of an FTC competition arena. The pipeline is as follows:
1. Resize the image. This not only increases processing speed of subsequent functions,
but also removes any higher-freqeuncy information that may cause problems with edge detection.
(Like image sensor noise, aliasing, or high-frequency patterns in the mats and glyphs)
2. HSV Threshold. This thresholds image colors to extract regions where the image is similar
in color to the grey and brown glyphs.
3. Bitwise Or. Combine the two HSV Threshold color masks to simplify extraction of cubes in general.
Colors can be resegmented between brown and grey can be performed after the countours step.
4. Canny Edge Detection. Extract color/edge transitions in the image. The min and max thresholds are
set to approximately emphasize major edge transitions and reduce noise.
5. Combine Thresholds and Edges. Canny edges are binary not-ed so that edge transitions can be
preserved in the HSV Threshold masks. This is primarily used to better segment the grey cubes from
the grey floor mats. After the edges are inverted the image is binary and-ed with the HSV mask,
and the resulting image has better masks for cube faces.
6. Find and Filter Contours. Contours are extracted from the combined color and edge mask to find regions
that approximate the faces of the cubes (Glyphs). The filters are set such that only relatively large,
square-ish contours remain.
<grip:Pipeline>
<sources>
<grip:ImageFile>
<property name="path" value="glyph2.jpg"/>
</grip:ImageFile>
</sources>
<steps>
<grip:Step name="CV resize">
<grip:Input step="0" socket="0"/>
<grip:Input step="0" socket="1"/>
<grip:Input step="0" socket="2">
<value>0.15</value>
</grip:Input>
<grip:Input step="0" socket="3">
<value>0.15</value>
</grip:Input>
<grip:Input step="0" socket="4">
<value>INTER_LINEAR</value>
</grip:Input>
<grip:Output step="0" socket="0" previewed="true"/>
</grip:Step>
<grip:Step name="HSV Threshold">
<grip:Input step="1" socket="0"/>
<grip:Input step="1" socket="1">
<value>
<double>0.0</double>
<double>180.0</double>
</value>
</grip:Input>
<grip:Input step="1" socket="2">
<value>
<double>0.0</double>
<double>47.19015280135823</double>
</value>
</grip:Input>
<grip:Input step="1" socket="3">
<value>
<double>0.0</double>
<double>151.0950764006791</double>
</value>
</grip:Input>
<grip:Output step="1" socket="0" previewed="false"/>
</grip:Step>
<grip:Step name="HSV Threshold">
<grip:Input step="2" socket="0"/>
<grip:Input step="2" socket="1">
<value>
<double>0.0</double>
<double>37.97872340425532</double>
</value>
</grip:Input>
<grip:Input step="2" socket="2">
<value>
<double>43.220338983050844</double>
<double>171.35638297872342</double>
</value>
</grip:Input>
<grip:Input step="2" socket="3">
<value>
<double>9.6045197740113</double>
<double>110.31914893617021</double>
</value>
</grip:Input>
<grip:Output step="2" socket="0" previewed="false"/>
</grip:Step>
<grip:Step name="CV bitwise_or">
<grip:Input step="3" socket="0"/>
<grip:Input step="3" socket="1"/>
<grip:Output step="3" socket="0" previewed="false"/>
</grip:Step>
<grip:Step name="CV Canny">
<grip:Input step="4" socket="0"/>
<grip:Input step="4" socket="1">
<value>96.0</value>
</grip:Input>
<grip:Input step="4" socket="2">
<value>29.0</value>
</grip:Input>
<grip:Input step="4" socket="3">
<value>3</value>
</grip:Input>
<grip:Input step="4" socket="4">
<value>false</value>
</grip:Input>
<grip:Output step="4" socket="0" previewed="false"/>
</grip:Step>
<grip:Step name="CV dilate">
<grip:Input step="5" socket="0"/>
<grip:Input step="5" socket="1"/>
<grip:Input step="5" socket="2"/>
<grip:Input step="5" socket="3">
<value>1</value>
</grip:Input>
<grip:Input step="5" socket="4">
<value>BORDER_CONSTANT</value>
</grip:Input>
<grip:Input step="5" socket="5"/>
<grip:Output step="5" socket="0" previewed="false"/>
</grip:Step>
<grip:Step name="CV bitwise_not">
<grip:Input step="6" socket="0"/>
<grip:Output step="6" socket="0" previewed="false"/>
</grip:Step>
<grip:Step name="CV bitwise_and">
<grip:Input step="7" socket="0"/>
<grip:Input step="7" socket="1"/>
<grip:Output step="7" socket="0" previewed="false"/>
</grip:Step>
<grip:Step name="Find Contours">
<grip:Input step="8" socket="0"/>
<grip:Input step="8" socket="1">
<value>true</value>
</grip:Input>
<grip:Output step="8" socket="0" previewed="false"/>
</grip:Step>
<grip:Step name="Filter Contours">
<grip:Input step="9" socket="0"/>
<grip:Input step="9" socket="1">
<value>60.0</value>
</grip:Input>
<grip:Input step="9" socket="2">
<value>0</value>
</grip:Input>
<grip:Input step="9" socket="3">
<value>41.0</value>
</grip:Input>
<grip:Input step="9" socket="4">
<value>1000</value>
</grip:Input>
<grip:Input step="9" socket="5">
<value>0</value>
</grip:Input>
<grip:Input step="9" socket="6">
<value>1000</value>
</grip:Input>
<grip:Input step="9" socket="7">
<value>
<int>0</int>
<int>100</int>
</value>
</grip:Input>
<grip:Input step="9" socket="8">
<value>1000000</value>
</grip:Input>
<grip:Input step="9" socket="9">
<value>0</value>
</grip:Input>
<grip:Input step="9" socket="10">
<value>0.6</value>
</grip:Input>
<grip:Input step="9" socket="11">
<value>1.3</value>
</grip:Input>
<grip:Output step="9" socket="0" previewed="true"/>
</grip:Step>
</steps>
<connections>
<grip:Connection>
<grip:Output step="0" socket="0" previewed="true"/>
<grip:Input step="2" socket="0"/>
</grip:Connection>
<grip:Connection>
<grip:Output step="1" socket="0" previewed="false"/>
<grip:Input step="3" socket="0"/>
</grip:Connection>
<grip:Connection>
<grip:Output source="0" socket="0" previewed="false"/>
<grip:Input step="0" socket="0"/>
</grip:Connection>
<grip:Connection>
<grip:Output step="4" socket="0" previewed="false"/>
<grip:Input step="5" socket="0"/>
</grip:Connection>
<grip:Connection>
<grip:Output step="7" socket="0" previewed="false"/>
<grip:Input step="8" socket="0"/>
</grip:Connection>
<grip:Connection>
<grip:Output step="6" socket="0" previewed="false"/>
<grip:Input step="7" socket="1"/>
</grip:Connection>
<grip:Connection>
<grip:Output step="2" socket="0" previewed="false"/>
<grip:Input step="3" socket="1"/>
</grip:Connection>
<grip:Connection>
<grip:Output step="3" socket="0" previewed="false"/>
<grip:Input step="7" socket="0"/>
</grip:Connection>
<grip:Connection>
<grip:Output step="0" socket="0" previewed="true"/>
<grip:Input step="1" socket="0"/>
</grip:Connection>
<grip:Connection>
<grip:Output step="5" socket="0" previewed="false"/>
<grip:Input step="6" socket="0"/>
</grip:Connection>
<grip:Connection>
<grip:Output step="0" socket="0" previewed="true"/>
<grip:Input step="4" socket="0"/>
</grip:Connection>
<grip:Connection>
<grip:Output step="8" socket="0" previewed="false"/>
<grip:Input step="9" socket="0"/>
</grip:Connection>
</connections>
<settings>
<teamNumber>0</teamNumber>
<publishAddress>roboRIO-0-FRC.local</publishAddress>
<deployAddress>roboRIO-0-FRC.local</deployAddress>
<deployDir>/home/lvuser</deployDir>
<deployUser>lvuser</deployUser>
<deployJavaHome>/usr/local/frc/JRE/</deployJavaHome>
<deployJvmOptions>-Xmx50m -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:MaxNewSize=16m</deployJvmOptions>
</settings>
<codeGenerationSettings>
<language>Java</language>
<className>GripPipeline</className>
<implementWpilibPipeline>false</implementWpilibPipeline>
<saveDir>/home/jordanbrobots/GRIP</saveDir>
<packageName></packageName>
<moduleName>grip</moduleName>
</codeGenerationSettings>
</grip:Pipeline>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment