Pipeline Backend¶
The Pipeline
is the core backend of the Playground
. It is responsible for running a sequence of Transforms
and displaying the output. It can also be run in conjunction with the Pipeline Launcher. It consists of the following components:
Pipeline
Windows
Transforms
Params
The following relationships are defined between these objects:
Pipeline
|- Window
| |- Transform
| | |- Param
| | |- ...
| |- ...
|- ...
The components above will be described from the lowest level to the highest.
Param¶
A Param
is an input to the Transform
that can be manipulated via some kind of Qt Widget, such a slider, input box, select box, etc.
See the Params
module for existing Param
classes that can be used.
Defining a New Param¶
A new Param
can be defined as follows:
class MyParam(Param):
def __init__(self, my_arg, default=None, label=None, read_only=False, help_text=''):
# You must call the super first
super.__init__(default=default, label=label, read_only=read_only,
help_text=help_text):
# define and store any new arguments
self.my_arg = my_arg
...
def _get_widget(self, parent=None):
"""Return a widget instance for this Param type"""
# 1. Create and configure widget instance, passing the parent in
# 2. Connect the widgets value changed handler to a function on this Param
# 3. Return the widget instance
@QtCore.Slot(<appropriate_type_here>)
def _handle_value_changed(self, value):
"""Stores the updated value and runs the pipeline"""
# NOTE: Do any necessary conversion to value here before storing it
self._store_value_and_start(value)
A nice introduction to the Signals and Slots mechanisms in Qt5.
Transform¶
A Transform
defines either a manipulation that will be done on an image or can perform a calculation and pass the results onto the following Transforms
. We define class level Param
variables to allow the user to interact with the Transforms
.
The Transform
defines a draw(img_in, extra_in)
method that takes an image in and possibly extra information, and then returns an image or an image and some extra information as a tuple
. These are passed onto the next Transform
.
Each Param
value can be accessed and set via self.param_name
. The actual Param
instance is stored as self._param_name
.
Please see the opencv_pg.models.transforms
and opencv_pg.models.support_transforms
modules for existing Transforms
which may be useful.
Creating a New Transform¶
Creating your own Transform
is easy!
A new Transform
can be defined as follows:
from opencv_pg import BaseTransform
from opencv_pg import params
OPTIONS = {
'Display 1': 'value1',
'Display 2': 'value2',
}
class MyTransform(BaseTransform):
param1_slider = params.IntSlider(min_val=0, max_val=255, step=1, default=100)
combo = params.ComboBox(
options=value_map.keys(), default='Display 1', options_map=OPTIONS
])
checkbox = params.CheckBox()
def draw(self, img_in, extra_in):
"""Required - must return an ndarray, or an (ndarray, object)"""
out = cv2.some_function(
img=img_in,
param1=self.param1_slider,
param2=self.combo,
chk=self.checkbox
)
return out
# or return out, something_extra
def get_info_widget(self):
"""Optional: Return a QWidget that will be displayed as extra
information above the Transform group"""
pass
def update_widgets_state(self):
"""Optional: update the state of other various widgets within this
Transform, based on each other's state. Can be used to test one
widget for a value, and enable/disable other widgets
"""
pass
This Transform
would display an Integer slider, a ComboBox and a CheckBox.
Window¶
A Window
is composed of one or more Transforms
. Each Window
is responsible for displaying the output of the last Transform
in its list, and then passing that output onto the first Transform
of the next Window
.
Creating a Window¶
A window can be created as follows:
window = Window([
Transform1(),
Transform2()
])
You can optionally pass a name
argument to the Window
to give it a meaningful window title. If no name
is passed, it will be named Step N
, according to its position in the Pipeline
.
Pipeline¶
The Pipeline
represents the top level feature of the hierarchy. It sets up the windows and is responsible for running all the Transforms
in the pipeline.
Creating a Pipeline¶
A Pipeline can be created in any of the following ways:
# There is a single Transform
pipeline1 = Pipeline(Transform())
# If there are multiple Transforms, but only one Window
pipeline2 = Pipeline([Transform1(), Transform2(), Transform3()])
# If there are multiple Windows
pipeline3 = Pipeline([
Window([
Transform1(),
Transform2(),
]),
Window([
Transform2(),
Transform3(),
])
])
Pipeline Launcher¶
Now that you’ve created your own custom Params
and Transforms
, we can put them all together into your own pipeline.
A custom Pipeline
can be launched by your own code using the launch_pipeline
function. When this is done, a Qt Window will be displayed for each Window
in your Pipeline
.
Example:
from opencv_pg import Pipeline, Window, launch_pipeline
from opencv_pg import support_transforms as supt
from opencv_pg import transforms as tf
if __name__ == '__main__':
my_image = '/path/to/image.png'
pipeline = Pipeline([
# You could also import and use your own Transforms
Window([
supt.LoadImage(my_image),
supt.CvtColor(),
tf.InRange(),
supt.BitwiseAnd(),
]),
Window([
tf.Canny(),
]),
])
launch_pipeline(pipeline)
This will show two Windows
. The first with the final output of the BitwiseAnd
and the second with the output of the Canny
operation.