<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://phenoworks.org/blog</id>
    <title>PhenoWorks Blog</title>
    <updated>2026-09-03T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://phenoworks.org/blog"/>
    <subtitle>PhenoWorks Blog</subtitle>
    <icon>https://phenoworks.org/img/phenoworks-logo.svg</icon>
    <entry>
        <title type="html"><![CDATA[Building Reusable LgoPy Analytical Blocks for PhenoWorks]]></title>
        <id>https://phenoworks.org/blog/building-custom-lgopy-blocks</id>
        <link href="https://phenoworks.org/blog/building-custom-lgopy-blocks"/>
        <updated>2026-09-03T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[An image-processing script works well on your first dataset. Months later,]]></summary>
        <content type="html"><![CDATA[<p>An image-processing script works well on your first dataset. Months later,
a new field campaign brings another batch of images, and you need to repeat
the analysis. Which version of the script did you use? What settings produced
those outputs? Could a colleague reproduce the same steps from the files you
shared?</p>
<p>Reproducible data processing depends on preserving those details alongside the
method. LgoPy blocks help by packaging a transformation with defined inputs,
configurable parameters, and versioned code. In this tutorial, we will use a
simple RGB-to-HSV conversion to show how to read plot images, save derived
visualizations, and build a processing component that can be shared and reused
across datasets in PhenoWorks.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-lgopy">What is LgoPy?<a href="https://phenoworks.org/blog/building-custom-lgopy-blocks#what-is-lgopy" class="hash-link" aria-label="Direct link to What is LgoPy?" title="Direct link to What is LgoPy?" translate="no">​</a></h2>
<p><a href="https://github.com/OpenSciML/lgopy" target="_blank" rel="noopener noreferrer" class="">LgoPy</a> is an open-source Python library for
building multimodal data science pipelines. It lets researchers package Python
methods as blocks that can be installed, inspected, and combined in PhenoWorks.</p>
<p>Phenotyping studies often bring together field observations, images, sensor
readings, and experimental metadata. Each may need a different processing
method. A LgoPy block gives that method a defined input and output, along with
its dependencies and documentation.</p>
<p>The idea is similar to building with Lego pieces. One block calculates a
vegetation index; another summarizes measurements for each plot. When their
inputs and outputs are compatible, the blocks can form a pipeline that a team
can reuse across datasets.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="example-turn-rgb-images-into-hsv-visualizations">Example: turn RGB images into HSV visualizations<a href="https://phenoworks.org/blog/building-custom-lgopy-blocks#example-turn-rgb-images-into-hsv-visualizations" class="hash-link" aria-label="Direct link to Example: turn RGB images into HSV visualizations" title="Direct link to Example: turn RGB images into HSV visualizations" translate="no">​</a></h2>
<p>Our example, <code>blocks/image_analysis/image_2_hsv.py</code>, reads the RGB images for a
plot and converts them to HSV: hue, saturation, and value. It then saves a
visualization of those channels as a PhenoWorks artifact, a derived file
associated with the processing run.</p>
<p>Hue represents color, saturation its intensity, and value its brightness.
Viewing these channels separately can help you decide how to approach
segmentation or feature extraction.</p>
<p>To make the channels viewable, the block maps H, S, and V to the red, green,
and blue channels of a JPEG. The resulting colors show the HSV channel values,
so they differ from the original photograph. These images are intended for
visual inspection. For numerical measurements that require exact values, work
from the original data or a lossless representation rather than the JPEG.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="read-the-complete-block">Read the complete block<a href="https://phenoworks.org/blog/building-custom-lgopy-blocks#read-the-complete-block" class="hash-link" aria-label="Direct link to Read the complete block" title="Direct link to Read the complete block" translate="no">​</a></h2>
<p>The implementation below comes from <code>blocks/image_analysis/image_2_hsv.py</code>:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv codeBlockLinesWithNumbering_o6Pm" style="counter-reset:line-count 0"><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> io </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> BytesIO</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> typing </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> Annotated</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> lgopy</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">core </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> Block</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> PIL </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> Image </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">as</span><span class="token plain"> PILImage</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> logging</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">Image2HSV</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">Block</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">"""Create visible HSV-channel JPEG artifacts from RGB plot images."""</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"image_2_hsv"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    display_name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Image to HSV"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    description </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Convert RGB image to HSV artifacts"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    category </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Image Processing"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    authors </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"PhenoWorks"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"haruiz"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    tags </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"phenoworks"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"rgb"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"image-processing"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    version </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"0.1.0"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    logger </span><span class="token operator">=</span><span class="token plain"> logging</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">getLogger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">__name__</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">__init__</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">            self</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">            jpeg_quality</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Annotated</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token builtin" style="color:rgb(189, 147, 249)">int</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"JPEG quality for saved HSV artifacts"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token number">95</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token boolean">None</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token builtin" style="color:rgb(189, 147, 249)">super</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">__init__</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        self</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">_jpeg_quality </span><span class="token operator">=</span><span class="token plain"> jpeg_quality</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">call</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">self</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> dataset_item</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">dict</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">dict</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">"""Convert RGB plot images into HSV-channel visualization artifacts.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token triple-quoted-string string" style="display:inline-block;color:rgb(255, 121, 198)"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">        Args:</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">            dataset_item: Plot dataset item dictionary with plot metadata and assets grouped by modality.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token triple-quoted-string string" style="display:inline-block;color:rgb(255, 121, 198)"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">        Returns:</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">            Dictionary with conversion status and processed asset counts.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">        """</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">try</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">            plot_id </span><span class="token operator">=</span><span class="token plain"> dataset_item</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"plot_id"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">            rgb_assets </span><span class="token operator">=</span><span class="token plain"> dataset_item</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"assets"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"rgb"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> rgb_asset </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">in</span><span class="token plain"> rgb_assets</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                rgb_image </span><span class="token operator">=</span><span class="token plain"> PILImage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token builtin" style="color:rgb(189, 147, 249)">open</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">rgb_asset</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"file_uri"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                self</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">info</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"Processing plot </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">plot_id</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)"> with "</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                            </span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"RGB asset </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">rgb_asset</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string-interpolation interpolation string" style="color:rgb(255, 121, 198)">'file_uri'</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                </span><span class="token comment" style="color:rgb(98, 114, 164)"># Store a visible HSV-channel visualization as JPEG.</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                hsv_image </span><span class="token operator">=</span><span class="token plain"> rgb_image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">convert</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"HSV"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                hsv_visual </span><span class="token operator">=</span><span class="token plain"> PILImage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">merge</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"RGB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> hsv_image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">split</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                </span><span class="token builtin" style="color:rgb(189, 147, 249)">buffer</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> BytesIO</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                hsv_visual</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">save</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">buffer</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">format</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"JPEG"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> quality</span><span class="token operator">=</span><span class="token plain">self</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">_jpeg_quality</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                image_bytes </span><span class="token operator">=</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">buffer</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">getvalue</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                self</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">artifacts</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">save_artifact</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                    </span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"plot_</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">plot_id</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">_hsv_</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">rgb_asset</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string-interpolation interpolation string" style="color:rgb(255, 121, 198)">'id'</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">.jpg"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                    data</span><span class="token operator">=</span><span class="token plain">image_bytes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                    artifact_type</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">'plot_hsv_image'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                    plot_id</span><span class="token operator">=</span><span class="token plain">plot_id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">isinstance</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">plot_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">int</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">else</span><span class="token plain"> </span><span class="token boolean">None</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                </span><span class="token string" style="color:rgb(255, 121, 198)">"status"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"success"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                </span><span class="token string" style="color:rgb(255, 121, 198)">"plot_id"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> plot_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">                </span><span class="token string" style="color:rgb(255, 121, 198)">"num_rgb_assets"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">len</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">rgb_assets</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">except</span><span class="token plain"> Exception </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">as</span><span class="token plain"> e</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">            self</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">error</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"Error processing  plot </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">plot_id</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">: </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">e</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">raise</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> __name__ </span><span class="token operator">==</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'__main__'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#F8F8F2"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    Image2HSV</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">output_dir</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"image-2-hsv-block"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">format</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"zip"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span></span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="define-the-method-and-its-settings">Define the method and its settings<a href="https://phenoworks.org/blog/building-custom-lgopy-blocks#define-the-method-and-its-settings" class="hash-link" aria-label="Direct link to Define the method and its settings" title="Direct link to Define the method and its settings" translate="no">​</a></h2>
<p><code>Image2HSV</code> inherits from LgoPy's <code>Block</code> class. The attributes at the top identify
it in the catalog: <code>image_2_hsv</code> is its name, <strong>Image to HSV</strong> its display label,
and <code>0.1.0</code> its version. The remaining metadata helps researchers find the method
and understand what it does before reading the code.</p>
<p>There is one configurable setting: <code>jpeg_quality</code>, which defaults to <code>95</code>.
Its <code>Annotated[...]</code> declaration supplies a type and description for LgoPy's
schema. After initializing the base class with <code>super().__init__()</code>, the
constructor stores the chosen quality in <code>self._jpeg_quality</code> for use when
saving the JPEG.</p>
<p>When you adapt the example, keep these parameter descriptions and defaults in
step with the implementation. They help other researchers configure the block
correctly.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="understand-the-input">Understand the input<a href="https://phenoworks.org/blog/building-custom-lgopy-blocks#understand-the-input" class="hash-link" aria-label="Direct link to Understand the input" title="Direct link to Understand the input" translate="no">​</a></h2>
<p>PhenoWorks passes plot-level blocks a dataset item with a plot identifier and
assets grouped by modality. This block expects RGB assets under <code>assets["rgb"]</code>:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token string" style="color:rgb(255, 121, 198)">"plot_id"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">101</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token string" style="color:rgb(255, 121, 198)">"assets"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token string" style="color:rgb(255, 121, 198)">"rgb"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"id"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"file_uri"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/path/to/plot_101_rgb.jpg"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Each image needs an <code>id</code> and a readable <code>file_uri</code>. Because this block opens the
file directly with Pillow, use a local image path for the example below. Remote
assets and logical storage keys must first be resolved to files the block can
read.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="follow-the-processing-step">Follow the processing step<a href="https://phenoworks.org/blog/building-custom-lgopy-blocks#follow-the-processing-step" class="hash-link" aria-label="Direct link to Follow the processing step" title="Direct link to Follow the processing step" translate="no">​</a></h2>
<p>For each RGB asset, <code>call(...)</code>:</p>
<ol>
<li class="">Opens the image with Pillow.</li>
<li class="">Converts it to HSV and splits the three channels.</li>
<li class="">Combines those channels into a viewable RGB image.</li>
<li class="">Encodes the visualization as a JPEG using <code>self._jpeg_quality</code>.</li>
<li class="">Saves the bytes through <code>self.artifacts.save_artifact(...)</code>.</li>
</ol>
<p>A file named <code>plot_101_hsv_1.jpg</code> records both the plot and source asset identifiers.
The artifact has the type <code>plot_hsv_image</code>; when the plot identifier is an integer,
the block also passes it to the artifact store. The original image stays intact.</p>
<p>After processing, the block returns <code>status</code>, <code>plot_id</code>, and <code>num_rgb_assets</code>.
An empty RGB list produces a successful result with a count of zero. If processing
fails, the block logs the error and raises it to the caller.</p>
<p>This return value matters when you build a pipeline. The JPEG is saved through
the artifact store, while <code>call(...)</code> returns a status dictionary. A later step
must accept that dictionary or retrieve the saved image; a step expecting a plot
dataset item cannot use the summary directly.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="check-the-block-locally">Check the block locally<a href="https://phenoworks.org/blog/building-custom-lgopy-blocks#check-the-block-locally" class="hash-link" aria-label="Direct link to Check the block locally" title="Direct link to Check the block locally" translate="no">​</a></h2>
<p>You can test the block without a database or a running PhenoWorks server.
LgoPy provides in-memory artifact and metadata stores. Its artifact store exposes
<code>save(...)</code>, while this block calls PhenoWorks' <code>save_artifact(...)</code> method, so a
small adapter connects the two.</p>
<p>The following example creates a temporary RGB image, runs the block, and checks
the generated JPEG. Run it from the repository root in the project's Python
environment:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> pathlib </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> Path</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> tempfile </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> TemporaryDirectory</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> lgopy</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">core</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">stores </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> InMemoryArtifactStore</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> InMemoryMetadataStore</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> PIL </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> Image </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">as</span><span class="token plain"> PILImage</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> blocks</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">image_analysis</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">image_2_hsv </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> Image2HSV</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">TestArtifactStore</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">InMemoryArtifactStore</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">"""Store artifact bytes in memory for local block tests."""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">save_artifact</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        self</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">str</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">bytes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token operator">**</span><span class="token plain">attributes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">object</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token boolean">None</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">"""Save an artifact without database persistence.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token triple-quoted-string string" style="display:inline-block;color:rgb(255, 121, 198)"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">        Args:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">            key: Artifact name used to retrieve the stored bytes.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">            data: Serialized artifact bytes.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">            **attributes: Database-specific attributes ignored by this test store.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">        """</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        self</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">save</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">artifacts </span><span class="token operator">=</span><span class="token plain"> TestArtifactStore</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">metadata </span><span class="token operator">=</span><span class="token plain"> InMemoryMetadataStore</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">block </span><span class="token operator">=</span><span class="token plain"> Image2HSV</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">block</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">artifacts </span><span class="token operator">=</span><span class="token plain"> artifacts</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">block</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">metadata </span><span class="token operator">=</span><span class="token plain"> metadata</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">with</span><span class="token plain"> TemporaryDirectory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">as</span><span class="token plain"> directory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    image_path </span><span class="token operator">=</span><span class="token plain"> Path</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">directory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">/</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"rgb.jpg"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    PILImage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">new</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"RGB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">32</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">32</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> color</span><span class="token operator">=</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">80</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">150</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">40</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">save</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">image_path</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    result </span><span class="token operator">=</span><span class="token plain"> block</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">call</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token string" style="color:rgb(255, 121, 198)">"plot_id"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">101</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token string" style="color:rgb(255, 121, 198)">"plot"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"name"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Plot 101"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token string" style="color:rgb(255, 121, 198)">"assets"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token string" style="color:rgb(255, 121, 198)">"rgb"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"id"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"file_uri"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">str</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">image_path</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token builtin" style="color:rgb(189, 147, 249)">set</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"test.result"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">print</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">result</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">print</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Artifacts:"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">list</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">artifacts</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token builtin" style="color:rgb(189, 147, 249)">all</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">print</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Metadata:"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token builtin" style="color:rgb(189, 147, 249)">all</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">with</span><span class="token plain"> PILImage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token builtin" style="color:rgb(189, 147, 249)">open</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">artifacts</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">as_bytes_io</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"plot_101_hsv_1.jpg"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">as</span><span class="token plain"> image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">assert</span><span class="token plain"> image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token builtin" style="color:rgb(189, 147, 249)">format</span><span class="token plain"> </span><span class="token operator">==</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"JPEG"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">assert</span><span class="token plain"> image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">size </span><span class="token operator">==</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">32</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">32</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p>The test adapter retains the JPEG bytes but ignores database-specific attributes
such as <code>artifact_type</code> and <code>plot_id</code>. PhenoWorks supplies its own adapter during
pipeline execution to record those associations. Both test stores keep their
contents only in memory; the temporary input image is removed after processing.</p>
<p><code>Image2HSV</code> does not write metadata itself. The example explicitly stores its
result under <code>test.result</code> to demonstrate the metadata store. To try a real image,
replace the temporary-image setup with an existing local path.</p>
<p>For this single-image example, a successful call saves the visualization and
returns:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"status"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"success"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"plot_id"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">101</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"num_rgb_assets"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="build-and-install-the-block">Build and install the block<a href="https://phenoworks.org/blog/building-custom-lgopy-blocks#build-and-install-the-block" class="hash-link" aria-label="Direct link to Build and install the block" title="Direct link to Build and install the block" translate="no">​</a></h2>
<p>From the repository root, run the example's build entry point:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">uv run python blocks/image_analysis/image_2_hsv.py</span><br></div></code></pre></div></div>
<p>The <code>__main__</code> section calls
<code>Image2HSV.build(output_dir="image-2-hsv-block", format="zip")</code>, creating a
package directory and a ZIP archive alongside it. The builder can replace an
existing output directory, so keep your source files elsewhere.</p>
<p>Install the generated ZIP from <strong>Analysis Modules</strong>, or install the package
directory with the CLI:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">phenoworks analysis-blocks install image-2-hsv-block</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">phenoworks analysis-blocks source image_2_hsv --version 0.1.0</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">phenoworks analysis-blocks requirements image_2_hsv --version 0.1.0</span><br></div></code></pre></div></div>
<p>Once installed, inspect the package and its requirements in PhenoWorks. To
produce the visualizations, select a compatible dataset, set <code>jpeg_quality</code>,
and run the block in a dataset pipeline. The build command only packages the
method. The current script also runs the local smoke test after building;
processing a dataset starts when you run the installed block in a pipeline.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="reuse-the-pattern-for-feature-extraction">Reuse the pattern for feature extraction<a href="https://phenoworks.org/blog/building-custom-lgopy-blocks#reuse-the-pattern-for-feature-extraction" class="hash-link" aria-label="Direct link to Reuse the pattern for feature extraction" title="Direct link to Reuse the pattern for feature extraction" translate="no">​</a></h2>
<p>The same structure can support a vegetation-index calculation, a canopy-cover
measurement, or a summary of image-channel statistics. The processing method
changes, but the block still reads a defined input, saves its outputs, and
returns a result that other code can use.</p>
<p>Let the next research task guide the output format. Images support visual
inspection; measurements and tables support further analysis. Clear input and
output descriptions make it easier for another researcher to connect your block
to a pipeline and apply the method to a new dataset.</p>]]></content>
        <author>
            <name>Henry Ruiz</name>
            <uri>https://github.com/haruiz</uri>
        </author>
        <category label="Documentation" term="Documentation"/>
        <category label="Development" term="Development"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[PhenoWorks Beta Is Here]]></title>
        <id>https://phenoworks.org/blog/phenoworks-beta-release</id>
        <link href="https://phenoworks.org/blog/phenoworks-beta-release"/>
        <updated>2026-09-03T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[PhenoWorks beta is here: a crop phenotyping web platform that brings research data, reusable analysis methods, and an AI assistant into one workspace. Load and visualize your data, extract structured features with LgoPy blocks, and use the results to support analysis, interpretation, and research writing.]]></summary>
        <content type="html"><![CDATA[<p>PhenoWorks beta is here: a crop phenotyping web platform that brings research data, reusable analysis methods, and an AI assistant into one workspace. Load and visualize your data, extract structured features with LgoPy blocks, and use the results to support analysis, interpretation, and research writing.</p>
<!-- -->
<p>The <a class="" href="https://phenoworks.org/">interactive diagram on our landing page</a> follows this workflow from data to scientific discovery. Here is how its four stages fit together.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-research-data-collection">1. Research Data Collection<a href="https://phenoworks.org/blog/phenoworks-beta-release#1-research-data-collection" class="hash-link" aria-label="Direct link to 1. Research Data Collection" title="Direct link to 1. Research Data Collection" translate="no">​</a></h2>
<p>Start by organizing your field trials into projects, studies, datasets, and plots. Bring UAV and satellite imagery, sensor readings, field observations, metadata, and treatment labels together, then visualize supported images before choosing how to process them.</p>
<p>Research documents belong here too. Keep PDFs, manuscript drafts, reference papers, and protocols alongside the experiments they describe. The platform can also be extended to support other modalities, such as laboratory measurements and genomic data.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-analysis--evidence-building">2. Analysis &amp; Evidence Building<a href="https://phenoworks.org/blog/phenoworks-beta-release#2-analysis--evidence-building" class="hash-link" aria-label="Direct link to 2. Analysis &amp; Evidence Building" title="Direct link to 2. Analysis &amp; Evidence Building" translate="no">​</a></h2>
<p>Move from <strong>data ingestion</strong> and <strong>quality control</strong> to <strong>feature extraction</strong> and <strong>analysis results</strong>. LgoPy blocks turn raw data into structured features, such as vegetation indices and canopy-cover measurements, that you can use in downstream analysis.</p>
<p>Like Lego pieces, each block performs one focused task with defined inputs and outputs. Connect compatible blocks into pipelines you can share and reuse across datasets. For example, an image-processing block can prepare images for a compatible feature-extraction step. Available methods depend on the installed block catalog.</p>
<p>Follow pipeline progress, inspect logs, and review the generated images, masks, summaries, and tables. These outputs stay connected to their source data and processing workflow.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-scientific-knowledge-base">3. Scientific Knowledge Base<a href="https://phenoworks.org/blog/phenoworks-beta-release#3-scientific-knowledge-base" class="hash-link" aria-label="Direct link to 3. Scientific Knowledge Base" title="Direct link to 3. Scientific Knowledge Base" translate="no">​</a></h2>
<p>Connect images, extracted features, figures, tables, model results, methods, and documents into a shared research knowledge base. Keep the evidence together so you can find relevant datasets, trace how measurements were produced, compare results, and download files for further work.</p>
<p>This lays the foundation for <strong>multimodal RAG</strong>: retrieving relevant context from visual, numerical, and written evidence to support an assistant's answers. What the agent can retrieve depends on the connected tools and indexing available.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-phenoworks-agent">4. PhenoWorks Agent<a href="https://phenoworks.org/blog/phenoworks-beta-release#4-phenoworks-agent" class="hash-link" aria-label="Direct link to 4. PhenoWorks Agent" title="Direct link to 4. PhenoWorks Agent" translate="no">​</a></h2>
<p>Work with the <strong>PhenoWorks Agent</strong> (<code>phenoworks-agent</code>) to process images, extract features, and analyze data using available LgoPy blocks. Describe the task in your own words:</p>
<blockquote>
<p>Can you calculate NDVI for my dataset?</p>
</blockquote>
<blockquote>
<p>Can you apply histogram equalization to the RGB images in my dataset?</p>
</blockquote>
<p>The agent can help choose a suitable method, check its inputs, run a compatible pipeline, and retrieve the results. You can then ask it to interpret differences across plots or draft a preliminary-data section from selected results.</p>
<p><strong>PhenoWorks MCP</strong> (<code>phenoworks-mcp</code>) provides the Model Context Protocol connection between compatible AI assistants and the PhenoWorks API. Its tools support finding datasets, inspecting analysis blocks, validating and running pipelines, tracking jobs, and retrieving outputs within the user's access permissions.</p>
<p>Learn more about the <a class="" href="https://phenoworks.org/docs/features/phenoworks-agent">PhenoWorks Agent</a> and its connected tools.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="research-outputs">Research outputs<a href="https://phenoworks.org/blog/phenoworks-beta-release#research-outputs" class="hash-link" aria-label="Direct link to Research outputs" title="Direct link to Research outputs" translate="no">​</a></h2>
<p>The options at the bottom of the diagram show what this workflow can support:</p>
<ul>
<li class=""><strong>Downstream Analysis:</strong> explore extracted features with the agent's available tools or export them to notebooks and statistical software.</li>
<li class=""><strong>Scientific Discovery:</strong> investigate patterns, develop new questions, and plan follow-up experiments.</li>
<li class=""><strong>Manuscripts:</strong> bring methods, figures, and measurements together, and work with the agent on preliminary-results drafts.</li>
<li class=""><strong>Grant Proposals:</strong> use pilot-study findings to draft preliminary-data sections that support a proposed study.</li>
</ul>
<p>For example: “Can you draft a preliminary-data section for my grant proposal using these canopy-cover results?” Researchers review the interpretation and supporting evidence, then refine the draft for their audience.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="continuous-learning">Continuous Learning<a href="https://phenoworks.org/blog/phenoworks-beta-release#continuous-learning" class="hash-link" aria-label="Direct link to Continuous Learning" title="Direct link to Continuous Learning" translate="no">​</a></h2>
<p>Research feeds back into the next workflow. Add new observations, review unexpected results, refine your analysis blocks, and compare the revised outputs. This cycle connects each round of data collection and analysis with the questions that come next.</p>
<p>We will use this blog to share release notes, tutorials, and development updates as PhenoWorks evolves.</p>
<p>Interested in PhenoWorks? <a href="https://docs.google.com/forms/d/e/1FAIpQLSfs6dAI3IQofL37Zmodcbr3v7B1f0_09GoUnwPrqVkgOdvf6g/viewform" target="_blank" rel="noopener noreferrer" class="">Express your interest through our Google Form</a>.</p>]]></content>
        <author>
            <name>PhenoWorks Contributors</name>
            <uri>https://github.com/OpenSciML/phenoworks</uri>
        </author>
        <category label="Release notes" term="Release notes"/>
        <category label="Development" term="Development"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[PhenoWorks Documentation Moves to Docusaurus]]></title>
        <id>https://phenoworks.org/blog/website-docs-migration</id>
        <link href="https://phenoworks.org/blog/website-docs-migration"/>
        <updated>2026-09-03T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[The PhenoWorks website now uses Docusaurus for documentation, project updates,]]></summary>
        <content type="html"><![CDATA[<p>The PhenoWorks website now uses Docusaurus for documentation, project updates,
release notes, and development notes.</p>
<!-- -->
<p>The initial documentation content keeps the same major sections: getting
started, software overview, features, tutorials, deployment, FAQ, and
troubleshooting.</p>
<p>Future updates can be published here as blog posts while the docs remain focused
on stable user and developer guidance.</p>]]></content>
        <author>
            <name>PhenoWorks Contributors</name>
            <uri>https://github.com/OpenSciML/phenoworks</uri>
        </author>
        <category label="Documentation" term="Documentation"/>
        <category label="Development" term="Development"/>
    </entry>
</feed>