├── .gitignore ├── LICENSE.txt ├── README.md ├── example ├── data │ ├── flow_example_output.jpg │ ├── mpi-sintel-01.png │ ├── mpi-sintel-02.png │ └── mpi-sintel-03.png └── main.py ├── flow_vis ├── __init__.py └── flow_vis.py ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | cover/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | # For a library or package, you might want to ignore these files since the code is 88 | # intended to run in multiple environments; otherwise, check them in: 89 | # .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 99 | __pypackages__/ 100 | 101 | # Celery stuff 102 | celerybeat-schedule 103 | celerybeat.pid 104 | 105 | # SageMath parsed files 106 | *.sage.py 107 | 108 | # Environments 109 | .env 110 | .venv 111 | env/ 112 | venv/ 113 | ENV/ 114 | env.bak/ 115 | venv.bak/ 116 | 117 | # Spyder project settings 118 | .spyderproject 119 | .spyproject 120 | 121 | # Rope project settings 122 | .ropeproject 123 | 124 | # mkdocs documentation 125 | /site 126 | 127 | # mypy 128 | .mypy_cache/ 129 | .dmypy.json 130 | dmypy.json 131 | 132 | # Pyre type checker 133 | .pyre/ 134 | 135 | # pytype static type analyzer 136 | .pytype/ 137 | 138 | # Cython debug symbols 139 | cython_debug/ 140 | 141 | # static files generated from Django application using `collectstatic` 142 | media 143 | static -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018-2020 Tom Runia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Optical Flow Visualization 2 | 3 | Python port of the optical flow visualization: [people.csail.mit.edu/celiu/OpticalFlow/](https://people.csail.mit.edu/celiu/OpticalFlow/). 4 | This implementation is based on the color wheel presented in: 5 | 6 | ``` 7 | S. Baker, D. Scharstein, J. Lewis, S. Roth, M. J. Black, and R. Szeliski. 8 | A database and evaluation methodology for optical flow. 9 | In Proc. IEEE International Conference on Computer Vision (ICCV), 2007. 10 | ``` 11 | 12 | ## Installation 13 | 14 | pip install flow_vis 15 | 16 | ## Usage 17 | 18 | import flow_vis 19 | flow_color = flow_vis.flow_to_color(flow_uv, convert_to_bgr=False) 20 | 21 | ## Examples visualizations 22 | 23 | Example visualization from the MPI Sintel Dataset: 24 | 25 | ![MPI Sintel 01](./example/data/mpi-sintel-01.png) 26 | 27 | ![MPI Sintel 02](./example/data/mpi-sintel-03.png) 28 | 29 | ![MPI Sintel 03](./example/data/mpi-sintel-02.png) 30 | 31 | 32 | ## Acknowledgements 33 | 34 | I would like to thank [Susana Bouchardet](https://github.com/sbouchardet) for preparing the repository for PyPi. 35 | -------------------------------------------------------------------------------- /example/data/flow_example_output.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomrunia/OpticalFlow_Visualization/248d9e4bcfa623017322a86ebf5e019b3589b0da/example/data/flow_example_output.jpg -------------------------------------------------------------------------------- /example/data/mpi-sintel-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomrunia/OpticalFlow_Visualization/248d9e4bcfa623017322a86ebf5e019b3589b0da/example/data/mpi-sintel-01.png -------------------------------------------------------------------------------- /example/data/mpi-sintel-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomrunia/OpticalFlow_Visualization/248d9e4bcfa623017322a86ebf5e019b3589b0da/example/data/mpi-sintel-02.png -------------------------------------------------------------------------------- /example/data/mpi-sintel-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomrunia/OpticalFlow_Visualization/248d9e4bcfa623017322a86ebf5e019b3589b0da/example/data/mpi-sintel-03.png -------------------------------------------------------------------------------- /example/main.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2018 Tom Runia 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to conditions. 11 | # 12 | # Author: Tom Runia 13 | # Date Created: 2018-08-03 14 | 15 | from __future__ import absolute_import 16 | from __future__ import division 17 | from __future__ import print_function 18 | 19 | import numpy as np 20 | import matplotlib.pyplot as plt 21 | import flow_vis 22 | 23 | # Load normalized flow image of shape [H,W,2] 24 | flow_uv = np.load('./data/flow_example_data.npy') 25 | 26 | # Apply the coloring (for OpenCV, set convert_to_bgr=True) 27 | flow_color = flow_vis.flow_to_color(flow_uv, convert_to_bgr=False) 28 | 29 | # Display the image 30 | plt.imshow(flow_color) 31 | plt.show() 32 | -------------------------------------------------------------------------------- /flow_vis/__init__.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019, Tom Runia 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to conditions. 11 | # 12 | # Author: Tom Runia (firstname.lastname@gmail.com) 13 | # Date Created: 2020-04-01 14 | 15 | from __future__ import absolute_import 16 | from __future__ import division 17 | from __future__ import print_function 18 | 19 | from .flow_vis import flow_to_color, flow_uv_to_colors, make_colorwheel 20 | -------------------------------------------------------------------------------- /flow_vis/flow_vis.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2018 Tom Runia 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to conditions. 11 | # 12 | # Author: Tom Runia 13 | # Date Created: 2018-08-03 14 | 15 | import numpy as np 16 | 17 | def make_colorwheel(): 18 | """ 19 | Generates a color wheel for optical flow visualization as presented in: 20 | Baker et al. "A Database and Evaluation Methodology for Optical Flow" (ICCV, 2007) 21 | URL: http://vision.middlebury.edu/flow/flowEval-iccv07.pdf 22 | 23 | Code follows the original C++ source code of Daniel Scharstein. 24 | Code follows the the Matlab source code of Deqing Sun. 25 | 26 | Returns: 27 | np.ndarray: Color wheel 28 | """ 29 | 30 | RY = 15 31 | YG = 6 32 | GC = 4 33 | CB = 11 34 | BM = 13 35 | MR = 6 36 | 37 | ncols = RY + YG + GC + CB + BM + MR 38 | colorwheel = np.zeros((ncols, 3)) 39 | col = 0 40 | 41 | # RY 42 | colorwheel[0:RY, 0] = 255 43 | colorwheel[0:RY, 1] = np.floor(255*np.arange(0,RY)/RY) 44 | col = col+RY 45 | # YG 46 | colorwheel[col:col+YG, 0] = 255 - np.floor(255*np.arange(0,YG)/YG) 47 | colorwheel[col:col+YG, 1] = 255 48 | col = col+YG 49 | # GC 50 | colorwheel[col:col+GC, 1] = 255 51 | colorwheel[col:col+GC, 2] = np.floor(255*np.arange(0,GC)/GC) 52 | col = col+GC 53 | # CB 54 | colorwheel[col:col+CB, 1] = 255 - np.floor(255*np.arange(CB)/CB) 55 | colorwheel[col:col+CB, 2] = 255 56 | col = col+CB 57 | # BM 58 | colorwheel[col:col+BM, 2] = 255 59 | colorwheel[col:col+BM, 0] = np.floor(255*np.arange(0,BM)/BM) 60 | col = col+BM 61 | # MR 62 | colorwheel[col:col+MR, 2] = 255 - np.floor(255*np.arange(MR)/MR) 63 | colorwheel[col:col+MR, 0] = 255 64 | return colorwheel 65 | 66 | 67 | def flow_uv_to_colors(u, v, convert_to_bgr=False): 68 | """ 69 | Applies the flow color wheel to (possibly clipped) flow components u and v. 70 | 71 | According to the C++ source code of Daniel Scharstein 72 | According to the Matlab source code of Deqing Sun 73 | 74 | Args: 75 | u (np.ndarray): Input horizontal flow of shape [H,W] 76 | v (np.ndarray): Input vertical flow of shape [H,W] 77 | convert_to_bgr (bool, optional): Convert output image to BGR. Defaults to False. 78 | 79 | Returns: 80 | np.ndarray: Flow visualization image of shape [H,W,3] 81 | """ 82 | flow_image = np.zeros((u.shape[0], u.shape[1], 3), np.uint8) 83 | colorwheel = make_colorwheel() # shape [55x3] 84 | ncols = colorwheel.shape[0] 85 | rad = np.sqrt(np.square(u) + np.square(v)) 86 | a = np.arctan2(-v, -u)/np.pi 87 | fk = (a+1) / 2*(ncols-1) 88 | k0 = np.floor(fk).astype(np.int32) 89 | k1 = k0 + 1 90 | k1[k1 == ncols] = 0 91 | f = fk - k0 92 | for i in range(colorwheel.shape[1]): 93 | tmp = colorwheel[:,i] 94 | col0 = tmp[k0] / 255.0 95 | col1 = tmp[k1] / 255.0 96 | col = (1-f)*col0 + f*col1 97 | idx = (rad <= 1) 98 | col[idx] = 1 - rad[idx] * (1-col[idx]) 99 | col[~idx] = col[~idx] * 0.75 # out of range 100 | # Note the 2-i => BGR instead of RGB 101 | ch_idx = 2-i if convert_to_bgr else i 102 | flow_image[:,:,ch_idx] = np.floor(255 * col) 103 | return flow_image 104 | 105 | 106 | def flow_to_color(flow_uv, clip_flow=None, convert_to_bgr=False): 107 | """ 108 | Expects a two dimensional flow image of shape. 109 | 110 | Args: 111 | flow_uv (np.ndarray): Flow UV image of shape [H,W,2] 112 | clip_flow (float, optional): Clip maximum of flow values. Defaults to None. 113 | convert_to_bgr (bool, optional): Convert output image to BGR. Defaults to False. 114 | 115 | Returns: 116 | np.ndarray: Flow visualization image of shape [H,W,3] 117 | """ 118 | assert flow_uv.ndim == 3, 'input flow must have three dimensions' 119 | assert flow_uv.shape[2] == 2, 'input flow must have shape [H,W,2]' 120 | if clip_flow is not None: 121 | flow_uv = np.clip(flow_uv, 0, clip_flow) 122 | u = flow_uv[:,:,0] 123 | v = flow_uv[:,:,1] 124 | rad = np.sqrt(np.square(u) + np.square(v)) 125 | rad_max = np.max(rad) 126 | epsilon = 1e-5 127 | u = u / (rad_max + epsilon) 128 | v = v / (rad_max + epsilon) 129 | return flow_uv_to_colors(u, v, convert_to_bgr) 130 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | setuptools.setup( 4 | name='flow_vis', 5 | packages=['flow_vis'], 6 | version='0.1', 7 | license='MIT', 8 | author='Tom Runia', 9 | author_email='firstname.lastname@gmail.com', 10 | description='Easy optical flow visualisation in Python.', 11 | long_description='Python port of the optical flow visualization: https://people.csail.mit.edu/celiu/OpticalFlow/', 12 | url='https://github.com/tomrunia/OpticalFlow_Visualization', 13 | download_url='https://github.com/tomrunia/OpticalFlow_Visualization/archive/0.1.tar.gz', 14 | keywords=['optical flow', 'visualization', 'motion'], 15 | install_requires=['numpy'], 16 | classifiers=[ 17 | 'Programming Language :: Python :: 3', 18 | 'Development Status :: 5 - Production/Stable', 19 | 'Intended Audience :: Developers', 20 | 'License :: OSI Approved :: MIT License', 21 | ], 22 | python_requires='>=3.6', 23 | ) --------------------------------------------------------------------------------