Tutorial 6: Size and colour reference

Unless images are taken in a highly standardized environment, e.g. via a scanner or a microscope, variation will be introduced in terms of exposure or distance between camera and photographed object, zooming, etc. To compensate this variation among images within and across datasets, Phenopype contains some preprocessing tools that can automatically correct images.

Load project

We will use the project we created before in Tutorial 5:

import phenopype as pp
import os

os.chdir(r"C:\Users\mluerig\Downloads\phenopype-tutorials-main")

myproj = pp.Project(r"tutorial_project") 
--------------------------------------------
Found existing project root directory - loading from:
C:\Users\mluerig\Downloads\phenopype-tutorials-main\tutorial_project

Project "tutorial_project" successfully loaded with 3 images
--------------------------------------------

First we will need to select and measure the pixel-ratio in a reference image, to which we want to match all images in the project.

Adding a reference to the project

With the class method add_reference we can set a project specific scale by measuring the pixel-ratio in a reference image. The steps for this are:

  1. Click on two points with a known distance, e.g. on a piece of mm-paper that you put in the image, and hit Enter.

  2. Enter the length that will be used to calculate the pixel-ratio.

  3. Mark the boundaries of the reference card to create a template for automatic scale detection that is saved on the project’s root directory (in the template folder).


Fig 1 The reference image can be any image, but choose it carefully: if you plan on doing brightness and colour corrections, it should be in the middle of the distribution of all exposures and colours so corrections will not over-expose or over-saturate the images.

We will use the image stickleback_side.jpg from the image folder in tutorials:

myproj.add_reference(
    reference_image_path=r"tutorials/data/stickleback_side.jpg", 
    reference_tag="scale1",
)
Reference image not saved, file already exists - use "overwrite==True" or chose different name.
setting active global project reference to "scale1" for 0__stickle1 (active=True)
setting active global project reference to "scale1" for 0__stickle2 (active=True)
setting active global project reference to "scale1" for 0__stickle3 (active=True)

We are now all set to automatically detect the reference card in our images.

Detecting a reference

To detect the reference template in our images, we need the function detect_reference from the preprocessing module. To do so on the existing project, we will use the edit_config method that was demonstrated in Tutorial 5. Again, we want to modify the config in two places: first in the preprocessing chunk, to perform the actual reference detection, and then in the visualization chunk, to show the outline of the detected reference:

## modify the "preprocessing" section

target1 = """    - preprocessing:"""
replacement1 = """    - preprocessing:
        - detect_reference"""
    
myproj.edit_config(
    tag="plates-v1",
    target=target1,
    replacement=replacement1,
)
# phenopype quickstart template
# -----------------------------
# This template is intended to go with the phenopype quickstart materials 
# - for details see https://www.phenopype.org/docs/quickstart/ and refer to
# Figure 2 in Luerig 2021 (https://doi.org/10.1111/2041-210X.13771) or 
# phenopype tutorial 3 (https://www.phenopype.org/docs/tutorials/tutorial_3).
# For a better job of measuring individual plate area see the phenopype 
# gallery (https://www.phenopype.org/gallery/example_5/).

config_info:
    config_name: pype_config_plates-v1.yaml
    date_created: '2022-05-06 15:04:33'
    date_last_modified:
    template_name: quickstart-template.yaml
    template_path: C:\Users\mluerig\Downloads\phenopype-quickstart-main\quickstart-template.yaml
processing_steps:
    - preprocessing:
        - detect_reference
        - create_mask:
            ANNOTATION: {type: mask, id: a, edit: false}
            tool: polygon
            label: plates
        - blur:
            kernel_size: 9
    - segmentation:
        - threshold:
            method: adaptive
            blocksize: 99
            constant: 5
        - detect_contour:
            ANNOTATION: {type: contour, id: a, edit: overwrite}
            retrieval: ext
            min_area: 150
    - measurement:
        - compute_shape_features:
            ANNOTATION: {type: shape_features, id: a, edit: overwrite}
    - visualization:
        - select_canvas:
            canvas: raw
        - draw_contour
        - draw_mask:
            label: true
    - export:
        - save_canvas
        - save_annotation:
            overwrite: true

This is what the new config may look like (can differ beteeen files) - proceed?y
New config saved for 0__stickle1
New config saved for 0__stickle2
New config saved for 0__stickle3
## "visualization" modification to add `draw_reference`:

target2 = """- draw_mask:
            label: true"""
replacement2 = """- draw_mask:
            label: true
        - draw_reference:
            line_colour: aqua
            line_width: 10
            scale: true"""
    
myproj.edit_config(
    tag="plates-v1",
    target=target2,
    replacement=replacement2,
)
# phenopype quickstart template
# -----------------------------
# This template is intended to go with the phenopype quickstart materials 
# - for details see https://www.phenopype.org/docs/quickstart/ and refer to
# Figure 2 in Luerig 2021 (https://doi.org/10.1111/2041-210X.13771) or 
# phenopype tutorial 3 (https://www.phenopype.org/docs/tutorials/tutorial_3).
# For a better job of measuring individual plate area see the phenopype 
# gallery (https://www.phenopype.org/gallery/example_5/).

config_info:
    config_name: pype_config_plates-v1.yaml
    date_created: '2022-05-06 15:04:33'
    date_last_modified:
    template_name: quickstart-template.yaml
    template_path: C:\Users\mluerig\Downloads\phenopype-quickstart-main\quickstart-template.yaml
processing_steps:
    - preprocessing:
        - detect_reference
        - create_mask:
            ANNOTATION: {type: mask, id: a, edit: false}
            tool: polygon
            label: plates
        - blur:
            kernel_size: 9
    - segmentation:
        - threshold:
            method: adaptive
            blocksize: 99
            constant: 5
        - detect_contour:
            ANNOTATION: {type: contour, id: a, edit: overwrite}
            retrieval: ext
            min_area: 150
    - measurement:
        - compute_shape_features:
            ANNOTATION: {type: shape_features, id: a, edit: overwrite}
    - visualization:
        - select_canvas:
            canvas: raw
        - draw_contour
        - draw_mask:
            label: true
        - draw_reference:
            line_colour: aqua
            line_width: 10
            scale: true
    - export:
        - save_canvas
        - save_annotation:
            overwrite: true

This is what the new config may look like (can differ beteeen files) - proceed?y
New config saved for 0__stickle1
New config saved for 0__stickle2
New config saved for 0__stickle3

Now we run our loop with the new pype configuration:

for path in myproj.dir_paths:
    pp.Pype(path, tag="plates-v1")
- no annotation_type selected - returning all annotations

AUTOLOAD
- annotations loaded:
{
"mask": ["a"],
"contour": ["a"],
"shape_features": ["a"],
"reference": ["a"]
}
- reference template image loaded from root directory
Stage: add annotation control args
Updating pype config: applying staged changes


------------+++ new pype iteration 2022-05-06 15:08:46 +++--------------




PREPROCESSING
detect_reference
- loaded existing annotation of type "reference" with ID "a": skipping (edit=False)
create_mask
- loaded existing annotation of type "mask" with ID "a": skipping (edit=False)
blur


SEGMENTATION
threshold
- multichannel image supplied, converting to grayscale
- decompose image: using gray channel
- including pixels from 1 drawn masks 
- excluding pixels from reference
detect_contour
- loaded existing annotation of type "contour" with ID "a": overwriting (edit=overwrite)
- found 16 contours that match criteria


MEASUREMENT
compute_shape_features
- loaded existing annotation of type "shape_features" with ID "a": overwriting (edit=overwrite)


VISUALIZATION
select_canvas
- raw image
draw_contour
draw_mask
draw_reference


EXPORT
save_canvas
- image saved under C:\Users\mluerig\Downloads\phenopype-tutorials-main\tutorial_project\data\0__stickle1\canvas_plates-v1.jpg (overwritten).
save_annotation
- loading existing annotation file
- no annotation_type selected - exporting all annotations
- updating annotations of type "mask" with id "a" in "annotations_plates-v1.json" (overwrite="entry")
- updating annotations of type "contour" with id "a" in "annotations_plates-v1.json" (overwrite="entry")
- updating annotations of type "shape_features" with id "a" in "annotations_plates-v1.json" (overwrite="entry")
- updating annotations of type "reference" with id "a" in "annotations_plates-v1.json" (overwrite="entry")


------------+++ finished pype iteration +++--------------
-------(End with Ctrl+Enter or re-run with Enter)--------


AUTOSHOW


------------+++ new pype iteration 2022-05-06 15:08:52 +++--------------




PREPROCESSING
detect_reference
- loaded existing annotation of type "reference" with ID "a": skipping (edit=False)
create_mask
- loaded existing annotation of type "mask" with ID "a": skipping (edit=False)
blur


SEGMENTATION
threshold
- multichannel image supplied, converting to grayscale
- decompose image: using gray channel
- including pixels from 1 drawn masks 
- excluding pixels from reference
detect_contour
- loaded existing annotation of type "contour" with ID "a": overwriting (edit=overwrite)
- found 16 contours that match criteria


MEASUREMENT
compute_shape_features
- loaded existing annotation of type "shape_features" with ID "a": overwriting (edit=overwrite)


VISUALIZATION
select_canvas
- raw image
draw_contour
draw_mask
draw_reference


EXPORT
save_canvas
- image saved under C:\Users\mluerig\Downloads\phenopype-tutorials-main\tutorial_project\data\0__stickle1\canvas_plates-v1.jpg (overwritten).
save_annotation
- loading existing annotation file
- no annotation_type selected - exporting all annotations
- updating annotations of type "mask" with id "a" in "annotations_plates-v1.json" (overwrite="entry")
- updating annotations of type "contour" with id "a" in "annotations_plates-v1.json" (overwrite="entry")
- updating annotations of type "shape_features" with id "a" in "annotations_plates-v1.json" (overwrite="entry")
- updating annotations of type "reference" with id "a" in "annotations_plates-v1.json" (overwrite="entry")


------------+++ finished pype iteration +++--------------
-------(End with Ctrl+Enter or re-run with Enter)--------


AUTOSHOW


TERMINATE

AUTOSAVE
- nothing to autosave
- no annotation_type selected - returning all annotations

AUTOLOAD
- annotations loaded:
{
"mask": ["a"],
"contour": ["a"],
"shape_features": ["a"]
}
- reference template image loaded from root directory
Stage: add annotation control args
Updating pype config: applying staged changes


------------+++ new pype iteration 2022-05-06 15:08:54 +++--------------




PREPROCESSING
detect_reference
---------------------------------------------------
Reference card found with 226 keypoint matches:
template image has 36.44 pixel per mm.
current image has 35.21 pixel per mm.
= 96.626 %% of template image.
---------------------------------------------------
create_mask
- loaded existing annotation of type "mask" with ID "a": skipping (edit=False)
blur


SEGMENTATION
threshold
- multichannel image supplied, converting to grayscale
- decompose image: using gray channel
- including pixels from 1 drawn masks 
- excluding pixels from reference
detect_contour
- loaded existing annotation of type "contour" with ID "a": overwriting (edit=overwrite)
- found 9 contours that match criteria


MEASUREMENT
compute_shape_features
- loaded existing annotation of type "shape_features" with ID "a": overwriting (edit=overwrite)


VISUALIZATION
select_canvas
- raw image
draw_contour
draw_mask
draw_reference


EXPORT
save_canvas
- image saved under C:\Users\mluerig\Downloads\phenopype-tutorials-main\tutorial_project\data\0__stickle2\canvas_plates-v1.jpg (overwritten).
save_annotation
- loading existing annotation file
- no annotation_type selected - exporting all annotations
- updating annotations of type "mask" with id "a" in "annotations_plates-v1.json" (overwrite="entry")
- updating annotations of type "contour" with id "a" in "annotations_plates-v1.json" (overwrite="entry")
- updating annotations of type "shape_features" with id "a" in "annotations_plates-v1.json" (overwrite="entry")
- writing annotations of type "reference" with id "a" to "annotations_plates-v1.json"


------------+++ finished pype iteration +++--------------
-------(End with Ctrl+Enter or re-run with Enter)--------


AUTOSHOW


TERMINATE

AUTOSAVE
- nothing to autosave
- no annotation_type selected - returning all annotations

AUTOLOAD
- annotations loaded:
{
"mask": ["a"],
"contour": ["a"],
"shape_features": ["a"]
}
- reference template image loaded from root directory
Stage: add annotation control args
Updating pype config: applying staged changes


------------+++ new pype iteration 2022-05-06 15:08:56 +++--------------




PREPROCESSING
detect_reference
---------------------------------------------------
Reference card found with 254 keypoint matches:
template image has 36.44 pixel per mm.
current image has 35.264 pixel per mm.
= 96.772 %% of template image.
---------------------------------------------------
create_mask
- loaded existing annotation of type "mask" with ID "a": skipping (edit=False)
blur


SEGMENTATION
threshold
- multichannel image supplied, converting to grayscale
- decompose image: using gray channel
- including pixels from 1 drawn masks 
- excluding pixels from reference
detect_contour
- loaded existing annotation of type "contour" with ID "a": overwriting (edit=overwrite)
- found 10 contours that match criteria


MEASUREMENT
compute_shape_features
- loaded existing annotation of type "shape_features" with ID "a": overwriting (edit=overwrite)


VISUALIZATION
select_canvas
- raw image
draw_contour
draw_mask
draw_reference


EXPORT
save_canvas
- image saved under C:\Users\mluerig\Downloads\phenopype-tutorials-main\tutorial_project\data\0__stickle3\canvas_plates-v1.jpg (overwritten).
save_annotation
- loading existing annotation file
- no annotation_type selected - exporting all annotations
- updating annotations of type "mask" with id "a" in "annotations_plates-v1.json" (overwrite="entry")
- updating annotations of type "contour" with id "a" in "annotations_plates-v1.json" (overwrite="entry")
- updating annotations of type "shape_features" with id "a" in "annotations_plates-v1.json" (overwrite="entry")
- writing annotations of type "reference" with id "a" to "annotations_plates-v1.json"


------------+++ finished pype iteration +++--------------
-------(End with Ctrl+Enter or re-run with Enter)--------


AUTOSHOW


TERMINATE

AUTOSAVE
- nothing to autosave
myproj.collect_results(tag="plates-v1",          
                       files="canvas", # 
                       folder="canvas-v1",
                       overwrite=True)
Created C:\Users\mluerig\Downloads\phenopype-tutorials-main\tutorial_project\results\canvas-v1
Search string: ['canvas_plates-v1']
Collected canvas_plates-v1.jpg from 0__stickle1
0__stickle1_canvas_plates-v1.jpg saved under C:\Users\mluerig\Downloads\phenopype-tutorials-main\tutorial_project\results\canvas-v1\0__stickle1_canvas_plates-v1.jpg.
Collected canvas_plates-v1.jpg from 0__stickle2
0__stickle2_canvas_plates-v1.jpg saved under C:\Users\mluerig\Downloads\phenopype-tutorials-main\tutorial_project\results\canvas-v1\0__stickle2_canvas_plates-v1.jpg.
Collected canvas_plates-v1.jpg from 0__stickle3
0__stickle3_canvas_plates-v1.jpg saved under C:\Users\mluerig\Downloads\phenopype-tutorials-main\tutorial_project\results\canvas-v1\0__stickle3_canvas_plates-v1.jpg.