Tutorial 1: A (very) brief python introduction

This notebook is meant to provide a very short overview of the basic python syntax needed for basic phenopype operability. This is useful if you have never used python before, but would like to be able to explore phenopype functionality on your own.

Additionally, if you are new to programming alltogether, you could have a look at this tutorial https://www.learnpython.org/ or find this reference useful https://docs.python.org/3/tutorial/.

If you are familiar with python, you probably want to skip ahead to Tutorial 2: Working with phenopype.

Python modules

At the beginning of our skript we import the python modules that we want to work with using import. When we import the module, we can call its methods and functions using what comes after import. In this case, these “bindings” are simply os.

[1]:
import os

We can inspect all the components of this module (i.e., its “namespace”) by using dir(): https://docs.python.org/3/library/functions.html?highlight=dir#dir). We can see that os is a comprehensive package with a lots of classes and functions:

[2]:
print(dir(os))
['DirEntry', 'F_OK', 'MutableMapping', 'O_APPEND', 'O_BINARY', 'O_CREAT', 'O_EXCL', 'O_NOINHERIT', 'O_RANDOM', 'O_RDONLY', 'O_RDWR', 'O_SEQUENTIAL', 'O_SHORT_LIVED', 'O_TEMPORARY', 'O_TEXT', 'O_TRUNC', 'O_WRONLY', 'P_DETACH', 'P_NOWAIT', 'P_NOWAITO', 'P_OVERLAY', 'P_WAIT', 'PathLike', 'R_OK', 'SEEK_CUR', 'SEEK_END', 'SEEK_SET', 'TMP_MAX', 'W_OK', 'X_OK', '_Environ', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_execvpe', '_exists', '_exit', '_fspath', '_get_exports_list', '_putenv', '_unsetenv', '_wrap_close', 'abc', 'abort', 'access', 'altsep', 'chdir', 'chmod', 'close', 'closerange', 'cpu_count', 'curdir', 'defpath', 'device_encoding', 'devnull', 'dup', 'dup2', 'environ', 'error', 'execl', 'execle', 'execlp', 'execlpe', 'execv', 'execve', 'execvp', 'execvpe', 'extsep', 'fdopen', 'fsdecode', 'fsencode', 'fspath', 'fstat', 'fsync', 'ftruncate', 'get_exec_path', 'get_handle_inheritable', 'get_inheritable', 'get_terminal_size', 'getcwd', 'getcwdb', 'getenv', 'getlogin', 'getpid', 'getppid', 'isatty', 'kill', 'linesep', 'link', 'listdir', 'lseek', 'lstat', 'makedirs', 'mkdir', 'name', 'open', 'pardir', 'path', 'pathsep', 'pipe', 'popen', 'putenv', 'read', 'readlink', 'remove', 'removedirs', 'rename', 'renames', 'replace', 'rmdir', 'scandir', 'sep', 'set_handle_inheritable', 'set_inheritable', 'spawnl', 'spawnle', 'spawnv', 'spawnve', 'st', 'startfile', 'stat', 'stat_result', 'statvfs_result', 'strerror', 'supports_bytes_environ', 'supports_dir_fd', 'supports_effective_ids', 'supports_fd', 'supports_follow_symlinks', 'symlink', 'sys', 'system', 'terminal_size', 'times', 'times_result', 'truncate', 'umask', 'uname_result', 'unlink', 'urandom', 'utime', 'waitpid', 'walk', 'write']

The double underscores (“dunders”) before and after the numeric string indicate that these are “special” names reserved for the python namespace (https://dbader.org/blog/meaning-of-underscores-in-python). We cannot tell what type of object is behind each name, e.g., whether it’s a function (like open) or a submodule, with a set of functions (like path). Higher level functions are accessed from the package namespace by joining the module name os with the function open, connected by a dot.

If you are ever unsure about what a package does, simply call help(os) (or any other package) to get some basic information. This also works for its methods and functions, e.g. for the open method:

[3]:
help(os.open)
Help on built-in function open in module nt:

open(path, flags, mode=511, *, dir_fd=None)
    Open a file for low level IO.  Returns a file descriptor (integer).

    If dir_fd is not None, it should be a file descriptor open to a directory,
      and path should be relative; path will then be relative to that directory.
    dir_fd may not be implemented on your platform.
      If it is unavailable, using it will raise a NotImplementedError.

path on the other hand is a submodule: here we see some functions we can use (they don’t have dunders), for example join, which can join separate character strings to a python-readable path string.

[4]:
print(dir(os.path))
['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_abspath_fallback', '_get_bothseps', '_getfinalpathname', '_getfullpathname', '_getvolumepathname', 'abspath', 'altsep', 'basename', 'commonpath', 'commonprefix', 'curdir', 'defpath', 'devnull', 'dirname', 'exists', 'expanduser', 'expandvars', 'extsep', 'genericpath', 'getatime', 'getctime', 'getmtime', 'getsize', 'isabs', 'isdir', 'isfile', 'islink', 'ismount', 'join', 'lexists', 'normcase', 'normpath', 'os', 'pardir', 'pathsep', 'realpath', 'relpath', 'samefile', 'sameopenfile', 'samestat', 'sep', 'split', 'splitdrive', 'splitext', 'stat', 'supports_unicode_filenames', 'sys']
[5]:
help(os.path.join)
Help on function join in module ntpath:

join(path, *paths)
    # Join two (or more) paths.

Paths and directories

Time to actually do something with a function. A useful function from the os module is listdir, which will list all files in a specified directory. If you don’t specify any directory, it will use the current directory

[6]:
os.listdir()
[6]:
['.ipynb_checkpoints',
 'example_1_detect_objects_isopods.ipynb',
 'example_2_landmarks_stickleback.ipynb',
 'example_3_shape_stickleback.ipynb',
 'example_4_video_analysis_stickleback.ipynb',
 'images',
 'tutorial_0.rst',
 'tutorial_1_python_intro.ipynb',
 'tutorial_2_phenopype_workflow.ipynb',
 'tutorial_3_managing_projects_1.ipynb',
 'tutorial_4_managing_projects_2.ipynb',
 'tutorial_5_gui_interactions.ipynb',
 'tutorial_6_video_analysis.ipynb',
 '_assets']
[8]:
os.listdir("images")
[8]:
['isopods.jpg',
 'phytoplankton.jpg',
 'snail.jpg',
 'stickle1.JPG',
 'stickle2.JPG',
 'stickle3.JPG',
 'stickleback_side.jpg',
 'stickleback_top.jpg']

Note that I used the relative path here - full paths of course are also possible. To check where you are, you can use getcwd (getCurrentWorkingDirectory):

[9]:
os.getcwd()
[9]:
'E:\\git_repos\\phenopype\\tutorials'

listdir, as the name suggest, creates a list. The list can be accessed with squarebrackets and by giving the position inside you want to have returned.

IMPORTANT:

In python, referencing starts with 0 and not with 1, as in R for example.

[10]:
image_dir = "images"
image_list = os.listdir(image_dir)
print(image_list)
print(image_list[0])
['isopods.jpg', 'phytoplankton.jpg', 'snail.jpg', 'stickle1.JPG', 'stickle2.JPG', 'stickle3.JPG', 'stickleback_side.jpg', 'stickleback_top.jpg']
isopods.jpg

So far we have only returned name-strings of our files, but unless our current working directory is the same as the target directory (or we have added it to the PYTHONPATH), we need the full or relative path of our files. We can get it using the path submodule. Getting the absolute path of a file is usually a two-part coding step: joining the directory path and the names of files within it. The joining operation is done with join:

Although you can join path strings by simply adding them, this sometimes leads to unexpected results, so better try to avoid it:

[11]:
image_dir + image_list[0] # doesn't work
[11]:
'imagesisopods.jpg'
[12]:
os.path.join(image_dir, image_list[0]) # works
[12]:
'images\\isopods.jpg'

Putting it all together, let’s try to get the path of all the images in our directory. For this, we need a for loop, and an empty list we can populate:

[13]:
filepaths = [] # square-brakets make an empty list
names = os.listdir(image_dir) # making a list of all the files names inside a directory

for i in names: # looping along our list of names
    filepath = os.path.join(image_dir, i) # joining name and path strings
    filepaths.append(filepath) # appending the joint string to the list

filepaths # showing the list content

[13]:
['images\\isopods.jpg',
 'images\\phytoplankton.jpg',
 'images\\snail.jpg',
 'images\\stickle1.JPG',
 'images\\stickle2.JPG',
 'images\\stickle3.JPG',
 'images\\stickleback_side.jpg',
 'images\\stickleback_top.jpg']

Images in OpenCV

Let’s import another module. In fact the module that phenopype is built around: OpenCV (see the resources section of the documentation). Note: sometimes the modules are called differently than their bindings in python, e.g., for opencv, we have to call cv2. Once imported, we then can use a basic opencv function: importing an image as an array using imread.

[14]:
import cv2
img = cv2.imread("images/isopods.jpg")

About digital images: cv2.imread converts a digital image file to an array, a stack of three matrices containing pixel wise information on the red, green and blue channel of an image, which - taken together - creates the colour image. To learn more about digital images and computer vision, check the resources section of the documentation.

Lets examine our image. It is not informative neither possible to look at the whole matrix of pixel values inside the console, but sometimes it is useful to look at specific features of your image, which we can acess directly from the image-object. For example, the dimensions, using shape:

[15]:
img.shape
[15]:
(1997, 2996, 3)

Or the mean pixel intensity:

[16]:
img.mean()
[16]:
160.3892609608672

We can look at the picture with imshow. However, because many opencv functions are GUI based, we need to add some more controller functions so we can control it.

[17]:
cv2.namedWindow('image', cv2.WINDOW_NORMAL) # open a resizable window
cv2.imshow('image',img) # show the image in that window
cv2.waitKey(0) # do nothing until a keystroke ...
cv2.destroyAllWindows() # ... and then close all open windows

Images in phenopype

Complicated. Luckily, phenopype has a neat functions to load and display images. It even can even zoom in using the scroll wheel:

[18]:
import phenopype as pp
img = pp.load_image("images/isopods.jpg")
pp.show_image(img)

show_image can also work with lists of arrays / images, to show multiple images at the same time. Individual zoom possible. Close with Esc or Enter

[21]:
import os
images = [] # square-brakets make an empty list
names = os.listdir("images") # making a list of all the files names inside a directory

for i in names: # looping along our list of names
    filepath = os.path.join("images", i) # joining name and path strings
    images.append(pp.load_image(filepath)) # load images and store them in list

pp.show_image(images, position_offset=100) ## show all images in the image folder in the tutorial directory