Tips & tricks¶
Generator filters (no inputs)¶
Filters don't have to take image inputs — they can simply generate data:
from interactive_pipe import interactive, context
import numpy as np
COLOR_DICT = {"red": [1., 0., 0.], "green": [0., 1., 0.], "blue": [0., 0., 1.], "gray": [0.5, 0.5, 0.5]}
@interactive(color_choice=["red", "green", "blue", "gray"])
def generate_flat_colored_image(color_choice="red"):
flat_array = np.array(COLOR_DICT.get(color_choice)) * np.ones((64, 64, 3))
context["avg"] = np.average(flat_array)
return flat_array
The color_choice list becomes a dropdown menu; the default is the first element.
Switching between images¶
from interactive_pipe import KeyboardControl
@interactive(image_index=KeyboardControl(0, [0, 2], keydown="pagedown", keyup="pageup", modulo=True))
def switch_image(img1, img2, img3, image_index=0):
return [img1, img2, img3][image_index]
modulo=True makes the index wrap around (back to 0 past the maximum) — flip through images with Page Up / Page Down.
Avoid in-place operations¶
Filters route their buffers by reference, so mutating an input in place silently corrupts sibling filters and cached buffers. Since 0.9.1, inputs are handed to filters as read-only views by default (readonly_inputs=True), so the mistake raises immediately instead of producing wrong results:
# Raises since 0.9.1 — img is a read-only view
def bad_processing_block(img):
img += 1
return img
If a filter intentionally mutates its inputs, declare it with @interactive(inplace=True) — it then receives private writable copies:
@interactive(inplace=True)
def add_one(img):
img += 1
return img
Pass readonly_inputs=False to @interactive_pipeline(...) to restore the old permissive behavior. In-place mutation is also detected for torch tensors (which cannot be marked read-only).
Cache intermediate results¶
@interactive_pipeline(gui="qt", cache=True) keeps intermediate filter outputs in RAM, so moving a slider only re-runs the filters downstream of the change (in source order) — a big win for heavy pipelines.
For pipelines with independent branches, use cache="graph" instead: this dependency-aware mode recomputes a filter only when it is actually affected — its own sliders moved, one of its producers was recomputed, or a context key it reads changed. Filters on branches unrelated to the change are left untouched.
@interactive_pipeline(gui="qt", cache="graph")
def my_pipeline(img):
exposed = exposure(img) # moving the exposure slider...
smooth = denoise(img) # ...leaves denoise and annotate cached
tagged = annotate(smooth)
return [exposed, tagged]
Use cache="graph-strict" to additionally receive context numpy arrays as read-only views, catching accidental in-place mutation of shared context data.
Export / import tuning¶
Press E in the GUI to export the current parameters to YAML and O to load them back; headless pipelines expose the same via export_tuning() / load_tuning(). Press G to export a graphviz diagram of the pipeline.
One-shot GUI with @interact¶
For a quick experiment on a single function — no pipeline needed — @interact opens the GUI immediately at decoration time:
from interactive_pipe import interact
import numpy as np
image = np.random.rand(256, 512, 3)
@interact(image, gain=(1.0, [0.0, 3.0]))
def show(img, gain=1.0):
return img * gain
Handy in notebooks and throwaway scripts; for anything reusable, prefer @interactive + @interactive_pipeline (decorators guide).
interact
¶
interact(*decorator_args: Any, gui: str = 'auto', disable: bool = False, output_routing: Optional[List[str]] = None, size: Union[str, Tuple[int, int], None] = None, inplace: bool = False, **decorator_controls: Any)
Launch a GUI from a single decorated function (one-shot decorator).
Unlike @interactive, decorating a function with @interact
immediately runs it inside a GUI: widgets are created for the declared
controls and the window opens right away. Inspired by the ipywidgets
interact function, except that returning numpy arrays or Curve
instances handles the display automatically — no matplotlib boilerplate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*decorator_args
|
Any
|
Positional inputs passed to the function (e.g. the input image). |
()
|
gui
|
str
|
Backend, same values as |
'auto'
|
disable
|
bool
|
When True, return the function untouched (no GUI). |
False
|
output_routing
|
Optional[List[str]]
|
Names of the outputs, when they cannot be deduced from a dry run. |
None
|
size
|
Union[str, Tuple[int, int], None]
|
Window/figure size hint forwarded to the backend. |
None
|
**decorator_controls
|
Any
|
Controls bound to keyword arguments by name
( |
{}
|
Example
@interact(image, gain=(1.0, [0.0, 3.0]))
def show(img, gain=1.0):
return img * gain
Source code in src/interactive_pipe/helper/filter_decorator.py
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | |