├── .coveragerc ├── .flake8 ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .mypy.ini ├── HornSchunck.py ├── LICENSE.txt ├── LucasKanade.py ├── README.md ├── matlab ├── HornSchunck.m ├── RunHornSchunck.m ├── computeDerivatives.m ├── gaussFilter.m ├── plotFlow.m └── smoothImg.m ├── setup.cfg ├── setup.py └── src └── pyoptflow ├── __init__.py ├── hornschunck.py ├── lucaskanade.py ├── plots.py └── tests ├── data ├── box │ ├── box.0.bmp │ └── box.1.bmp ├── office │ ├── office.0.bmp │ ├── office.1.bmp │ ├── office.10.bmp │ ├── office.11.bmp │ ├── office.12.bmp │ ├── office.13.bmp │ ├── office.14.bmp │ ├── office.15.bmp │ ├── office.16.bmp │ ├── office.17.bmp │ ├── office.18.bmp │ ├── office.19.bmp │ ├── office.2.bmp │ ├── office.3.bmp │ ├── office.4.bmp │ ├── office.5.bmp │ ├── office.6.bmp │ ├── office.7.bmp │ ├── office.8.bmp │ └── office.9.bmp ├── rubic │ ├── rubic.-1 │ ├── rubic.-1.bmp │ ├── rubic.0 │ ├── rubic.0.bmp │ ├── rubic.1 │ ├── rubic.1.bmp │ ├── rubic.10 │ ├── rubic.10.bmp │ ├── rubic.11 │ ├── rubic.11.bmp │ ├── rubic.12 │ ├── rubic.12.bmp │ ├── rubic.13 │ ├── rubic.13.bmp │ ├── rubic.14 │ ├── rubic.14.bmp │ ├── rubic.15 │ ├── rubic.15.bmp │ ├── rubic.16 │ ├── rubic.16.bmp │ ├── rubic.17 │ ├── rubic.17.bmp │ ├── rubic.18 │ ├── rubic.18.bmp │ ├── rubic.19 │ ├── rubic.19.bmp │ ├── rubic.2 │ ├── rubic.2.bmp │ ├── rubic.3 │ ├── rubic.3.bmp │ ├── rubic.4 │ ├── rubic.4.bmp │ ├── rubic.5 │ ├── rubic.5.bmp │ ├── rubic.6 │ ├── rubic.6.bmp │ ├── rubic.7 │ ├── rubic.7.bmp │ ├── rubic.8 │ ├── rubic.8.bmp │ ├── rubic.9 │ └── rubic.9.bmp └── sphere │ ├── sphere.0.bmp │ ├── sphere.1.bmp │ ├── sphere.10.bmp │ ├── sphere.11.bmp │ ├── sphere.12.bmp │ ├── sphere.13.bmp │ ├── sphere.14.bmp │ ├── sphere.15.bmp │ ├── sphere.16.bmp │ ├── sphere.17.bmp │ ├── sphere.18.bmp │ ├── sphere.19.bmp │ ├── sphere.2.bmp │ ├── sphere.3.bmp │ ├── sphere.4.bmp │ ├── sphere.5.bmp │ ├── sphere.6.bmp │ ├── sphere.7.bmp │ ├── sphere.8.bmp │ └── sphere.9.bmp └── test_all.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | cover_pylib = false 3 | omit = 4 | /home/travis/virtualenv/* 5 | */site-packages/* 6 | */bin/* 7 | 8 | [report] 9 | exclude_lines = 10 | pragma: no cover 11 | def __repr__ 12 | except RuntimeError 13 | except NotImplementedError 14 | except ImportError 15 | except FileNotFoundError 16 | except CalledProcessError 17 | logging.warning 18 | logging.error 19 | logging.critical 20 | if __name__ == .__main__.: -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 132 3 | exclude = .git,__pycache__,.eggs/,doc/,docs/,build/,dist/,archive/ 4 | per-file-ignores = 5 | __init__.py:F401 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | paths: 6 | - "**.py" 7 | - ".github/workflows/ci.yml" 8 | pull_request: 9 | paths: 10 | - "**.py" 11 | 12 | jobs: 13 | 14 | linux: 15 | runs-on: ubuntu-latest 16 | timeout-minutes: 5 17 | strategy: 18 | matrix: 19 | python-version: [ '3.7', '3.9' ] 20 | name: Lint Python ${{ matrix.python-version }} 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | - uses: actions/setup-python@v2 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | 28 | - run: pip install .[tests,lint] 29 | - run: flake8 30 | - run: mypy 31 | - run: pytest 32 | 33 | integration: 34 | needs: linux 35 | runs-on: ${{ matrix.os }} 36 | strategy: 37 | matrix: 38 | os: [windows-latest, macos-latest] 39 | 40 | steps: 41 | - uses: actions/checkout@v2 42 | - uses: actions/setup-python@v2 43 | with: 44 | python-version: '3.7' 45 | 46 | - run: pip install .[tests] 47 | - run: pytest 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .mypy_cache/ 2 | .pytest_cache/ 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 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 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | .hypothesis/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # IPython Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # dotenv 82 | .env 83 | 84 | # virtualenv 85 | venv/ 86 | ENV/ 87 | 88 | # Spyder project settings 89 | .spyderproject 90 | 91 | # Rope project settings 92 | .ropeproject 93 | -------------------------------------------------------------------------------- /.mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | files = src/ 3 | 4 | ignore_missing_imports = True 5 | strict_optional = False 6 | allow_redefinition = True 7 | -------------------------------------------------------------------------------- /HornSchunck.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | python HornSchunck.py data/box box.* 4 | python HornSchunck.py data/office office.* 5 | python HornSchunck.py data/rubic rubic.* 6 | python HornSchunck.py data/sphere sphere.* 7 | """ 8 | # from scipy.ndimage.filters import gaussian_filter 9 | import imageio 10 | from pathlib import Path 11 | from matplotlib.pyplot import show 12 | from argparse import ArgumentParser 13 | 14 | from pyoptflow import HornSchunck, getimgfiles 15 | from pyoptflow.plots import compareGraphs 16 | 17 | FILTER = 7 18 | 19 | 20 | def main(): 21 | p = ArgumentParser(description="Pure Python Horn Schunck Optical Flow") 22 | p.add_argument("stem", help="path/stem of files to analyze") 23 | p.add_argument("pat", help="glob pattern of files", default="*.bmp") 24 | p.add_argument("-p", "--plot", help="show plots", action="store_true") 25 | p.add_argument( 26 | "-a", "--alpha", help="regularization parameter", type=float, default=0.001 27 | ) 28 | p.add_argument("-N", help="number of iterations", type=int, default=8) 29 | p = p.parse_args() 30 | 31 | U, V = horn_schunck(p.stem, p.pat, alpha=p.alpha, Niter=p.N, verbose=p.plot) 32 | 33 | show() 34 | 35 | 36 | def horn_schunck(stem: Path, pat: str, alpha: float, Niter: int, verbose: bool): 37 | flist = getimgfiles(stem, pat) 38 | 39 | for i in range(len(flist) - 1): 40 | fn1 = flist[i] 41 | im1 = imageio.imread(fn1, as_gray=True) 42 | 43 | # Iold = gaussian_filter(Iold,FILTER) 44 | 45 | fn2 = flist[i + 1] 46 | im2 = imageio.imread(fn2, as_gray=True) 47 | # Inew = gaussian_filter(Inew,FILTER) 48 | 49 | U, V = HornSchunck(im1, im2, alpha=1.0, Niter=100) 50 | compareGraphs(U, V, im2, fn=fn2.name) 51 | 52 | return U, V 53 | 54 | 55 | if __name__ == "__main__": 56 | main() 57 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | -------------------------------------------------------------------------------- /LucasKanade.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | This function isn't working yet. 4 | """ 5 | import imageio 6 | from scipy.ndimage.filters import gaussian_filter 7 | from argparse import ArgumentParser 8 | from pyoptflow import LucasKanade, getPOI, gaussianWeight 9 | from pyoptflow import getimgfiles 10 | from pyoptflow.plots import compareGraphsLK 11 | 12 | 13 | def main(): 14 | p = ArgumentParser(description="Pure Python Lucas Kanade Optical Flow") 15 | p.add_argument("stem", help="path/stem of files to analyze") 16 | p.add_argument("pat", help="pattern glob") 17 | p = p.parse_args() 18 | 19 | lucas_kanade(p.stem, p.pat) 20 | 21 | 22 | def lucas_kanade(stem, pat: str, kernel: int = 5, Nfilter: int = 7): 23 | flist = getimgfiles(stem, pat) 24 | 25 | # %% priming read 26 | im1 = imageio.imread(flist[0], as_gray=True) 27 | 28 | # %% evaluate the first frame's POI 29 | X = im1.shape[1] // 16 30 | Y = im1.shape[0] // 16 31 | poi = getPOI(X, Y, kernel) 32 | # % get the weights 33 | W = gaussianWeight(kernel) 34 | # %% loop over all images in directory 35 | for i in range(1, len(flist)): 36 | im2 = imageio.imread(flist[i], as_gray=True) 37 | 38 | im2 = gaussian_filter(im2, Nfilter) 39 | 40 | V = LucasKanade(im1, im2, kernel, poi, W) 41 | 42 | compareGraphsLK(im1, im2, poi, V) 43 | 44 | im1 = im2.copy() 45 | 46 | 47 | if __name__ == "__main__": 48 | main() 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Optical Flow: Horn-Schunck 2 | 3 | [![image](https://zenodo.org/badge/DOI/10.5281/zenodo.1043971.svg)](https://doi.org/10.5281/zenodo.1043971) 4 | [![Actions Status](https://github.com/scivision/pyoptflow/workflows/ci/badge.svg)](https://github.com/scivision/pyoptflow/actions) 5 | [![PyPi Download stats](http://pepy.tech/badge/pyoptflow)](http://pepy.tech/project/pyoptflow) 6 | 7 | Python implementation of optical flow estimation using only the Scipy stack for: 8 | 9 | * Horn Schunck 10 | 11 | Lucas-Kanade is also possible in the future, let us know if you're interested in Lucas Kanade. 12 | 13 | ## Install 14 | 15 | ```sh 16 | python -m pip install -e . 17 | ``` 18 | 19 | optionally, to run self-tests: 20 | 21 | ```sh 22 | python -m pip install -e .[tests] 23 | 24 | pytest -v 25 | ``` 26 | 27 | ## Examples 28 | 29 | The program scripts expect `directory` `glob pattern` 30 | 31 | [imageio](https://imageio.github.io/) loads a wide varity of images *and* video. 32 | 33 | **Box:** 34 | 35 | ```sh 36 | python HornSchunck.py src/pyoptflow/data/tests/box box*.bmp 37 | ``` 38 | 39 | **Office**: all time steps: 40 | 41 | ```sh 42 | python HornSchunck.py src/pyoptflow/data/tests/office office*.bmp 43 | ``` 44 | 45 | or just the first 2 time steps: 46 | 47 | ```sh 48 | python HornSchunck.py src/pyoptflow/data/tests/office office.[0-2].bmp 49 | ``` 50 | 51 | **Rubic**: 52 | 53 | ```sh 54 | python HornSchunck.py src/pyoptflow/data/tests/rubic rubic*.bmp 55 | ``` 56 | 57 | **Sphere** 58 | 59 | ```sh 60 | python HornSchunck.py src/pyoptflow/data/tests/sphere sphere*.bmp 61 | ``` 62 | 63 | Compare: Matlab Computer Vision toolbox: in [matlab](./matlab), 64 | similar method in Octave and a comparison plot using Matlab Computer Vision toolbox. 65 | 66 | Reference:[Inspiration](https://github.com/ablarry91/Optical-Flow-LucasKanade-HornSchunck/) 67 | -------------------------------------------------------------------------------- /matlab/HornSchunck.m: -------------------------------------------------------------------------------- 1 | function [u, v] = HornSchunck(im1, im2, alpha, ite, uInitial, vInitial, displayFlow) 2 | % Horn-Schunck optical flow method 3 | % Horn, B.K.P., and Schunck, B.G., Determining Optical Flow, AI(17), No. 4 | % 1-3, August 1981, pp. 185-203 http://dspace.mit.edu/handle/1721.1/6337 5 | % 6 | % Usage: 7 | % [u, v] = HS(im1, im2, alpha, ite, uInitial, vInitial, displayFlow) 8 | % For an example, run this file from the menu Debug->Run or press (F5) 9 | % 10 | % -im1,im2 : two subsequent frames or images. 11 | % -alpha : a parameter that reflects the influence of the smoothness term. 12 | % -ite : number of iterations. 13 | % -uInitial, vInitial : initial values for the flow. If available, the 14 | % flow would converge faster and hence would need less iterations ; default is zero. 15 | % -displayFlow : 1 for display, 0 for no display ; default is 1. 16 | % -displayImg : specify the image on which the flow would appear ( use an 17 | % empty matrix "[]" for no image. ) 18 | % 19 | % Author: Mohd Kharbat at Cranfield Defence and Security 20 | % mkharbat(at)ieee(dot)org , http://mohd.kharbat.com 21 | % Published under a Creative Commons Attribution-Non-Commercial-Share Alike 22 | % 3.0 Unported Licence http://creativecommons.org/licenses/by-nc-sa/3.0/ 23 | % 24 | % October 2008 25 | % Rev: Jan 2009 26 | 27 | %% Default parameters 28 | arguments 29 | im1 (:,:) {mustBeNumeric} 30 | im2 (:,:) {mustBeNumeric} 31 | alpha (1,1) {mustBeNumeric} = 1 32 | ite (1,1) {mustBePositive,mustBeInteger} = 100 33 | uInitial (:,:) {mustBeNumeric} = zeros(size(im1(:,:,1))) 34 | vInitial (:,:) {mustBeNumeric} = zeros(size(im2(:,:,1))) 35 | displayFlow (1,1) = false 36 | end 37 | 38 | im1=smoothImg(im1,1); 39 | im2=smoothImg(im2,1); 40 | 41 | tic; 42 | 43 | %% 44 | % Set initial value for the flow vectors 45 | u = uInitial; 46 | v = vInitial; 47 | 48 | % Estimate spatiotemporal derivatives 49 | [fx, fy, ft] = computeDerivatives(im1, im2); 50 | 51 | % Averaging kernel 52 | kernel_1=[1/12 1/6 1/12;1/6 0 1/6;1/12 1/6 1/12]; 53 | 54 | % Iterations 55 | for i=1:ite 56 | % Compute local averages of the flow vectors 57 | uAvg=conv2(u,kernel_1,'same'); 58 | vAvg=conv2(v,kernel_1,'same'); 59 | % Compute flow vectors constrained by its local average and the optical flow constraints 60 | der = ( fx.*uAvg + fy.*vAvg + ft ) ./ ( alpha^2 + fx.^2 + fy.^2); 61 | u = uAvg - fx .* der; 62 | v = vAvg - fy .* der; 63 | end 64 | 65 | u(isnan(u))=0; 66 | v(isnan(v))=0; 67 | 68 | %% Plotting 69 | if displayFlow 70 | plotFlow(u, v, im1, 5, 5); 71 | end 72 | -------------------------------------------------------------------------------- /matlab/RunHornSchunck.m: -------------------------------------------------------------------------------- 1 | 2 | name = 'sphere'; 3 | Niter = 100; 4 | 5 | datadir = fullfile("../src/pyoptflow/tests/data/",name); 6 | 7 | fn1 = fullfile(datadir, name + ".0.bmp"); 8 | fn2 = fullfile(datadir, name + ".1.bmp"); 9 | 10 | 11 | im1 = imread(fn1); 12 | im2 = imread(fn2); 13 | 14 | if ndims(im1) == 3 15 | im1 = im2gray(im1); 16 | im2 = im2gray(im2); 17 | elseif islogical(im1) 18 | im1 = uint8(im1); 19 | im2 = uint8(im2); 20 | end 21 | 22 | fg = figure(1); 23 | clf(fg) 24 | %t = tiledlayout(fg, 1, 3); 25 | subplot(1,3,1) 26 | axis("image") 27 | [Umat, Vmat] = HornSchunck(im1, im2, 1, Niter); 28 | plotFlow(Umat, Vmat) 29 | title("Matlab plain") 30 | %% compare to Matlab CV Toolbox 31 | subplot(1,3,2) 32 | title("Matlab Computer Vision toolbox") 33 | 34 | if ~isempty(ver('vision')) 35 | %nexttile(t) 36 | 37 | axis("image") 38 | opticFlow = opticalFlowHS('Smoothness', 0.001, 'MaxIteration', Niter); 39 | estimateFlow(opticFlow, im1); 40 | flow = estimateFlow(opticFlow, im2); 41 | 42 | %plot(flow,'DecimationFactor',[5 5],'ScaleFactor',25) 43 | plotFlow(flow.Vx, flow.Vy) 44 | 45 | end 46 | %% compare to pure Python 47 | if ispc 48 | % Numpy needs mkl*.dll on PATH on Windows 49 | pe = pyenv; 50 | old_path = getenv("PATH"); 51 | new_path = fullfile(pe.Home, "Library/bin"); 52 | if ~contains(old_path, new_path, 'IgnoreCase', true) 53 | setenv('PATH', fullfile(pe.Home, "Library/bin") + pathsep + getenv('PATH')) 54 | end 55 | end 56 | 57 | UV = py.pyoptflow.HornSchunck(py.numpy.asarray(im1), py.numpy.asarray(im2), ... 58 | pyargs("Niter", uint16(Niter), 'alpha', 1)); 59 | U = double(UV{1}); 60 | V = double(UV{2}); 61 | 62 | %nexttile(t) 63 | subplot(1,3,3) 64 | axis("image") 65 | plotFlow(U,V) 66 | title("PyOptFlow") 67 | -------------------------------------------------------------------------------- /matlab/computeDerivatives.m: -------------------------------------------------------------------------------- 1 | function [fx, fy, ft] = computeDerivatives(im1, im2) 2 | 3 | % derivatives as in Barron 4 | % fx= conv2(im1,(1/12)*[-1 8 0 -8 1],'same'); 5 | % fy= conv2(im1,(1/12)*[-1 8 0 -8 1]','same'); 6 | % ft = conv2(im1, 0.25*ones(2),'same') + conv2(im2, -0.25*ones(2),'same'); 7 | % fx=-fx;fy=-fy; 8 | 9 | % An alternative way to compute the spatiotemporal derivatives is to use simple finite difference masks. 10 | % fx = conv2(im1,[1 -1]); 11 | % fy = conv2(im1,[1; -1]); 12 | % ft= im2-im1; 13 | 14 | arguments 15 | im1 (:,:) {mustBeNumeric} 16 | im2 (:,:) {mustBeNumeric} 17 | end 18 | 19 | Xkern = 0.25* [-1 1; 20 | -1 1]; 21 | 22 | Ykern = 0.25*[-1 -1; 23 | 1 1]; 24 | 25 | Tkern = 0.25*ones(2); 26 | 27 | % Horn-Schunck original method 28 | fx = conv2(im1,Xkern,'same') + conv2(im2, Xkern,'same'); 29 | fy = conv2(im1, Ykern, 'same') + conv2(im2, Ykern, 'same'); 30 | ft = conv2(im1, Tkern,'same') + conv2(im2, -Tkern, 'same'); 31 | 32 | end 33 | -------------------------------------------------------------------------------- /matlab/gaussFilter.m: -------------------------------------------------------------------------------- 1 | function G=gaussFilter(segma,kSize) 2 | % Creates a 1-D Gaussian kernel of a standard deviation 'segma' and a size 3 | % of 'kSize'. 4 | % 5 | % In theory, the Gaussian distribution is non-zero everywhere. In practice, 6 | % it's effectively zero at places further away from about three standard 7 | % deviations. Hence the reason why the kernel is suggested to be truncated 8 | % at that point. 9 | % 10 | % The 2D Gaussian filter is a complete circular symmetric operator. It can be 11 | % seperated into x and y components. The 2D convolution can be performed by 12 | % first convolving with 1D Gaussian in the x direction and the same in the 13 | % y direction. 14 | % 15 | % Author: Mohd Kharbat at Cranfield Defence and Security 16 | % mkharbat(at)ieee(dot)org , http://mohd.kharbat.com 17 | % Published under a Creative Commons Attribution-Non-Commercial-Share Alike 18 | % 3.0 Unported Licence http://creativecommons.org/licenses/by-nc-sa/3.0/ 19 | % 20 | % October 2008 21 | 22 | arguments 23 | segma (1,1) = 1 24 | kSize (1,1) = 2*segma*3 25 | end 26 | 27 | x=-(kSize/2):(1+1/kSize):(kSize/2); 28 | G=(1/(sqrt(2*pi)*segma)) * exp (-(x.^2)/(2*segma^2)); 29 | end 30 | -------------------------------------------------------------------------------- /matlab/plotFlow.m: -------------------------------------------------------------------------------- 1 | function plotFlow(u, v, imgOriginal, rSize, scale) 2 | % Creates a quiver plot that displays the optical flow vectors on the 3 | % original first frame (if provided). See the MATLAB Function Reference for 4 | % "quiver" for more info. 5 | % 6 | % Usage: 7 | % plotFlow(u, v, imgOriginal, rSize, scale) 8 | % 9 | % u and v are the horizontal and vertical optical flow vectors, 10 | % respectively. imgOriginal, if supplied, is the first frame on which the 11 | % flow vectors would be plotted. use an empty matrix '[]' for no image. 12 | % rSize is the size of the region in which one vector is visible. scale 13 | % over-rules the auto scaling. 14 | % 15 | % Author: Mohd Kharbat at Cranfield Defence and Security 16 | % mkharbat(at)ieee(dot)org , http://mohd.kharbat.com 17 | % Published under a Creative Commons Attribution-Non-Commercial-Share Alike 18 | % 3.0 Unported Licence http://creativecommons.org/licenses/by-nc-sa/3.0/ 19 | % 20 | % October 2008 21 | % Rev: Jan 2009 22 | 23 | arguments 24 | u (:,:) {mustBeNumeric} 25 | v (:,:) {mustBeNumeric} 26 | imgOriginal (:,:) {mustBeNumeric} = [] 27 | rSize (1,1) {mustBeInteger,mustBePositive} = 5 28 | scale (1,1) {mustBeInteger,mustBePositive} = 3 29 | end 30 | 31 | if ~isempty(imgOriginal) 32 | imshow(imgOriginal); 33 | hold on; 34 | end 35 | 36 | % Enhance the quiver plot visually by showing one vector per region 37 | for i=1:size(u,1) 38 | for j=1:size(u,2) 39 | if floor(i/rSize)~=i/rSize || floor(j/rSize)~=j/rSize 40 | u(i,j)=0; 41 | v(i,j)=0; 42 | end 43 | end 44 | end 45 | quiver(u, v, scale)%, 'color', 'b', 'linewidth', 2); 46 | %set(gca,'YDir','reverse'); 47 | -------------------------------------------------------------------------------- /matlab/smoothImg.m: -------------------------------------------------------------------------------- 1 | function smoothedImg=smoothImg(img,segma) 2 | % Convolving an image with a Gaussian kernel. 3 | 4 | % The degree of smoothing is determined by the Gaussian's standard 5 | % deviation 'segma'. The Gaussian outputs a 'weighted average' of each 6 | % pixel's neighborhood, with the average weighted more towards the value of 7 | % the central pixels. The larger the standard deviation, the less weight 8 | % towards the central pixels and more weight towards the pixels away, hence 9 | % heavier blurring effect and less details in the output image. 10 | % 11 | % Author: Mohd Kharbat at Cranfield Defence and Security 12 | % mkharbat(at)ieee(dot)org , http://mohd.kharbat.com 13 | % Published under a Creative Commons Attribution-Non-Commercial-Share Alike 14 | % 3.0 Unported Licence http://creativecommons.org/licenses/by-nc-sa/3.0/ 15 | % 16 | % October 2008 17 | 18 | arguments 19 | img (:,:) {mustBeNumeric} 20 | segma (1,1) = 1 21 | end 22 | 23 | G=gaussFilter(segma); 24 | smoothedImg=conv2(img,G,'same'); 25 | smoothedImg=conv2(smoothedImg,G','same'); 26 | end 27 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = pyoptflow 3 | version = 1.4.0 4 | author = Michael Hirsch, Ph.D. 5 | author_email = scivision@users.noreply.github.com 6 | url = https://github.com/scivision/pyoptflow 7 | description = Pure Python optical flow: Horn-Schunck 8 | keywords = 9 | optical flow 10 | Horn-Schunck 11 | Lucas-Kanade 12 | classifiers = 13 | Development Status :: 5 - Production/Stable 14 | Environment :: Console 15 | Intended Audience :: Science/Research 16 | Operating System :: OS Independent 17 | Programming Language :: Python :: 3 18 | Topic :: Scientific/Engineering 19 | license_files = 20 | LICENSE.txt 21 | long_description = file: README.md 22 | long_description_content_type = text/markdown 23 | 24 | [options] 25 | python_requires = >= 3.7 26 | packages = find: 27 | install_requires = 28 | imageio 29 | scipy 30 | numpy 31 | include_package_data = True 32 | zip_safe = False 33 | package_dir= 34 | =src 35 | 36 | [options.packages.find] 37 | where=src 38 | 39 | [options.extras_require] 40 | tests = 41 | pytest >= 3.6 42 | lint = 43 | flake8 44 | mypy 45 | plot = 46 | matplotlib 47 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup 3 | 4 | setup() 5 | -------------------------------------------------------------------------------- /src/pyoptflow/__init__.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from .hornschunck import HornSchunck 4 | from .lucaskanade import LucasKanade, getPOI, gaussianWeight 5 | 6 | 7 | def getimgfiles(stem: Path, pat: str) -> list: 8 | 9 | stem = Path(stem).expanduser() 10 | 11 | print("searching", stem / pat) 12 | 13 | flist = sorted([f for f in stem.glob(pat) if f.is_file()]) 14 | 15 | if not flist: 16 | raise FileNotFoundError(f"no files found under {stem} using {pat}") 17 | 18 | return flist 19 | -------------------------------------------------------------------------------- /src/pyoptflow/hornschunck.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import numpy as np 4 | from scipy.signal import convolve2d 5 | 6 | HSKERN = np.array( 7 | [[1 / 12, 1 / 6, 1 / 12], [1 / 6, 0, 1 / 6], [1 / 12, 1 / 6, 1 / 12]], float 8 | ) 9 | 10 | kernelX = np.array([[-1, 1], [-1, 1]]) * 0.25 # kernel for computing d/dx 11 | 12 | kernelY = np.array([[-1, -1], [1, 1]]) * 0.25 # kernel for computing d/dy 13 | 14 | kernelT = np.ones((2, 2)) * 0.25 15 | 16 | 17 | def HornSchunck( 18 | im1: np.ndarray, 19 | im2: np.ndarray, 20 | *, 21 | alpha: float = 0.001, 22 | Niter: int = 8, 23 | verbose: bool = False 24 | ) -> tuple[np.ndarray, np.ndarray]: 25 | """ 26 | 27 | Parameters 28 | ---------- 29 | 30 | im1: numpy.ndarray 31 | image at t=0 32 | im2: numpy.ndarray 33 | image at t=1 34 | alpha: float 35 | regularization constant 36 | Niter: int 37 | number of iteration 38 | """ 39 | im1 = im1.astype(np.float32) 40 | im2 = im2.astype(np.float32) 41 | 42 | # set up initial velocities 43 | uInitial = np.zeros([im1.shape[0], im1.shape[1]], dtype=np.float32) 44 | vInitial = np.zeros([im1.shape[0], im1.shape[1]], dtype=np.float32) 45 | 46 | # Set initial value for the flow vectors 47 | U = uInitial 48 | V = vInitial 49 | 50 | # Estimate derivatives 51 | [fx, fy, ft] = computeDerivatives(im1, im2) 52 | 53 | if verbose: 54 | from .plots import plotderiv 55 | 56 | plotderiv(fx, fy, ft) 57 | 58 | # Iteration to reduce error 59 | for _ in range(Niter): 60 | # %% Compute local averages of the flow vectors 61 | uAvg = convolve2d(U, HSKERN, "same") 62 | vAvg = convolve2d(V, HSKERN, "same") 63 | # %% common part of update step 64 | der = (fx * uAvg + fy * vAvg + ft) / (alpha ** 2 + fx ** 2 + fy ** 2) 65 | # %% iterative step 66 | U = uAvg - fx * der 67 | V = vAvg - fy * der 68 | 69 | return U, V 70 | 71 | 72 | def computeDerivatives( 73 | im1: np.ndarray, im2: np.ndarray 74 | ) -> tuple[np.ndarray, np.ndarray, np.ndarray]: 75 | 76 | fx = convolve2d(im1, kernelX, "same") + convolve2d(im2, kernelX, "same") 77 | fy = convolve2d(im1, kernelY, "same") + convolve2d(im2, kernelY, "same") 78 | 79 | # ft = im2 - im1 80 | ft = convolve2d(im1, kernelT, "same") + convolve2d(im2, -kernelT, "same") 81 | 82 | return fx, fy, ft 83 | -------------------------------------------------------------------------------- /src/pyoptflow/lucaskanade.py: -------------------------------------------------------------------------------- 1 | """ 2 | This function isn't working yet. 3 | """ 4 | import numpy as np 5 | 6 | 7 | def LucasKanade(im1, im2, kernel, poi, W): 8 | # %% evaluate every POI 9 | V = np.zeros((poi.shape[0], 2)) 10 | for i in range(len(poi)): 11 | A = buildA(im2, poi[i][0][1], poi[i][0][0], kernel) 12 | B = buildB(im2, im1, poi[i][0][1], poi[i][0][0], kernel) 13 | # %% solve for v 14 | Vpt = np.linalg.inv(A.T @ W ** 2 @ A) @ A.T @ W ** 2 @ B 15 | V[i, 0] = Vpt[0] 16 | V[i, 1] = Vpt[1] 17 | 18 | return V 19 | 20 | 21 | def buildA(img, centerX, centerY, kernelSize): 22 | # build a kernel containing pixel intensities 23 | mean = kernelSize // 2 24 | count = 0 25 | home = img[centerY, centerX] # storing the intensity of the center pixel 26 | A = np.zeros([kernelSize ** 2, 2]) 27 | for j in range(-mean, mean + 1): # advance the y 28 | for i in range(-mean, mean + 1): # advance the x 29 | if i == 0: 30 | Ax = 0 31 | else: 32 | Ax = (home - img[centerY + j, centerX + i]) / i 33 | if j == 0: 34 | Ay = 0 35 | else: 36 | Ay = (home - img[centerY + j, centerX + i]) / j 37 | # write to A 38 | A[count] = np.array([Ay, Ax]) 39 | count += 1 40 | 41 | return A 42 | 43 | 44 | def buildB(imgNew, imgOld, centerX, centerY, kernelSize): 45 | mean = kernelSize // 2 46 | count = 0 47 | # home = imgNew[centerY, centerX] 48 | 49 | B = np.zeros([kernelSize ** 2]) 50 | for j in range(-mean, mean + 1): 51 | for i in range(-mean, mean + 1): 52 | Bt = imgNew[centerY + j, centerX + i] - imgOld[centerY + j, centerX + i] 53 | B[count] = Bt 54 | count += 1 55 | # print np.linalg.norm(B) 56 | 57 | return B 58 | 59 | 60 | def getPOI(xSize, ySize, kernelSize): 61 | mean = kernelSize // 2 62 | xPos = mean 63 | yPos = mean 64 | xStep = (xSize - mean) // kernelSize 65 | yStep = (ySize - mean) // kernelSize 66 | length = xStep * yStep 67 | 68 | poi = np.zeros((length, 1, 2), int) 69 | count = 0 70 | for _ in range(yStep): 71 | for _ in range(xStep): 72 | poi[count, 0, 1] = xPos 73 | poi[count, 0, 0] = yPos 74 | xPos += kernelSize 75 | count += 1 76 | xPos = mean 77 | yPos += kernelSize 78 | 79 | return poi 80 | 81 | 82 | def gaussianWeight(kernelSize: int, even: bool = False) -> np.ndarray: 83 | if even: 84 | weight = np.ones([kernelSize, kernelSize]) 85 | weight = weight.reshape((1, kernelSize ** 2)) 86 | weight = np.array(weight)[0] 87 | weight = np.diag(weight) 88 | return weight 89 | 90 | SIGMA = 1 # the standard deviation of your normal curve 91 | CORRELATION = 0 # see wiki for multivariate normal distributions 92 | weight = np.zeros([kernelSize, kernelSize]) 93 | cpt = kernelSize % 2 + kernelSize // 2 # gets the center point 94 | for i in range(len(weight)): 95 | for j in range(len(weight)): # noqa: B007 96 | ptx = i + 1 97 | pty = j + 1 98 | weight[i, j] = ( 99 | 1 100 | / (2 * np.pi * SIGMA ** 2) 101 | / (1 - CORRELATION ** 2) ** 0.5 102 | * np.exp( 103 | -1 104 | / (2 * (1 - CORRELATION ** 2)) 105 | * ((ptx - cpt) ** 2 + (pty - cpt) ** 2) 106 | / (SIGMA ** 2) 107 | ) 108 | ) 109 | # weight[i,j] = 1/SIGMA/(2*np.pi)**.5*np.exp(-(pt-cpt)**2/(2*SIGMA**2)) 110 | weight = weight.reshape((1, kernelSize ** 2)) 111 | weight = np.array(weight)[0] # convert to a 1D array 112 | weight = np.diag(weight) # convert to n**2xn**2 diagonal matrix 113 | 114 | return weight 115 | # return np.diag(weight) 116 | -------------------------------------------------------------------------------- /src/pyoptflow/plots.py: -------------------------------------------------------------------------------- 1 | from matplotlib.pyplot import figure, draw, pause, gca 2 | from pathlib import Path 3 | 4 | 5 | def plotderiv(fx, fy, ft): 6 | 7 | fg = figure(figsize=(18, 5)) 8 | ax = fg.subplots(1, 3) 9 | 10 | for f, a, t in zip((fx, fy, ft), ax, ("$f_x$", "$f_y$", "$f_t$")): 11 | h = a.imshow(f, cmap="bwr") 12 | a.set_title(t) 13 | fg.colorbar(h, ax=a) 14 | 15 | 16 | def compareGraphs(u, v, Inew, scale: int = 3, quivstep: int = 5, fn: Path = None): 17 | """ 18 | makes quiver 19 | """ 20 | 21 | ax = figure().gca() 22 | ax.imshow(Inew, cmap="gray", origin="lower") 23 | # plt.scatter(POI[:,0,1],POI[:,0,0]) 24 | for i in range(0, u.shape[0], quivstep): 25 | for j in range(0, v.shape[1], quivstep): 26 | ax.arrow( 27 | j, 28 | i, 29 | v[i, j] * scale, 30 | u[i, j] * scale, 31 | color="red", 32 | head_width=0.5, 33 | head_length=1, 34 | ) 35 | 36 | # plt.arrow(POI[:,0,0],POI[:,0,1],0,-5) 37 | if fn: 38 | ax.set_title(fn) 39 | 40 | draw() 41 | pause(0.01) 42 | 43 | 44 | def compareGraphsLK(imgOld, imgNew, POI, V, scale=1.0, fn: Path = None): 45 | 46 | ax = gca() 47 | ax.imshow(imgNew, cmap="gray", origin="lower") 48 | # plt.scatter(POI[:,0,1],POI[:,0,0]) 49 | for i in range(len(POI)): 50 | ax.arrow( 51 | POI[i, 0, 1], POI[i, 0, 0], V[i, 1] * scale, V[i, 0] * scale, color="red" 52 | ) 53 | # plt.arrow(POI[:,0,0],POI[:,0,1],0,-5) 54 | if fn: 55 | ax.set_title(fn) 56 | 57 | draw() 58 | pause(0.5) 59 | -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/box/box.0.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/box/box.0.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/box/box.1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/box/box.1.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.0.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.0.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.1.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.10.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.10.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.11.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.11.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.12.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.12.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.13.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.13.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.14.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.14.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.15.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.15.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.16.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.16.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.17.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.17.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.18.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.18.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.19.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.19.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.2.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.3.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.4.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.4.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.5.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.5.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.6.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.6.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.7.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.7.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.8.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.8.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/office/office.9.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/office/office.9.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.-1 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.-1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.-1.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.0 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.0.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.0.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.1 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.1.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.10: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.10 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.10.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.10.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.11: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.11 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.11.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.11.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.12 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.12.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.12.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.13: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.13 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.13.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.13.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.14: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.14 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.14.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.14.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.15: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.15 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.15.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.15.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.16: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.16 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.16.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.16.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.17: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.17 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.17.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.17.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.18: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.18 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.18.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.18.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.19: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.19 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.19.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.19.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.2 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.2.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.3 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.3.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.4 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.4.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.4.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.5 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.5.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.5.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.6 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.6.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.6.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.7 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.7.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.7.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.8 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.8.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.8.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.9 -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/rubic/rubic.9.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/rubic/rubic.9.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.0.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.0.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.1.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.10.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.10.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.11.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.11.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.12.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.12.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.13.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.13.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.14.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.14.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.15.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.15.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.16.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.16.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.17.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.17.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.18.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.18.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.19.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.19.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.2.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.3.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.4.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.4.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.5.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.5.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.6.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.6.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.7.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.7.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.8.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.8.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/data/sphere/sphere.9.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pyoptflow/4485da929c5567470d821c3f2a23e3bccc7f768e/src/pyoptflow/tests/data/sphere/sphere.9.bmp -------------------------------------------------------------------------------- /src/pyoptflow/tests/test_all.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import numpy as np 3 | from pytest import approx 4 | import pyoptflow as pof 5 | 6 | RDIR = Path(__file__).parent 7 | 8 | FILTER = 7 9 | 10 | IM1 = np.ones((16, 16)) 11 | IM2 = IM1.copy() 12 | 13 | IM1[7, 7] = 0 14 | 15 | 16 | def test_hornschunck(): 17 | U, V = pof.HornSchunck(IM1, IM2, alpha=1.0, Niter=100) 18 | 19 | assert U[7, 7] == approx(0.05951344974325876) 20 | 21 | 22 | def test_io(): 23 | flist = pof.getimgfiles(RDIR / "data/box", "box*") 24 | assert len(flist) == 2 25 | --------------------------------------------------------------------------------