Example 8: Extracting shape features from fossil teeth of cichlids

This example demonstrates how basic and complex features of tooth-shapes are extracted from images containing single and multiple microscope images. Two separate phenopype projects are used for better organization (single tooth and multiple tooth per images).

All images are kindly provided by Ole Seehausen.

Cichlid jaw Fig. 1 - Teeth of haplochromine cichlids, still attached to the jaw.

Single teeth

These are single-tooth images where the background is brighter than the teeth, and also quite noisy. The challenges here lie in preventing unwanted objects from being detected (e.g. the checkered paper). In this case, the easiest way is to draw a rectangle mask around the object. Furthermore, the scale bar needs to be measured in every image because it varies in size between images. This is because the microscope software returns non-standardized scale bars that vary between images. However, this is done quickly using create_reference.

Before - single tooth case
After - single tooth case

Preparation - single tooth

[1]:
import phenopype as pp
import os

## change working dir - will contain project folder
os.chdir(r"D:\git-repos\phenopype\phenopype-gallery\_temp")

## set project name
project_name = "project_8a"

## fetch template from downloaded template repo (https://github.com/phenopype/phenopype-templates)
template_path = r"D:\git-repos\phenopype\phenopype-templates\templates\gallery\project_8a.yaml"

## set directory with images
image_dir = r"D:\git-repos\phenopype\phenopype-gallery\gallery\data"
phenopype successfully imported the following plugin dependencies:
phenomorph, keras

Project

[2]:
proj = pp.Project(project_name)
--------------------------------------------
Creating a new phenopype project directory at:
D:\git-repos\phenopype\phenopype-gallery\_temp\project_8a

Proceed? (y/n)
y

Project "project_8a" successfully created.
--------------------------------------------
[3]:
## add tooth-images from the data folder, but exclude the images containing multiple teeth
proj.add_files(image_dir = image_dir, include="cichlid", exclude="multi")
--------------------------------------------
phenopype will search for image files at

D:\git-repos\phenopype\phenopype-gallery\gallery\data

using the following settings:

filetypes: ['jpg', 'JPG', 'jpeg', 'JPEG', 'tif', 'png', 'bmp'], include: cichlid, exclude: multi, mode: copy, recursive: False, resize: False, unique: path

Found image cichlid1.jpg - phenopype-project folder 0__cichlid1 created
Found image cichlid2.jpg - phenopype-project folder 0__cichlid2 created
Found image cichlid3.jpg - phenopype-project folder 0__cichlid3 created

Found 3 files - using all
--------------------------------------------
[4]:
## add the config template; provide a tag
proj.add_config(template_path=template_path, tag="v1", overwrite=True)
- template saved under D:\git-repos\phenopype\phenopype-gallery\_temp\project_8a\data\0__cichlid1\pype_config_v1.yaml
- template saved under D:\git-repos\phenopype\phenopype-gallery\_temp\project_8a\data\0__cichlid2\pype_config_v1.yaml
- template saved under D:\git-repos\phenopype\phenopype-gallery\_temp\project_8a\data\0__cichlid3\pype_config_v1.yaml
[5]:
## run image processing
for path in proj.dir_paths:
    pp.Pype(path, tag="v1")

AUTOLOAD
 - nothing to autoload
Stage: add annotation control args
Stage: add annotation control args
Stage: fixed method name
Stage: add annotation control args
Stage: add annotation control args
Updating pype config: applying staged changes


------------+++ new pype iteration 2022-07-29 11:42:57 +++--------------




PREPROCESSING
create_mask
create_reference
Reference set
remove
remove
remove
Reference set


SEGMENTATION
blur
threshold
- decompose image: using blue channel
- including pixels from 1 drawn masks
- excluding pixels from reference
morphology
detect_contour
- found 1 contours that match criteria


MEASUREMENT
compute_shape_features


VISUALIZATION
select_canvas
- blue channel
draw_contour


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


AUTOSHOW
An exception has occurred, use %tb to see the full traceback.

SystemExit:

TERMINATE (by user)

C:\Miniconda3\envs\pp-dev\lib\site-packages\IPython\core\interactiveshell.py:3560: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.
  warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)
[7]:
## this collects all annotations
proj.collect_results(tag="v1", files="annotations",  folder="annotations", overwrite=True)
Search string: ['annotations_v1']
Collected annotations_v1.json from 0__cichlid1
0__cichlid1_annotations_v1.json saved under D:\workspace\git-repos\phenopype\phenopype-gallery\_temp\project_8\project_a\results\annotations\0__cichlid1_annotations_v1.json (overwritten).
Collected annotations_v1.json from 0__cichlid2
0__cichlid2_annotations_v1.json saved under D:\workspace\git-repos\phenopype\phenopype-gallery\_temp\project_8\project_a\results\annotations\0__cichlid2_annotations_v1.json (overwritten).
Collected annotations_v1.json from 0__cichlid3
0__cichlid3_annotations_v1.json saved under D:\workspace\git-repos\phenopype\phenopype-gallery\_temp\project_8\project_a\results\annotations\0__cichlid3_annotations_v1.json (overwritten).
[8]:
## display results
import ipyplot ## install with `pip install ipyplot`

canvas_list = []
for path in proj.dir_paths:
    canvas_list.append(pp.load_image(os.path.join(path, "canvas_v1.jpg"), mode="rgb"))

ipyplot.plot_images(canvas_list, img_width=300)

Multiple teeth

Now we will implement the same procedure for the images with multiple teeth, which require only a slightly altered configuration file: the maine difference that we are excluding the reference scale (exclude when drawing the mask with include: false), and some slightly altered settigngs for threshold (invert:true considers inverted values of the images, to make the dark background light and the bright teeth dark for the algorithm to work).

Before
After

Preparation - multiple teeth

[7]:
## set project name
project_name = "project_8b"

## fetch template from downloaded template repo (https://github.com/phenopype/phenopype-templates)
template_path = r"D:\git-repos\phenopype\phenopype-templates\templates\gallery\project_8b.yaml"
[8]:
proj = pp.Project(project_name)
--------------------------------------------
Creating a new phenopype project directory at:
D:\git-repos\phenopype\phenopype-gallery\_temp\project_8b

Proceed? (y/n)
y

Project "project_8b" successfully created.
--------------------------------------------
[9]:
## add the images containing multiple teeth
proj.add_files(image_dir = image_dir, include="cichlid_multi")
--------------------------------------------
phenopype will search for image files at

D:\git-repos\phenopype\phenopype-gallery\gallery\data

using the following settings:

filetypes: ['jpg', 'JPG', 'jpeg', 'JPEG', 'tif', 'png', 'bmp'], include: cichlid_multi, exclude: [], mode: copy, recursive: False, resize: False, unique: path

Found image cichlid_multi1.jpg - phenopype-project folder 0__cichlid_multi1 created
Found image cichlid_multi2.jpg - phenopype-project folder 0__cichlid_multi2 created
Found image cichlid_multi3.jpg - phenopype-project folder 0__cichlid_multi3 created

Found 3 files - using all
--------------------------------------------
[10]:
## add the config template; provide a tag
proj.add_config(template_path=template_path, tag="v1", overwrite=True)
- template saved under D:\git-repos\phenopype\phenopype-gallery\_temp\project_8b\data\0__cichlid_multi1\pype_config_v1.yaml
- template saved under D:\git-repos\phenopype\phenopype-gallery\_temp\project_8b\data\0__cichlid_multi2\pype_config_v1.yaml
- template saved under D:\git-repos\phenopype\phenopype-gallery\_temp\project_8b\data\0__cichlid_multi3\pype_config_v1.yaml
[11]:
## run image processing
for path in proj.dir_paths:
    pp.Pype(path, tag="v1")

AUTOLOAD
 - nothing to autoload
Stage: add annotation control args
Stage: add annotation control args
Stage: add annotation control args
Stage: add annotation control args
Updating pype config: applying staged changes


------------+++ new pype iteration 2022-07-29 11:43:56 +++--------------




PREPROCESSING
create_mask
create_reference
Reference set
already two points selected
remove
remove
Reference set


SEGMENTATION
blur
threshold
- multichannel image supplied, converting to grayscale
- decompose image: using gray channel
- excluding pixels from 1 drawn masks
- excluding pixels from reference
morphology
detect_contour
- found 37 contours that match criteria


MEASUREMENT
compute_shape_features


VISUALIZATION
select_canvas
- raw image
draw_contour


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


AUTOSHOW


TERMINATE

AUTOSAVE
- save_canvas
- image saved under D:\git-repos\phenopype\phenopype-gallery\_temp\project_8b\data\0__cichlid_multi1\canvas_v1.jpg.
- save_annotation
- creating new annotation file
- no annotation_type selected - exporting all annotations
- writing annotations of type "mask" with id "a" to "annotations_v1.json"
- writing annotations of type "reference" with id "a" to "annotations_v1.json"
- writing annotations of type "contour" with id "a" to "annotations_v1.json"
- writing annotations of type "shape_features" with id "a" to "annotations_v1.json"

AUTOLOAD
 - nothing to autoload
Stage: add annotation control args
Stage: add annotation control args
Stage: add annotation control args
Stage: add annotation control args
Updating pype config: applying staged changes


------------+++ new pype iteration 2022-07-29 11:44:31 +++--------------




PREPROCESSING
create_mask
An exception has occurred, use %tb to see the full traceback.

SystemExit:

TERMINATE (by user)

[14]:
## display results
import ipyplot ## install with `pip install ipyplot`

canvas_list = []
for path in proj.dir_paths:
    canvas_list.append(pp.load_image(os.path.join(path, "canvas_v1.jpg"), mode="rgb"))

ipyplot.plot_images(canvas_list, img_width=300)
C:\Anaconda3\envs\pp_dev\lib\site-packages\ipyplot\_utils.py:97: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
  return np.asarray(seq)

2