{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Tutorial 1: A (very) brief python introduction\n", "\n", "This tutorial is meant to provide a very short overview of the basic Python syntax needed for basic phenopype operability, which might be useful if you have never used Python before, but would like to be able to explore phenopype functionality on your own. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you are familiar with Python, you may want to skip ahead to [Tutorial 2: Interacting with images in phenopype](tutorial_2.ipynb).\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "
\n", "

See also:

\n", "

\n", "\n", "* [Python resources in the phenopype docs](https://www.phenopype.org/docs/resources/python/)\n", "* [CV resources in the phenopype docs](https://www.phenopype.org/docs/resources/cv/)\n", "\n", "

\n", "
\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Python modules \n", "\n", "At the beginning of our script 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` - for instance, the `os` module, which provides functions for interacting with the operating system." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import os" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can inspect all the components of this module (i.e., its \"namespace\") by using `dir()`. We can see that `os` is a comprehensive package with a lots of classes and functions:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['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__', '_check_methods', '_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']\n" ] } ], "source": [ "print(dir(os))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on built-in function open in module nt:\n", "\n", "open(path, flags, mode=511, *, dir_fd=None)\n", " Open a file for low level IO. Returns a file descriptor (integer).\n", " \n", " If dir_fd is not None, it should be a file descriptor open to a directory,\n", " and path should be relative; path will then be relative to that directory.\n", " dir_fd may not be implemented on your platform.\n", " If it is unavailable, using it will raise a NotImplementedError.\n", "\n" ] } ], "source": [ "help(os.open)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`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." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['__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']\n" ] } ], "source": [ "print(dir(os.path))" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function join in module ntpath:\n", "\n", "join(path, *paths)\n", " # Join two (or more) paths.\n", "\n" ] } ], "source": [ "help(os.path.join)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Paths and directories \n", "\n", "Time to actually do something with a function. A useful function from the `os` module is `getcwd`, which will return the current working directory as a string (all paths in the `os` module are treated as strings):" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'D:\\\\git-repos\\\\phenopype\\\\phenopype-tutorials\\\\tutorials'" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "os.getcwd()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another frequently used function is `listdir`, which will list all files in a specified directory. If you don't specify any directory, it will use the current working directory" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['.ipynb_checkpoints',\n", " 'data',\n", " 'tutorial_1.ipynb',\n", " 'tutorial_2.ipynb',\n", " 'tutorial_3.ipynb',\n", " 'tutorial_4.ipynb',\n", " 'tutorial_5.ipynb',\n", " 'tutorial_6.ipynb',\n", " 'tutorial_7.ipynb',\n", " '_figures']" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "os.listdir()" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['isopods_fish.mp4',\n", " 'stickle1.jpg',\n", " 'stickle2.jpg',\n", " 'stickle3.jpg',\n", " 'stickleback_side.jpg',\n", " 'stickleback_top.jpg']" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "os.listdir(\"data\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that I used the relative path here - full paths of course are also possible. \n", "\n", "`listdir`, as the name suggest, creates a list:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['isopods_fish.mp4', 'stickle1.jpg', 'stickle2.jpg', 'stickle3.jpg', 'stickleback_side.jpg', 'stickleback_top.jpg']\n" ] } ], "source": [ "image_list = os.listdir(\"data\")\n", "print(image_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now can access `image_list` with hard brackets and by giving the position inside you want to have returned:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**IMPORTANT - read before continuing**\n", "\n", "In python, referencing starts with `0` and not with `1`.\n", " \n", "
" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "isopods_fish.mp4\n" ] } ], "source": [ "print(image_list[0])" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "stickle1.jpg\n" ] } ], "source": [ "print(image_list[1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far we have only returned name-strings of our files, but unless we will always stay in the current working directory, we need the full or relative path of our files. We can get it using the `path` submodule and the `join` function to paste the directory path and the names of files within it:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'data\\\\stickle1.jpg'" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "os.path.join(\"data\", image_list[1]) # works" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Although you can join path strings by simply adding them, this sometimes leads to unexpected results, so better try to avoid manipulating strings directly and instead use the `join` function:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'datastickle1.jpg'" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"data\" + image_list[1] # doesn't work" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'data\\\\stickle1.jpg'" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"data\" \"\\\\\" + image_list[1] # works" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['data\\\\isopods_fish.mp4',\n", " 'data\\\\stickle1.jpg',\n", " 'data\\\\stickle2.jpg',\n", " 'data\\\\stickle3.jpg',\n", " 'data\\\\stickleback_side.jpg',\n", " 'data\\\\stickleback_top.jpg']" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "image_dir = \"data\" # save image directory to a string variable\n", "filepaths = [] # square-brakets make an empty list\n", "names = os.listdir(image_dir) # making a list of all the files names inside a directory\n", "\n", "for i in names: # looping along our list of names\n", " filepath = os.path.join(image_dir, i) # joining name and path strings \n", " filepaths.append(filepath) # appending the joint string to the list\n", " \n", "filepaths # showing the list content" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Images in OpenCV \n", "\n", "Let's import another module. In fact _the_ module that phenopype is built around: OpenCV. Note that mopdule bindings in python can be called differently than the package name - 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`." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "numpy.ndarray" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import cv2\n", "img = cv2.imread(r\"data/stickle1.jpg\")\n", "\n", "type(img) ## check loading the image worked: should return numpy.ndarray \n", "## \"imread\" will silently fail if the path is incorrect, in which case \"img\" will be \"NoneType\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lets examine our image: `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. It is neither informative nor possible to look at the whole pixel array 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`:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1600, 2400, 3)" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "img.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or the mean pixel intensity:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "220.84537473958332" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "img.mean()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can look at the picture with `imshow`. However, because many `opencv` functions are GUI based, we need to add some more controller functions to open and close a window." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "cv2.namedWindow('image', cv2.WINDOW_NORMAL) # open a resizable window\n", "cv2.imshow('image',img) # show the image in that window\n", "cv2.waitKey(0) # do nothing until a keystroke ...\n", "cv2.destroyAllWindows() # ... and then close all open windows" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Four lines of code to display an image - this will clutter your code really fast. Luckily, phenopype has some functions of its own to load and display images. Continue with [Tutorial 2](tutorial_2.ipynb) to find out how phenopype works with images." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.13" } }, "nbformat": 4, "nbformat_minor": 2 }