├── .bumpversion.cfg ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── python-publish.yml ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── MANIFEST.in ├── README.md ├── docs ├── .nojekyll ├── .vscode │ └── settings.json ├── Makefile ├── _images │ ├── abstract.png │ ├── aoi_pn.png │ ├── border.png │ ├── centroid.png │ ├── ch3_io_res_demo_shapefile_13_0.png │ ├── ch3_io_res_demo_shapefile_9_1.png │ ├── ch4_adv_custom_index_15_0.png │ ├── ch4_adv_custom_index_20_1.png │ ├── ch4_adv_custom_index_6_1.png │ ├── ch4_adv_seasons_11_1.png │ ├── ch4_adv_seasons_13_1.png │ ├── ch4_adv_seasons_15_0.png │ ├── ch4_adv_seasons_16_1.png │ ├── ch4_adv_seasons_22_0.png │ ├── ck_out.png │ ├── ct_adj.png │ ├── ct_maj.png │ ├── ct_pn.png │ ├── demo_image.png │ ├── demo_map.png │ ├── demo_option.png │ ├── fill.png │ ├── geo.png │ ├── h5.png │ ├── input.png │ ├── inputs.png │ ├── inspect.png │ ├── km_alg.png │ ├── km_dis.png │ ├── km_k.png │ ├── km_misc.png │ ├── km_noise.png │ ├── km_pn.png │ ├── km_shade.png │ ├── label.png │ ├── ld_dnd.gif │ ├── ld_pn.png │ ├── load.png │ ├── npy_plot.png │ ├── results.png │ ├── s1_shp.png │ ├── seg.png │ ├── seg_fix.png │ ├── seg_grid.png │ ├── seg_out.png │ ├── seg_pn.png │ ├── shapefiles.png │ └── unck_out.png ├── _sources │ ├── ch1_started │ │ ├── firsttime.rst.txt │ │ └── installation.rst.txt │ ├── ch2_interface │ │ ├── aoi.rst.txt │ │ ├── centroids.rst.txt │ │ ├── kmeans.rst.txt │ │ ├── load.rst.txt │ │ └── segmentation.rst.txt │ ├── ch3_io │ │ ├── inputs.rst.txt │ │ ├── outputs.rst.txt │ │ └── res │ │ │ └── demo_shapefile.ipynb.txt │ ├── ch4_adv │ │ ├── arbitrary.rst.txt │ │ ├── custom_index.ipynb.txt │ │ ├── dl.rst.txt │ │ ├── qgis.rst.txt │ │ └── seasons.ipynb.txt │ └── index.rst.txt ├── _static │ ├── GRID_banner.png │ ├── _sphinx_javascript_frameworks_compat.js │ ├── aoi_corner.mp4 │ ├── aoi_create.mp4 │ ├── aoi_entire.mp4 │ ├── aoi_new.mp4 │ ├── aoi_rotate.mp4 │ ├── aoi_side.mp4 │ ├── basic.css │ ├── css │ │ ├── badge_only.css │ │ ├── fonts │ │ │ ├── Roboto-Slab-Bold.woff │ │ │ ├── Roboto-Slab-Bold.woff2 │ │ │ ├── Roboto-Slab-Regular.woff │ │ │ ├── Roboto-Slab-Regular.woff2 │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ ├── fontawesome-webfont.woff2 │ │ │ ├── lato-bold-italic.woff │ │ │ ├── lato-bold-italic.woff2 │ │ │ ├── lato-bold.woff │ │ │ ├── lato-bold.woff2 │ │ │ ├── lato-normal-italic.woff │ │ │ ├── lato-normal-italic.woff2 │ │ │ ├── lato-normal.woff │ │ │ └── lato-normal.woff2 │ │ └── theme.css │ ├── ct_min.mp4 │ ├── doctools.js │ ├── documentation_options.js │ ├── file.png │ ├── google_analytics.js │ ├── jquery-3.6.0.js │ ├── jquery.js │ ├── js │ │ ├── badge_only.js │ │ ├── html5shiv-printshiv.min.js │ │ ├── html5shiv.min.js │ │ └── theme.js │ ├── language_data.js │ ├── minus.png │ ├── plus.png │ ├── poi_bin.mp4 │ ├── poi_noise.mp4 │ ├── poi_refine.mp4 │ ├── poi_shad.mp4 │ ├── pygments.css │ ├── searchtools.js │ ├── seg_adj.mp4 │ ├── underscore-1.13.1.js │ └── underscore.js ├── ch1_started │ ├── firsttime.html │ └── installation.html ├── ch2_interface │ ├── aoi.html │ ├── centroids.html │ ├── kmeans.html │ ├── load.html │ └── segmentation.html ├── ch3_io │ ├── inputs.html │ ├── outputs.html │ └── res │ │ ├── demo_shapefile.html │ │ └── demo_shapefile.ipynb ├── ch4_adv │ ├── arbitrary.html │ ├── custom_index.html │ ├── custom_index.ipynb │ ├── dl.html │ ├── qgis.html │ ├── seasons.html │ └── seasons.ipynb ├── demo │ ├── features.rst │ └── main.rst ├── genindex.html ├── grid.bib ├── index.html ├── make.bat ├── objects.inv ├── search.html └── searchindex.js ├── grid ├── __init__.py ├── __init__.pyc ├── __main__.py ├── archive │ ├── 0611_shapefile.py │ ├── 0615_zz_image.r │ ├── CPU_Agent.py │ ├── CPU_Field copy.py │ ├── GPU_Anchor copy.py │ ├── GPU_Output copy.py │ ├── Misc.py │ ├── archive.py │ ├── grid2.py │ ├── test.py │ ├── test.r │ ├── testAnchor.py │ ├── testFindAngel.py │ ├── testRecoverSize.py │ ├── test_shapefile.py │ └── thread.py ├── benchmark.py ├── benchmark.r ├── demo │ └── seg_img.jpg ├── dir.py ├── gagent.py ├── gimage.py ├── gimage.pyc ├── gmap.py ├── grid.py ├── grid.pyc ├── gridGUI.py ├── gtest.py ├── gui │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-310.pyc │ │ ├── __init__.cpython-36.pyc │ │ ├── __init__.cpython-37.pyc │ │ ├── __init__.cpython-39.pyc │ │ ├── anchor.cpython-310.pyc │ │ ├── anchor.cpython-36.pyc │ │ ├── anchor.cpython-37.pyc │ │ ├── anchor.cpython-39.pyc │ │ ├── cropper.cpython-310.pyc │ │ ├── cropper.cpython-36.pyc │ │ ├── cropper.cpython-37.pyc │ │ ├── cropper.cpython-39.pyc │ │ ├── customQt.cpython-310.pyc │ │ ├── customQt.cpython-36.pyc │ │ ├── customQt.cpython-37.pyc │ │ ├── customQt.cpython-39.pyc │ │ ├── inputer.cpython-310.pyc │ │ ├── inputer.cpython-36.pyc │ │ ├── inputer.cpython-37.pyc │ │ ├── inputer.cpython-39.pyc │ │ ├── kmeaner.cpython-310.pyc │ │ ├── kmeaner.cpython-36.pyc │ │ ├── kmeaner.cpython-37.pyc │ │ ├── kmeaner.cpython-39.pyc │ │ ├── outputer.cpython-310.pyc │ │ ├── outputer.cpython-36.pyc │ │ ├── outputer.cpython-37.pyc │ │ └── outputer.cpython-39.pyc │ ├── anchor.py │ ├── cropper.py │ ├── customQt.py │ ├── inputer.py │ ├── kmeaner.py │ └── outputer.py ├── guser.py ├── guser.pyc ├── io.py ├── io.pyc ├── lib.py ├── res │ └── rotate.png └── test │ └── test_gui.py ├── requirements.txt ├── res ├── GRID_banner.png ├── GRID_logo2.png └── abstract.png └── setup.py /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 1.3.13 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:setup.py] 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python Package 10 | 11 | on: 12 | push: 13 | tags: 14 | - '*' 15 | 16 | permissions: 17 | contents: read 18 | 19 | jobs: 20 | deploy: 21 | 22 | runs-on: ubuntu-latest 23 | 24 | steps: 25 | - uses: actions/checkout@v3 26 | - name: Set up Python 27 | uses: actions/setup-python@v3 28 | with: 29 | python-version: '3.x' 30 | - name: Install dependencies 31 | run: | 32 | python -m pip install --upgrade pip 33 | pip install build 34 | - name: Build package 35 | run: python -m build 36 | - name: Publish package 37 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 38 | with: 39 | user: __token__ 40 | password: ${{ secrets.PYPI_UNIVERSAL_TOKEN }} 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.out 2 | *.log 3 | *.map 4 | *.idx 5 | *.xlsx 6 | *.roc 7 | *.pVal 8 | *.h5 9 | *.csv 10 | *.x 11 | *.y 12 | *.wt 13 | *.hdr 14 | *.raw 15 | *.pdf 16 | *.tif 17 | *.zip 18 | *.key 19 | *.ppt 20 | *.pptx 21 | *.Rprofile 22 | *.tidy 23 | *.spec 24 | *.dat 25 | *.pyc 26 | **/data/* 27 | docs/source/* 28 | env/* 29 | *.npy 30 | test/* 31 | .DS_Store 32 | build/* 33 | **/__pycache__/* 34 | 35 | publish.sh -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Python Console App", 9 | "type": "python", 10 | "request": "launch", 11 | "stopOnEntry": true, 12 | "program": "${file}", 13 | "externalConsole": true, 14 | "debugOptions": [ 15 | "WaitOnAbnormalExit", 16 | "WaitOnNormalExit" 17 | ], 18 | "env": {}, 19 | "envFile": "${workspaceRoot}/.env", 20 | "console": "integratedTerminal", 21 | "python": "${command:python.interpreterPath}" 22 | // "python": "/usr/local/bin/python3" 23 | // "python": "${workspaceRoot}" 24 | } 25 | ] 26 | } 27 | 28 | // ${workspaceRoot} -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.linting.enabled": true, 3 | "python.linting.pylintEnabled": false, 4 | "python.linting.flake8Enabled": true, 5 | "workbench.colorTheme": "Monokai Pro", 6 | "jupyter.jupyterServerType": "local", 7 | "jupyter.jupyterCommandLineArguments": [] 8 | } -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include grid/demo/seg_img.jpg 2 | include grid/demo/seg_map.csv 3 | include grid/res/rotate.png 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | [![](https://img.shields.io/pypi/pyversions/photo_grid.svg?logo=python&logoColor=white)](https://pypi.org/project/photo-grid/) 4 | [![](https://img.shields.io/pypi/dm/photo_grid.svg?label=pypi%20downloads&logo=python&logoColor=white)](https://pypi.org/project/photo-grid/) 5 | [![](https://img.shields.io/pypi/v/photo_grid.svg?label=pypi%20version&logo=python&logoColor=white)](https://pypi.org/project/photo-grid/) 6 | [![](https://api.codacy.com/project/badge/Grade/626008b19df543ecb33a78e8f82f5e91)](https://app.codacy.com/manual/Poissonfish/photo_grid/dashboard) 7 | [![](https://img.shields.io/github/license/poissonfish/photo_grid)](https://github.com/Poissonfish/GRID/blob/master/LICENSE) 8 | [![](https://img.shields.io/github/languages/code-size/poissonfish/photo_grid)](https://github.com/Poissonfish/GRID/search?l=Python) 9 | 10 | 11 | 12 | ### [Software Page (zzlab.net)](https://zzlab.net/GRID) 13 | 14 | ### [User Manual](https://poissonfish.github.io/GRID/index.html) 15 | 16 | ## Get Started 17 | ### Installation (All users) 18 | ***Highly recommended install GRID in [Conda](https://poissonfish.github.io/GRID/ch1_started/installation.html) environment*** 19 | 20 | ```bash 21 | conda create -n grid python==3.11 22 | conda install gdal 23 | conda install rasterio 24 | python -m pip install photo-grid 25 | ``` 26 | 27 | ### Launch GRID 28 | Just type `GRID` (case-sensitive) in your terminal. 29 | 30 | ```GRID``` 31 | 32 | or 33 | ```python -m grid``` 34 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/.nojekyll -------------------------------------------------------------------------------- /docs/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "restructuredtext.confPath": "${workspaceFolder}/source" 3 | } -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | PDF = GRID.pdf 11 | 12 | # Put it first so that "make" without argument is like "make help". 13 | help: 14 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 15 | 16 | .PHONY: help Makefile 17 | 18 | # Catch-all target: route all unknown targets to Sphinx using the new 19 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 20 | %: Makefile 21 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 22 | -------------------------------------------------------------------------------- /docs/_images/abstract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/abstract.png -------------------------------------------------------------------------------- /docs/_images/aoi_pn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/aoi_pn.png -------------------------------------------------------------------------------- /docs/_images/border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/border.png -------------------------------------------------------------------------------- /docs/_images/centroid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/centroid.png -------------------------------------------------------------------------------- /docs/_images/ch3_io_res_demo_shapefile_13_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ch3_io_res_demo_shapefile_13_0.png -------------------------------------------------------------------------------- /docs/_images/ch3_io_res_demo_shapefile_9_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ch3_io_res_demo_shapefile_9_1.png -------------------------------------------------------------------------------- /docs/_images/ch4_adv_custom_index_15_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ch4_adv_custom_index_15_0.png -------------------------------------------------------------------------------- /docs/_images/ch4_adv_custom_index_20_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ch4_adv_custom_index_20_1.png -------------------------------------------------------------------------------- /docs/_images/ch4_adv_custom_index_6_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ch4_adv_custom_index_6_1.png -------------------------------------------------------------------------------- /docs/_images/ch4_adv_seasons_11_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ch4_adv_seasons_11_1.png -------------------------------------------------------------------------------- /docs/_images/ch4_adv_seasons_13_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ch4_adv_seasons_13_1.png -------------------------------------------------------------------------------- /docs/_images/ch4_adv_seasons_15_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ch4_adv_seasons_15_0.png -------------------------------------------------------------------------------- /docs/_images/ch4_adv_seasons_16_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ch4_adv_seasons_16_1.png -------------------------------------------------------------------------------- /docs/_images/ch4_adv_seasons_22_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ch4_adv_seasons_22_0.png -------------------------------------------------------------------------------- /docs/_images/ck_out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ck_out.png -------------------------------------------------------------------------------- /docs/_images/ct_adj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ct_adj.png -------------------------------------------------------------------------------- /docs/_images/ct_maj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ct_maj.png -------------------------------------------------------------------------------- /docs/_images/ct_pn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ct_pn.png -------------------------------------------------------------------------------- /docs/_images/demo_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/demo_image.png -------------------------------------------------------------------------------- /docs/_images/demo_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/demo_map.png -------------------------------------------------------------------------------- /docs/_images/demo_option.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/demo_option.png -------------------------------------------------------------------------------- /docs/_images/fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/fill.png -------------------------------------------------------------------------------- /docs/_images/geo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/geo.png -------------------------------------------------------------------------------- /docs/_images/h5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/h5.png -------------------------------------------------------------------------------- /docs/_images/input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/input.png -------------------------------------------------------------------------------- /docs/_images/inputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/inputs.png -------------------------------------------------------------------------------- /docs/_images/inspect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/inspect.png -------------------------------------------------------------------------------- /docs/_images/km_alg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/km_alg.png -------------------------------------------------------------------------------- /docs/_images/km_dis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/km_dis.png -------------------------------------------------------------------------------- /docs/_images/km_k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/km_k.png -------------------------------------------------------------------------------- /docs/_images/km_misc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/km_misc.png -------------------------------------------------------------------------------- /docs/_images/km_noise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/km_noise.png -------------------------------------------------------------------------------- /docs/_images/km_pn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/km_pn.png -------------------------------------------------------------------------------- /docs/_images/km_shade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/km_shade.png -------------------------------------------------------------------------------- /docs/_images/label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/label.png -------------------------------------------------------------------------------- /docs/_images/ld_dnd.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ld_dnd.gif -------------------------------------------------------------------------------- /docs/_images/ld_pn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/ld_pn.png -------------------------------------------------------------------------------- /docs/_images/load.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/load.png -------------------------------------------------------------------------------- /docs/_images/npy_plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/npy_plot.png -------------------------------------------------------------------------------- /docs/_images/results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/results.png -------------------------------------------------------------------------------- /docs/_images/s1_shp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/s1_shp.png -------------------------------------------------------------------------------- /docs/_images/seg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/seg.png -------------------------------------------------------------------------------- /docs/_images/seg_fix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/seg_fix.png -------------------------------------------------------------------------------- /docs/_images/seg_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/seg_grid.png -------------------------------------------------------------------------------- /docs/_images/seg_out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/seg_out.png -------------------------------------------------------------------------------- /docs/_images/seg_pn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/seg_pn.png -------------------------------------------------------------------------------- /docs/_images/shapefiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/shapefiles.png -------------------------------------------------------------------------------- /docs/_images/unck_out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_images/unck_out.png -------------------------------------------------------------------------------- /docs/_sources/ch1_started/firsttime.rst.txt: -------------------------------------------------------------------------------- 1 | First-Time Users 2 | ================ 3 | 4 | Learning a new thing could sometimes be a tough thing to do, 5 | but not when you deal with GRID. 6 | GRID aims to provide a smooth envirnoment 7 | for users to perform image segmentation, 8 | with no experience required! 9 | 10 | Launch GRID 11 | ----------- 12 | 13 | In the terminal: 14 | 15 | .. prompt:: bash $ 16 | 17 | python3 -m grid 18 | 19 | Demo mode 20 | --------- 21 | 22 | For the first-time users, it's recommended to use demo files. Users can start 23 | GRID in the demo mode by choosing the option "Demo" in the 24 | :ref:`welcome panel `. 25 | 26 | .. figure:: res/demo_option.png 27 | 28 | GRID provides two ways to start the analysis, 29 | the bottom one will use the demo image to proceed. 30 | 31 | .. figure:: res/demo_image.png 32 | 33 | **Demo image**. It's recommended to define AOI over the highlighted area. 34 | 35 | Work with your images 36 | --------------------- 37 | 38 | GRID supports most types of :ref:`image files `, 39 | including GeoTiff, PNG, and JPEG. 40 | It's encouraged to provide a :ref:`map file (CSV) ` as well to specify 41 | the field layout and plot IDs. To learn how to make a valid map file, 42 | please follow the instruction of the section :ref:`Maps`. 43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/_sources/ch1_started/installation.rst.txt: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Step 1: Python 5 | ---------------- 6 | 7 | GRID is developed in **Python 3**. 8 | Follow the `official instruction `_ 9 | to set up Python. 10 | 11 | Step 2: Rasterio (Windows Users) 12 | --------------------------------- 13 | The easiest way to install Rasterio in Windows is to build it from binaries 14 | `(Official instruction) `_. 15 | Please download correct versions of ``.whl`` from 16 | `Rasterio `_ and 17 | `GDAL `_, and use ``pip``` to install them. 18 | 19 | For example, if you want to run GRID in **64-bit** Windows 10 using Python **3.9**, 20 | the ``.whl`` names and the commands should be: 21 | 22 | .. prompt:: bash 23 | 24 | python -m pip install GDAL-3.4.3-cp39-cp39-win_amd64.whl 25 | python -m pip install rasterio-1.2.10-cp39-cp39-win_amd64.whl 26 | 27 | Step 2: Rasterio (Other Users) 28 | --------------------------------- 29 | `Rasterio `_ 30 | is the only GRID dependency that can't be installed via 31 | `PyPI `_. 32 | Below are the alternatives: 33 | 34 | * **Anaconda (Recommended)** 35 | Install Anaconda `here `_, 36 | and run the following commands in **Anaconda Prompt**: 37 | 38 | .. prompt:: bash 39 | 40 | conda config --add channels conda-forge 41 | conda install rasterio 42 | 43 | * `Install from binaries `_ 44 | 45 | * `Install from the source distribution `_ 46 | 47 | Step 3: Install GRID via PyPI 48 | -------------------------------- 49 | 50 | Finally, to install GRID, in the prompt 51 | (or Anaconda Prompts if you installed Rasterio via Anaconda): 52 | 53 | .. prompt:: bash 54 | 55 | python -m pip install photo_grid 56 | 57 | After finishing this step, you should be good to go. 58 | Otherwise, check what dependencies you miss or report any issue to 59 | the `GitHub repository `_ . 60 | 61 | .. NOTE:: 62 | If your system can't find the command ``pip``, 63 | follow the `link `_ 64 | to install it. 65 | -------------------------------------------------------------------------------- /docs/_sources/ch2_interface/aoi.rst.txt: -------------------------------------------------------------------------------- 1 | Define AOI 2 | ================ 3 | 4 | .. figure:: res/aoi_pn.png 5 | 6 | 7 | Screenshot of GRID defining AOI 8 | 9 | Often time the provided orthoimages contain areas you don't need. 10 | In this step, you can define an area of interest (AOI) to 11 | exclude the redundant parts. 12 | 13 | Draw AOI 14 | ---------- 15 | 16 | To draw AOI, 17 | left-click on the image **four times** to assign 18 | four corners of the AOI rectangle. 19 | The red shaded region will be AOI used in the analysis. 20 | 21 | .. raw:: html 22 | 23 | 26 | 27 | | 28 | 29 | Adjust AOI 30 | ---------- 31 | 32 | * **Move the entire AOI** 33 | Drag from the central area to move the AOI. 34 | The cursor should become a "move" icon. 35 | 36 | .. raw:: html 37 | 38 | 41 | 42 | | 43 | 44 | * **Move one corner of the AOI** 45 | Left-click near the **corner** and drag it to adjust its position. 46 | The cursor should become a magnifying glass to help increasing the precision. 47 | 48 | .. raw:: html 49 | 50 | 53 | 54 | | 55 | 56 | * **Move one side of the AOI** 57 | Left-click near the AOI **edge** and drag it to adjust its position. 58 | The cursor should become a magnifying glass to help increasing the precision. 59 | 60 | .. raw:: html 61 | 62 | 65 | 66 | | 67 | 68 | * **Rotate the AOI** 69 | Left-click and drag at the surrounding area of the AOI to rotate. 70 | The cursor should become a "rotation" icon indicating the AOI can be rotated. 71 | 72 | .. raw:: html 73 | 74 | 77 | 78 | | 79 | 80 | * **Create a new AOI** 81 | When move the cursor far away from the exisitng AOI, 82 | the cursor should become a magnifying glass again. 83 | It indicates that users can assign new set of coorindates to create a new AOI. 84 | 85 | .. raw:: html 86 | 87 | 90 | 91 | -------------------------------------------------------------------------------- /docs/_sources/ch2_interface/centroids.rst.txt: -------------------------------------------------------------------------------- 1 | Detect Centroids 2 | =================== 3 | 4 | .. figure:: res/ct_pn.png 5 | 6 | Screenshot of GRID searching centroids 7 | 8 | After defining POI, GRID will detect the plot centroids automatically by 9 | fitting a major axis and a minor axis to the field layout. 10 | The interactions of two axes are the detected centroids. 11 | Users can furhter adjust the axes or centroids if needed. 12 | 13 | Major axis 14 | ---------- 15 | 16 | .. figure:: res/ct_maj.png 17 | :width: 450 px 18 | :align: center 19 | 20 | GRID required one axis of the field layout to be 21 | either vertial (0°) or horizontal (90°). 22 | And this axis in GRID is defined as the **major axis**. 23 | 24 | * **Angle** 25 | Check 0° to set the major axis in the vertical direction, 26 | and 90° for the horizontal direction. 27 | 28 | * **# of ticks** 29 | Specify how many lines of plots follow the direction of the major axis. 30 | 31 | Minor axis 32 | ---------- 33 | 34 | .. raw:: html 35 | 36 | 39 | 40 | | 41 | 42 | The other axis to fit the layout is called the **minor axis**. 43 | This axis can be any angle that can fit the layout the best. 44 | 45 | * **Angle** 46 | Rotate the spinner to specify an angle 47 | between the minor axis and a vertical line. 48 | 49 | 50 | * **# of ticks** 51 | Same usage as one in the major axis. 52 | 53 | Centroid adjustment 54 | ------------------------- 55 | 56 | .. figure:: res/ct_adj.png 57 | :width: 450 px 58 | :align: center 59 | 60 | This section allows users to adjust the position of each axis. 61 | The checked axis will be highlighted by a thicker red line 62 | compared to the other axis. 63 | Following actions are available on the highlighted axis: 64 | 65 | * **Move** 66 | Left-click and drag on any lines to move. 67 | 68 | * **Add** 69 | Left-click on an area not occupied by any line. 70 | 71 | * **Delete** 72 | Right-click on a line you want to delete -------------------------------------------------------------------------------- /docs/_sources/ch2_interface/kmeans.rst.txt: -------------------------------------------------------------------------------- 1 | Define POI 2 | ================== 3 | 4 | .. figure:: res/km_pn.png 5 | 6 | Screenshot of GRID defining POI 7 | 8 | In this step, 9 | GRID will help users to extract pixel of interest (POI) from the AOI. 10 | POI is colored in yellow, whereas the remaining areas are shown as black pixels. 11 | Pixels contain soil, non-vegetation objects, 12 | and field residuals can be excluded in this step. 13 | There're also refining tools avaialble to remove shadow and image noise 14 | 15 | 16 | K-Means clustering algorithm 17 | ---------------------------- 18 | 19 | .. figure:: res/km_alg.png 20 | :width: 450 px 21 | :align: center 22 | 23 | The interface of K-means clustering algorithm 24 | 25 | GRID uses K-means clustering algorithm to cluster imagery pixels. 26 | Two parameters are available to be tuned: 27 | 28 | * **Channels used for clustering** 29 | By checking the box, the corresponding channel will be included 30 | in the feature space for clustering. 31 | The checkable boxes are imagery channels found from the inputs. 32 | For example, in a JPEG image, "1" is the red channel, 33 | which should also be the first channel of a JPEG image. 34 | 35 | * **K** 36 | It's how many clusters expected to exist in the image. 37 | 38 | .. figure:: res/km_k.png 39 | 40 | Comparison between the raw image, 3-cluster image, and 5-cluster image. 41 | 42 | Binarization 43 | ------------ 44 | 45 | .. raw:: html 46 | 47 | 50 | 51 | | 52 | 53 | This section allows you to assign which clusters belong to POI. 54 | The image will then be binarized into a yellow/black image 55 | showing POI and non-POI, respectively. Two ways to do the binarization: 56 | 57 | * **Auto cutoff** 58 | GRID will rank clusters based on the possibility of being POI. 59 | When auto cutoff = **m**, 60 | the first **m** cluters will be assigned to AOI (yellow). 61 | 62 | * **Custom** 63 | You can also assign POI in the original order of clusters 64 | (which usually is arbitrary from K-means clustering algorithm). 65 | The checked box will be assign to POI. 66 | 67 | 68 | Refine POI 69 | ----------- 70 | 71 | .. raw:: html 72 | 73 | 76 | 77 | | 78 | 79 | * **De-Shade** 80 | Even though most orthoimages are taken around noon, 81 | which is a time that has minimum shaded area, 82 | there're still some cases that shaded areas are observed in the image. 83 | These type of areas usually have lower brightness and spectral reflectance. 84 | Exclude such areas can improve the quality of the evaluation on each plot. 85 | 86 | .. figure:: res/km_shade.png 87 | :align: center 88 | 89 | * **De-Noise** 90 | It's inevitable to include non-vegetation objects in the image. 91 | Pipes for irrigation system, 92 | or field residuals can sometimes look similar to your evaluated subjects. 93 | To exclude them, GRID provide a gaussian filtering approach 94 | to remove such noise. 95 | 96 | .. figure:: res/km_noise.png 97 | :align: center 98 | 99 | 100 | Display/Zoom 101 | ------------ 102 | 103 | .. figure:: res/km_misc.png 104 | :width: 450 px 105 | :align: center 106 | 107 | Different display modes can help users to validate the result. 108 | Users can hover the cursor on the image to zoom in certain areas. 109 | And use the keys ``A``, ``S``, and ``D`` 110 | to switch between different display modes 111 | in order to evaluate whether the extracted POIs are valid. 112 | 113 | .. figure:: res/km_dis.png 114 | :align: center -------------------------------------------------------------------------------- /docs/_sources/ch2_interface/load.rst.txt: -------------------------------------------------------------------------------- 1 | Load Files 2 | ========== 3 | 4 | It's the first panel you'll see in GRID. 5 | The top option allows you to load your own image, field layouts, and shapefiles. 6 | And the bottom option will load an example image for you to explore GRID. 7 | 8 | .. figure:: res/ld_pn.png 9 | 10 | Screenshot of GRID taking inputs 11 | 12 | Input files 13 | ----------- 14 | 15 | * **Iamge** 16 | It's a required input in GRID. Load an image you want to work on. 17 | GeoTiff, regular Tiff, PNG, and JPG are supported. 18 | 19 | * **Map** 20 | It's optional to provide a map file. 21 | It can help GRID to know how many rows and columns exist in the image, 22 | also providing plot IDs in order to track your plots better. 23 | To learn how to provide a valid map file, go to this :ref:`section`. 24 | 25 | * **Shape** 26 | It's another optional input. 27 | By loading a shapefile, GRID can replicate plot positions 28 | learned from the previous image to the current one. 29 | This feature is specificaly designed for a situation when you have multiple 30 | images taken in different seasons. 31 | A detailed tutorial is shown in this 32 | :ref:`section`. 33 | 34 | Drag and drop 35 | ------------- 36 | 37 | Don't like the "browse button"? 38 | No worry, you can drag and drop files directly from your folders 39 | 40 | .. figure:: res/ld_dnd.gif 41 | 42 | Drag-n-drop feature in GRID 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /docs/_sources/ch2_interface/segmentation.rst.txt: -------------------------------------------------------------------------------- 1 | Segmentation 2 | ============ 3 | 4 | .. figure:: res/seg_pn.png 5 | 6 | Screenshot of GRID performing segmentation 7 | 8 | Dynamic 9 | ------- 10 | 11 | .. figure:: res/seg_grid.png 12 | :width: 350 px 13 | :align: center 14 | 15 | GRID infers plot boundaries based on the POI distribution. 16 | The only tunable parameter ``Grid Coef.`` is how much weight should be put 17 | on the global layout over local pattern. 18 | For example, when the boundary between two adjacent plots are ambiguous, 19 | ``Grid Coef. = 0.0`` would encourge plot with more connected POI to 20 | agressively expand its border. 21 | Whereas ``Grid Coef. = 1.0`` would let both of their borders to follow 22 | the pattern from other rows/columes. 23 | 24 | Fixed 25 | ----- 26 | 27 | .. figure:: res/seg_fix.png 28 | :width: 450 px 29 | :align: center 30 | 31 | If the option ``Fixed`` is checked, 32 | plots will be assigned boundaries with an equal size. 33 | The size of **units** is obtained from: 34 | 35 | .. math:: 36 | 37 | \text{1 unit} = 38 | \frac{\text{Image size}}{\text{Number of plots}} * \frac{1}{100} 39 | 40 | For example, if an input image is 1,000 pixels wide, 41 | and there're 10 columns of plots. 42 | 1 **unit** then become 1 pixel wide. 43 | In this case, assigning 50 **units** width to the plots meaning 44 | each plot would has 5 pixel wide boundary. 45 | And when ``width = 100 units``, 46 | plot boundaries should horizontally occupy the entire image. 47 | 48 | Fine-tune results 49 | ----------------- 50 | 51 | .. raw:: html 52 | 53 | 56 | 57 | | 58 | 59 | GRID provides several ways to fine-tune the segmentation results: 60 | 61 | * **Move centroids** 62 | Left-click and drag within the plots to move the centroids 63 | 64 | * **Resize borders** 65 | Left-click and drag at **the inner side** of the border to resize it. 66 | 67 | * **Row positions** 68 | Left-click and drag at any row of plots to move the entire row. 69 | 70 | * **Column positions** 71 | Left-click and drag at any column of plots to move the entire column. 72 | 73 | Export results 74 | -------------- 75 | 76 | .. figure:: res/seg_out.png 77 | :width: 450 px 78 | :align: center 79 | 80 | * **Prefix** 81 | GRID will create a folder for placing the outputs. 82 | The name of this folder is the specified ``Prefix``, 83 | and all the output filenames will start wtth the ``Prefix``. 84 | 85 | * **Output path** 86 | A path where you want the outputs to be saved 87 | 88 | * **Simple output** 89 | By checking this option, only basic results will be saved. 90 | There's further discussion in this `section ` 91 | -------------------------------------------------------------------------------- /docs/_sources/ch3_io/inputs.rst.txt: -------------------------------------------------------------------------------- 1 | Inputs 2 | ====== 3 | 4 | Images 5 | ------ 6 | 7 | GRID supports most of common formats, 8 | including ``.tif``, ``.jpg``, and ``.png`` 9 | 10 | .. note:: 11 | Currently, GRID supports images with up to **9 spectral channels**. 12 | The exceeded channels would be ignored in the K-means clustering process, 13 | and it may also affect the performance. 14 | 15 | Maps 16 | ---- 17 | 18 | .. figure:: res/demo_map.png 19 | :align: center 20 | 21 | This is an optional input to tell what's the field layout in the image. 22 | It's a simple ``.csv`` file, requiring no header nor row names. 23 | Simply put the plot names based on how they are grown in the field, 24 | and GRID can output results along with these given IDs. 25 | 26 | Shapefiles (inputs) 27 | ---------------------- 28 | 29 | The shapefile has to be obtained from GRID to be a valid input. 30 | It's an optional file allows GRID to replicate plot boundaries 31 | rom the previous analysis in GRID. 32 | You can learn the details in this :ref:`section`. 33 | 34 | 35 | -------------------------------------------------------------------------------- /docs/_sources/ch3_io/outputs.rst.txt: -------------------------------------------------------------------------------- 1 | Outputs 2 | ======= 3 | 4 | Tabular results 5 | --------------- 6 | 7 | Extracted information for each plot is presented in a ``.csv`` file. 8 | Below we will list the definition of each field: 9 | 10 | * **var** 11 | Variety names, which are the IDs learned from the map file. 12 | ID will be "unnamed_xx" instead if there's no map file provided. 13 | 14 | * **row** 15 | Count from the toppest row of plots. 16 | 17 | * **column** 18 | Count from the leftest column of plots. 19 | 20 | * **area_all** 21 | Number of pixels in the segmented rectangle (red boundaries). 22 | 23 | * **area_veg** 24 | Number of POI 25 | 26 | Vegetation indices are represented by the averaged value of the POI, 27 | and ``xxx_std`` is its corresponding standard deviation. 28 | Below are the formula of each index, 29 | the x\ :sup:`th`\ band will be labeled as b\ :sub:`x`\ : 30 | 31 | * **Normalized Difference Vegetation Index (NDVI)** 32 | .. math:: 33 | 34 | NDVI = \frac{(b_4 - b_1)}{(b_4 + b_1)} 35 | 36 | * **Green Normalized Difference Vegetation Index (GNDVI)** 37 | .. math:: 38 | 39 | GNDVI = \frac{(b_4 - b_2)}{(b_4 + b_2)} 40 | 41 | * **Combination of Normalized Difference Vegetation Index (CNDVI)** 42 | .. math:: 43 | 44 | CNDVI = \frac{(2 * b_4 - b_1 - b_2)}{(b_4 + b_1 + b_2)} 45 | 46 | * **Ratio Vegetation Index (RVI)** 47 | .. math:: 48 | 49 | RVI = \frac{b_4}{b_1} 50 | 51 | * **Green Ratio Vegetation Index (GRVI)** 52 | .. math:: 53 | 54 | GRVI = \frac{b_4}{b_2} 55 | 56 | * **Normalized Difference Greenness Vegetation Index (NDGVI)** 57 | .. math:: 58 | 59 | NDGVI = \frac{(b_2 - b_1)}{(b_2 + b_1)} 60 | 61 | The channel values are recorded in this output as well. 62 | The **averaged value** of the x\ :sup:`th`\ channel and 63 | its **standard deviation** will be stored 64 | in the column of ``ch_x`` and ``ch_x_std``, respectively. 65 | 66 | Images for validations 67 | ---------------------- 68 | 69 | Several images are exported for users to validate the results. 70 | The title of each sub-figure is its suffix: 71 | 72 | * **Checked** :ref:`Simple output `: 73 | 74 | .. figure:: res/ck_out.png 75 | :align: center 76 | 77 | 78 | 79 | * **unchecked** :ref:`Simple output `: 80 | 81 | .. figure:: res/unck_out.png 82 | :align: center 83 | 84 | Shapefiles (outputs) 85 | ---------------------- 86 | 87 | The shapefile is generated by the Python package 88 | `PyShp `_ . 89 | Each record represent a segmented plot, 90 | containing coordinates of boundaries 91 | and all the attributes listed in the tabular results. 92 | The shapefile can be `integrated to the analysis in QGIS ` 93 | You can also fetch the attributes in Python, 94 | we use the first plot as an example: 95 | 96 | .. nbinput:: ipython3 97 | :execution-count: 1 98 | 99 | # import PyShp 100 | import shapefile 101 | 102 | # load shapefile and get record 103 | file_sp = shapefile.Reader("GRID.shp") 104 | 105 | .. nbinput:: ipython3 106 | :execution-count: 2 107 | 108 | # list attributes 109 | file_sp.fields 110 | 111 | .. nboutput:: 112 | :execution-count: 2 113 | 114 | [('DeletionFlag', 'C', 1, 0), 115 | ['var', 'C', 20, 20], 116 | ['row', 'N', 10, 10], 117 | ['col', 'N', 10, 10], ... ... 118 | 119 | .. nbinput:: ipython3 120 | :execution-count: 3 121 | 122 | # demo with the first record 123 | records = file_sp.shapeRecords() 124 | first_plot = records[0] 125 | 126 | .. nbinput:: ipython3 127 | :execution-count: 4 128 | 129 | # polygon coordinates 130 | first_plot.shape.points 131 | 132 | .. nboutput:: 133 | :execution-count: 4 134 | 135 | [(1153.5487982819586, 154.80525925556415), 136 | 137 | (1222.2592755806982, 159.82284445357462), 138 | 139 | (1203.8125387566886, 194.30747345836616), 140 | 141 | (1135.102061457949, 189.2898882603557)] 142 | 143 | .. nbinput:: ipython3 144 | :execution-count: 5 145 | 146 | # attributes (exact the same as ones from the csv output) 147 | first_plot.record 148 | 149 | .. nboutput:: 150 | :execution-count: 5 151 | 152 | Record #0: ['325', 0.0, 0.0, 2584.0, 1359.0, 0.23869822, 0.12908907, nan, 153 | nan, 0.14441162, 0.07516949, 1.69628965, 0.47877022, 0.99999999, 0.0, 154 | 0.23869822, 0.12908907, 36.2281089, 23.588658, 54.5069904, 22.4681228, 155 | 29.9830757, 14.3784804, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0] 156 | 157 | .. note:: 158 | When you load the ``.shp`` file in Python, 159 | make sure you also have ``.dbf`` and ``.shx`` 160 | placed in the **same location** and sharing the **same name**. 161 | 162 | NumPy format of AOI 163 | ------------------- 164 | 165 | GRID will output the AOI you cropped from the step of :ref:`define AOI`. 166 | The file is suffixed with ``_image`` and has an extension name ``.npy``. 167 | It's encoded as a 168 | `numpy ndarray `_ 169 | and can be loaded in Python: 170 | 171 | .. nbinput:: ipython3 172 | :execution-count: 1 173 | 174 | # imports 175 | import numpy as np 176 | import matplotlib.pyplot as plt 177 | 178 | .. nbinput:: ipython3 179 | :execution-count: 2 180 | 181 | # load npy file 182 | data = np.load("GRID_image.npy") 183 | data.shape 184 | 185 | .. nboutput:: 186 | :execution-count: 2 187 | 188 | (142, 214, 3) 189 | 190 | .. nbinput:: ipython3 191 | :execution-count: 3 192 | 193 | # plot the ndarray 194 | plt.imshow(data) 195 | 196 | .. nboutput:: 197 | :execution-count: 3 198 | 199 | 200 | 201 | .. figure:: res/npy_plot.png 202 | :align: center 203 | :width: 400 px 204 | 205 | H5 dataset 206 | ---------- 207 | .. note:: 208 | Only by **unchecking** the option :ref:`Simple output ` 209 | to obtain this file, 210 | as compressing plots into a hdf5 can sometimes be time-consuming. 211 | 212 | Segmented plots are structured in 213 | `numpy ndarrays `_ 214 | and are compressed in a 215 | `HDF5 file `_ . 216 | The file has an extension name ``.h5`` and you can load the file in Python via 217 | `H5py `_ : 218 | 219 | .. nbinput:: ipython3 220 | :execution-count: 1 221 | 222 | # imports 223 | import matplotlib.pyplot as plt 224 | import h5py as h5 225 | 226 | .. nbinput:: ipython3 227 | :execution-count: 2 228 | 229 | # read h5 file in read mode and list all the plot IDs 230 | f = h5.File("GRID.h5", "r") 231 | ids = list(f.keys()); ids 232 | 233 | .. nboutput:: 234 | :execution-count: 2 235 | 236 | ['plot_1', 'plot_2', 'plot_3', 'plot_4', 237 | 'plot_5', 'plot_6', 'plot_7', 'plot_8', 238 | 'plot_9', 'plot_10', 'plot_11', 'plot_12'] 239 | 240 | .. nbinput:: ipython3 241 | :execution-count: 3 242 | 243 | # plot all the segmented plots 244 | r = 3; c = 4 245 | for i in range(r * c): 246 | plt.subplot(r, c, (i + 1)) 247 | plt.title(ids[i]) 248 | img = f.get(ids[i]) 249 | plt.imshow(img) 250 | plt.show() 251 | 252 | .. figure:: res/h5.png 253 | :width: 800 px 254 | 255 | .. nbinput:: ipython3 256 | :execution-count: 4 257 | 258 | # close the h5 file 259 | f.close() 260 | 261 | | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | -------------------------------------------------------------------------------- /docs/_sources/ch4_adv/arbitrary.rst.txt: -------------------------------------------------------------------------------- 1 | Deal with Arbitrary Field Layout 2 | =================================== 3 | 4 | .. figure:: res/arbitrary/input.png 5 | :align: center 6 | 7 | Example of an arbitrary layout 8 | 9 | An arbitrary field layout is a type of plot arrangement that 10 | there's no consistant number of plots exisitng in a row/column. 11 | To deal with such scenario, users have to additionally 12 | assign centroids for the extra plots. Make sure **every plot** in the 13 | image get assigned a centroid, and it's ok to have some centroids 14 | occupying empty space without plot. 15 | 16 | .. figure:: res/arbitrary/centroid.png 17 | :align: center 18 | 19 | The way to define centroids for an arbitrary layout 20 | 21 | 22 | .. figure:: res/arbitrary/seg.png 23 | :align: center 24 | 25 | GRID will automatically detect whether the centroid has plot occupied or not, 26 | shrinking the space of the empty spots and 27 | expanding borders for ones that really cover plots. 28 | 29 | -------------------------------------------------------------------------------- /docs/_sources/ch4_adv/dl.rst.txt: -------------------------------------------------------------------------------- 1 | Generate Datasets for Deep Learning 2 | =================================== 3 | 4 | Will be done by the end of August 2020. -------------------------------------------------------------------------------- /docs/_sources/ch4_adv/qgis.rst.txt: -------------------------------------------------------------------------------- 1 | Work with QGIS 2 | ============== 3 | 4 | QGIS supports Drag and Drop feature to load files. 5 | You can simply drag the orthoimage into QGIS, 6 | as well as the shapefile obtained from GRID 7 | 8 | .. figure:: res/qgis/shapefiles.png 9 | :align: center 10 | :width: 500 11 | 12 | Right click on the shapefile layer, and choose ``property`` to define the layer. 13 | In the tab of ``Symbology``, users can design how to show the border. 14 | Our example use red solid line to draw borders without filling any color. 15 | 16 | .. figure:: res/qgis/border.png 17 | :align: center 18 | 19 | In the tab of ``label``, users can further config the way to show plot IDs. 20 | 21 | .. figure:: res/qgis/label.png 22 | :align: center 23 | 24 | It's also possible to take advantage of GRID outputs. 25 | In the tab of ``Symbology``, 26 | and select ``Graduated`` in the very top drop-down menu. 27 | Then in the drop-down menu of ``Value``, 28 | select a feature you'd like to visualize. 29 | In this example, we use NDVI as indicators. 30 | Then click ``classify`` button to generate the heat map. 31 | 32 | .. figure:: res/qgis/fill.png 33 | :align: center 34 | 35 | Visualize data in a heatmap allows us to evaluate the spatial variation 36 | between different plots. 37 | 38 | .. figure:: res/qgis/inspect.png 39 | :align: center 40 | -------------------------------------------------------------------------------- /docs/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | .. GRID documentation master file, created by 2 | sphinx-quickstart on Thu Jul 9 14:34:18 2020. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | GRID: Deal with Field Segmentations Elegantly 7 | ========================================================== 8 | 9 | .. figure:: res/abstract.png 10 | 11 | .. figure:: https://img.shields.io/pypi/dm/photo_grid.svg?label=pypi%20downloads&logo=python&logoColor=white 12 | 13 | .. figure:: https://img.shields.io/pypi/v/photo_grid.svg?label=pypi%20version&logo=python&logoColor=white 14 | 15 | .. raw:: html 16 | 17 | Follow @poissonfish 18 | Watch 19 | Star 20 | Fork 21 | Issue 22 | 23 | | 24 | 25 | 26 | Getting started 27 | ---------------- 28 | 29 | * **Installation**: 30 | :ref:`Python 3 ` | 31 | :ref:`Rasterio ` | 32 | :ref:`PyPI ` | 33 | 34 | * **First-time users**: 35 | :ref:`Launch GRID` | 36 | :ref:`Demo mode` | 37 | :ref:`Work with your images` | 38 | 39 | .. toctree:: 40 | :maxdepth: 2 41 | :hidden: 42 | :caption: GATTING STARTED 43 | 44 | /ch1_started/installation 45 | /ch1_started/firsttime 46 | 47 | Interface 48 | --------- 49 | 50 | * **File loading**: 51 | :ref:`Input files` | 52 | :ref:`Drag and drop` | 53 | 54 | * **Define AOI**: 55 | :ref:`Draw AOI` | 56 | :ref:`Adjust AOI` | 57 | 58 | * **Define POI**: 59 | :ref:`K-Means clustering algorithm` | 60 | :ref:`Binarization` | 61 | :ref:`Refine POI` | 62 | :ref:`Display/Zoom` | 63 | 64 | * **Detect centroids**: 65 | :ref:`Major axis` | 66 | :ref:`Minor axis` | 67 | :ref:`Centroid adjustment` | 68 | 69 | * **Segmentation**: 70 | :ref:`Dynamic` | 71 | :ref:`Fixed` | 72 | :ref:`Fine-tune results` | 73 | :ref:`Export results` | 74 | 75 | .. toctree:: 76 | :maxdepth: 2 77 | :hidden: 78 | :caption: INTERFACE 79 | 80 | /ch2_interface/load 81 | /ch2_interface/aoi 82 | /ch2_interface/kmeans 83 | /ch2_interface/centroids 84 | /ch2_interface/segmentation 85 | 86 | Inputs/Outputs 87 | -------------- 88 | 89 | * **Inputs**: 90 | :ref:`Images` | 91 | :ref:`Maps` | 92 | :ref:`Shapefiles ` | 93 | 94 | * **Outputs**: 95 | :ref:`Tabular results` | 96 | :ref:`Validation ` | 97 | :ref:`Shapefiles ` | 98 | :ref:`NumPy format of AOI` | 99 | :ref:`H5 dataset` | 100 | 101 | .. toctree:: 102 | :maxdepth: 2 103 | :hidden: 104 | :caption: INPUTS/OUTPUTS 105 | 106 | /ch3_io/inputs 107 | /ch3_io/outputs 108 | 109 | Advanced usage (Jupyter notebook) 110 | ---------------------------------- 111 | 112 | * :ref:`Customize Vegetation Indices` 113 | 114 | * :ref:`Multi-Season Images` 115 | 116 | * :ref:`Work with QGIS` 117 | 118 | * :ref:`Deal with Arbitrary Field Layout` 119 | 120 | * :ref:`Generate datasets for deep learning` 121 | 122 | .. toctree:: 123 | :maxdepth: 2 124 | :hidden: 125 | :caption: ADVANCED USAGE 126 | 127 | /ch4_adv/custom_index 128 | /ch4_adv/seasons 129 | /ch4_adv/qgis 130 | /ch4_adv/arbitrary 131 | /ch4_adv/dl 132 | 133 | How to cite GRID 134 | ------------------- 135 | To cite GRID, you can: 136 | 137 | - Import `GRID.bib `_ 138 | 139 | - or manually key in the following info: 140 | 141 | .. code-block:: none 142 | 143 | @article{Chen and Zhang:2020, 144 | author = {Chunpeng James Chen and Zhiwu Zhang}, 145 | title = {GRID: A Python Package for Field Plot Phenotyping Using Aerial Images}, 146 | month = may, 147 | year = 2020, 148 | journal = {Remote Sensing}, 149 | volume = 12, 150 | issue = 11, 151 | pages = 1697 152 | doi = {10.3390/rs12111697}, 153 | url = {https://doi.org/10.3390/rs12111697} 154 | } 155 | 156 | Support 157 | -------- 158 | 159 | Find any issue? Post it on 160 | `GitHub `_ 161 | or contact `James Chen `_ -------------------------------------------------------------------------------- /docs/_static/GRID_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/GRID_banner.png -------------------------------------------------------------------------------- /docs/_static/_sphinx_javascript_frameworks_compat.js: -------------------------------------------------------------------------------- 1 | /* 2 | * _sphinx_javascript_frameworks_compat.js 3 | * ~~~~~~~~~~ 4 | * 5 | * Compatability shim for jQuery and underscores.js. 6 | * 7 | * WILL BE REMOVED IN Sphinx 6.0 8 | * xref RemovedInSphinx60Warning 9 | * 10 | */ 11 | 12 | /** 13 | * select a different prefix for underscore 14 | */ 15 | $u = _.noConflict(); 16 | 17 | 18 | /** 19 | * small helper function to urldecode strings 20 | * 21 | * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL 22 | */ 23 | jQuery.urldecode = function(x) { 24 | if (!x) { 25 | return x 26 | } 27 | return decodeURIComponent(x.replace(/\+/g, ' ')); 28 | }; 29 | 30 | /** 31 | * small helper function to urlencode strings 32 | */ 33 | jQuery.urlencode = encodeURIComponent; 34 | 35 | /** 36 | * This function returns the parsed url parameters of the 37 | * current request. Multiple values per key are supported, 38 | * it will always return arrays of strings for the value parts. 39 | */ 40 | jQuery.getQueryParameters = function(s) { 41 | if (typeof s === 'undefined') 42 | s = document.location.search; 43 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 44 | var result = {}; 45 | for (var i = 0; i < parts.length; i++) { 46 | var tmp = parts[i].split('=', 2); 47 | var key = jQuery.urldecode(tmp[0]); 48 | var value = jQuery.urldecode(tmp[1]); 49 | if (key in result) 50 | result[key].push(value); 51 | else 52 | result[key] = [value]; 53 | } 54 | return result; 55 | }; 56 | 57 | /** 58 | * highlight a given string on a jquery object by wrapping it in 59 | * span elements with the given class name. 60 | */ 61 | jQuery.fn.highlightText = function(text, className) { 62 | function highlight(node, addItems) { 63 | if (node.nodeType === 3) { 64 | var val = node.nodeValue; 65 | var pos = val.toLowerCase().indexOf(text); 66 | if (pos >= 0 && 67 | !jQuery(node.parentNode).hasClass(className) && 68 | !jQuery(node.parentNode).hasClass("nohighlight")) { 69 | var span; 70 | var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); 71 | if (isInSVG) { 72 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 73 | } else { 74 | span = document.createElement("span"); 75 | span.className = className; 76 | } 77 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 78 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 79 | document.createTextNode(val.substr(pos + text.length)), 80 | node.nextSibling)); 81 | node.nodeValue = val.substr(0, pos); 82 | if (isInSVG) { 83 | var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); 84 | var bbox = node.parentElement.getBBox(); 85 | rect.x.baseVal.value = bbox.x; 86 | rect.y.baseVal.value = bbox.y; 87 | rect.width.baseVal.value = bbox.width; 88 | rect.height.baseVal.value = bbox.height; 89 | rect.setAttribute('class', className); 90 | addItems.push({ 91 | "parent": node.parentNode, 92 | "target": rect}); 93 | } 94 | } 95 | } 96 | else if (!jQuery(node).is("button, select, textarea")) { 97 | jQuery.each(node.childNodes, function() { 98 | highlight(this, addItems); 99 | }); 100 | } 101 | } 102 | var addItems = []; 103 | var result = this.each(function() { 104 | highlight(this, addItems); 105 | }); 106 | for (var i = 0; i < addItems.length; ++i) { 107 | jQuery(addItems[i].parent).before(addItems[i].target); 108 | } 109 | return result; 110 | }; 111 | 112 | /* 113 | * backward compatibility for jQuery.browser 114 | * This will be supported until firefox bug is fixed. 115 | */ 116 | if (!jQuery.browser) { 117 | jQuery.uaMatch = function(ua) { 118 | ua = ua.toLowerCase(); 119 | 120 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 121 | /(webkit)[ \/]([\w.]+)/.exec(ua) || 122 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 123 | /(msie) ([\w.]+)/.exec(ua) || 124 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 125 | []; 126 | 127 | return { 128 | browser: match[ 1 ] || "", 129 | version: match[ 2 ] || "0" 130 | }; 131 | }; 132 | jQuery.browser = {}; 133 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; 134 | } 135 | -------------------------------------------------------------------------------- /docs/_static/aoi_corner.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/aoi_corner.mp4 -------------------------------------------------------------------------------- /docs/_static/aoi_create.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/aoi_create.mp4 -------------------------------------------------------------------------------- /docs/_static/aoi_entire.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/aoi_entire.mp4 -------------------------------------------------------------------------------- /docs/_static/aoi_new.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/aoi_new.mp4 -------------------------------------------------------------------------------- /docs/_static/aoi_rotate.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/aoi_rotate.mp4 -------------------------------------------------------------------------------- /docs/_static/aoi_side.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/aoi_side.mp4 -------------------------------------------------------------------------------- /docs/_static/css/badge_only.css: -------------------------------------------------------------------------------- 1 | .fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} -------------------------------------------------------------------------------- /docs/_static/css/fonts/Roboto-Slab-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/Roboto-Slab-Bold.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/Roboto-Slab-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/Roboto-Slab-Bold.woff2 -------------------------------------------------------------------------------- /docs/_static/css/fonts/Roboto-Slab-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/Roboto-Slab-Regular.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/Roboto-Slab-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/Roboto-Slab-Regular.woff2 -------------------------------------------------------------------------------- /docs/_static/css/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/_static/css/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/_static/css/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/lato-bold-italic.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/lato-bold-italic.woff2 -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/lato-bold.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/lato-bold.woff2 -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-normal-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/lato-normal-italic.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-normal-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/lato-normal-italic.woff2 -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/lato-normal.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/css/fonts/lato-normal.woff2 -------------------------------------------------------------------------------- /docs/_static/ct_min.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/ct_min.mp4 -------------------------------------------------------------------------------- /docs/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: '1.2.19', 4 | LANGUAGE: 'en', 5 | COLLAPSE_INDEX: false, 6 | BUILDER: 'html', 7 | FILE_SUFFIX: '.html', 8 | LINK_SUFFIX: '.html', 9 | HAS_SOURCE: true, 10 | SOURCELINK_SUFFIX: '.txt', 11 | NAVIGATION_WITH_KEYS: false, 12 | SHOW_SEARCH_SUMMARY: true, 13 | ENABLE_SEARCH_SHORTCUTS: true, 14 | }; -------------------------------------------------------------------------------- /docs/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/file.png -------------------------------------------------------------------------------- /docs/_static/google_analytics.js: -------------------------------------------------------------------------------- 1 | window.dataLayer = window.dataLayer || []; 2 | 3 | function gtag() { 4 | dataLayer.push(arguments); 5 | } 6 | gtag('js', new Date()); 7 | gtag('config', 'UA-151005724-3'); -------------------------------------------------------------------------------- /docs/_static/js/badge_only.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}}); -------------------------------------------------------------------------------- /docs/_static/js/html5shiv-printshiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /docs/_static/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /docs/_static/js/theme.js: -------------------------------------------------------------------------------- 1 | !function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 63 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 64 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 65 | var s_v = "^(" + C + ")?" + v; // vowel in stem 66 | 67 | this.stemWord = function (w) { 68 | var stem; 69 | var suffix; 70 | var firstch; 71 | var origword = w; 72 | 73 | if (w.length < 3) 74 | return w; 75 | 76 | var re; 77 | var re2; 78 | var re3; 79 | var re4; 80 | 81 | firstch = w.substr(0,1); 82 | if (firstch == "y") 83 | w = firstch.toUpperCase() + w.substr(1); 84 | 85 | // Step 1a 86 | re = /^(.+?)(ss|i)es$/; 87 | re2 = /^(.+?)([^s])s$/; 88 | 89 | if (re.test(w)) 90 | w = w.replace(re,"$1$2"); 91 | else if (re2.test(w)) 92 | w = w.replace(re2,"$1$2"); 93 | 94 | // Step 1b 95 | re = /^(.+?)eed$/; 96 | re2 = /^(.+?)(ed|ing)$/; 97 | if (re.test(w)) { 98 | var fp = re.exec(w); 99 | re = new RegExp(mgr0); 100 | if (re.test(fp[1])) { 101 | re = /.$/; 102 | w = w.replace(re,""); 103 | } 104 | } 105 | else if (re2.test(w)) { 106 | var fp = re2.exec(w); 107 | stem = fp[1]; 108 | re2 = new RegExp(s_v); 109 | if (re2.test(stem)) { 110 | w = stem; 111 | re2 = /(at|bl|iz)$/; 112 | re3 = new RegExp("([^aeiouylsz])\\1$"); 113 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 114 | if (re2.test(w)) 115 | w = w + "e"; 116 | else if (re3.test(w)) { 117 | re = /.$/; 118 | w = w.replace(re,""); 119 | } 120 | else if (re4.test(w)) 121 | w = w + "e"; 122 | } 123 | } 124 | 125 | // Step 1c 126 | re = /^(.+?)y$/; 127 | if (re.test(w)) { 128 | var fp = re.exec(w); 129 | stem = fp[1]; 130 | re = new RegExp(s_v); 131 | if (re.test(stem)) 132 | w = stem + "i"; 133 | } 134 | 135 | // Step 2 136 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; 137 | if (re.test(w)) { 138 | var fp = re.exec(w); 139 | stem = fp[1]; 140 | suffix = fp[2]; 141 | re = new RegExp(mgr0); 142 | if (re.test(stem)) 143 | w = stem + step2list[suffix]; 144 | } 145 | 146 | // Step 3 147 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; 148 | if (re.test(w)) { 149 | var fp = re.exec(w); 150 | stem = fp[1]; 151 | suffix = fp[2]; 152 | re = new RegExp(mgr0); 153 | if (re.test(stem)) 154 | w = stem + step3list[suffix]; 155 | } 156 | 157 | // Step 4 158 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; 159 | re2 = /^(.+?)(s|t)(ion)$/; 160 | if (re.test(w)) { 161 | var fp = re.exec(w); 162 | stem = fp[1]; 163 | re = new RegExp(mgr1); 164 | if (re.test(stem)) 165 | w = stem; 166 | } 167 | else if (re2.test(w)) { 168 | var fp = re2.exec(w); 169 | stem = fp[1] + fp[2]; 170 | re2 = new RegExp(mgr1); 171 | if (re2.test(stem)) 172 | w = stem; 173 | } 174 | 175 | // Step 5 176 | re = /^(.+?)e$/; 177 | if (re.test(w)) { 178 | var fp = re.exec(w); 179 | stem = fp[1]; 180 | re = new RegExp(mgr1); 181 | re2 = new RegExp(meq1); 182 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 183 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) 184 | w = stem; 185 | } 186 | re = /ll$/; 187 | re2 = new RegExp(mgr1); 188 | if (re.test(w) && re2.test(w)) { 189 | re = /.$/; 190 | w = w.replace(re,""); 191 | } 192 | 193 | // and turn initial Y back to y 194 | if (firstch == "y") 195 | w = firstch.toLowerCase() + w.substr(1); 196 | return w; 197 | } 198 | } 199 | 200 | -------------------------------------------------------------------------------- /docs/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/minus.png -------------------------------------------------------------------------------- /docs/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/plus.png -------------------------------------------------------------------------------- /docs/_static/poi_bin.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/poi_bin.mp4 -------------------------------------------------------------------------------- /docs/_static/poi_noise.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/poi_noise.mp4 -------------------------------------------------------------------------------- /docs/_static/poi_refine.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/poi_refine.mp4 -------------------------------------------------------------------------------- /docs/_static/poi_shad.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/poi_shad.mp4 -------------------------------------------------------------------------------- /docs/_static/pygments.css: -------------------------------------------------------------------------------- 1 | pre { line-height: 125%; } 2 | td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 4 | td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 6 | .highlight .hll { background-color: #ffffcc } 7 | .highlight { background: #f8f8f8; } 8 | .highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ 9 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 10 | .highlight .k { color: #008000; font-weight: bold } /* Keyword */ 11 | .highlight .o { color: #666666 } /* Operator */ 12 | .highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ 13 | .highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ 14 | .highlight .cp { color: #9C6500 } /* Comment.Preproc */ 15 | .highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ 16 | .highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ 17 | .highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ 18 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 19 | .highlight .ge { font-style: italic } /* Generic.Emph */ 20 | .highlight .gr { color: #E40000 } /* Generic.Error */ 21 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 22 | .highlight .gi { color: #008400 } /* Generic.Inserted */ 23 | .highlight .go { color: #717171 } /* Generic.Output */ 24 | .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 25 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 26 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 27 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 28 | .highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ 29 | .highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ 30 | .highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ 31 | .highlight .kp { color: #008000 } /* Keyword.Pseudo */ 32 | .highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ 33 | .highlight .kt { color: #B00040 } /* Keyword.Type */ 34 | .highlight .m { color: #666666 } /* Literal.Number */ 35 | .highlight .s { color: #BA2121 } /* Literal.String */ 36 | .highlight .na { color: #687822 } /* Name.Attribute */ 37 | .highlight .nb { color: #008000 } /* Name.Builtin */ 38 | .highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 39 | .highlight .no { color: #880000 } /* Name.Constant */ 40 | .highlight .nd { color: #AA22FF } /* Name.Decorator */ 41 | .highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ 42 | .highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ 43 | .highlight .nf { color: #0000FF } /* Name.Function */ 44 | .highlight .nl { color: #767600 } /* Name.Label */ 45 | .highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 46 | .highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ 47 | .highlight .nv { color: #19177C } /* Name.Variable */ 48 | .highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 49 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 50 | .highlight .mb { color: #666666 } /* Literal.Number.Bin */ 51 | .highlight .mf { color: #666666 } /* Literal.Number.Float */ 52 | .highlight .mh { color: #666666 } /* Literal.Number.Hex */ 53 | .highlight .mi { color: #666666 } /* Literal.Number.Integer */ 54 | .highlight .mo { color: #666666 } /* Literal.Number.Oct */ 55 | .highlight .sa { color: #BA2121 } /* Literal.String.Affix */ 56 | .highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ 57 | .highlight .sc { color: #BA2121 } /* Literal.String.Char */ 58 | .highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ 59 | .highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ 60 | .highlight .s2 { color: #BA2121 } /* Literal.String.Double */ 61 | .highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ 62 | .highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ 63 | .highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ 64 | .highlight .sx { color: #008000 } /* Literal.String.Other */ 65 | .highlight .sr { color: #A45A77 } /* Literal.String.Regex */ 66 | .highlight .s1 { color: #BA2121 } /* Literal.String.Single */ 67 | .highlight .ss { color: #19177C } /* Literal.String.Symbol */ 68 | .highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ 69 | .highlight .fm { color: #0000FF } /* Name.Function.Magic */ 70 | .highlight .vc { color: #19177C } /* Name.Variable.Class */ 71 | .highlight .vg { color: #19177C } /* Name.Variable.Global */ 72 | .highlight .vi { color: #19177C } /* Name.Variable.Instance */ 73 | .highlight .vm { color: #19177C } /* Name.Variable.Magic */ 74 | .highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/_static/seg_adj.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/_static/seg_adj.mp4 -------------------------------------------------------------------------------- /docs/ch4_adv/arbitrary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Deal with Arbitrary Field Layout — GRID 1.2.19 documentation 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 73 | 74 |
78 | 79 |
80 |
81 |
82 | 89 |
90 |
91 |
92 |
93 | 94 | 95 | 118 |
119 |

Deal with Arbitrary Field Layout

120 |
121 | ../_images/input.png 122 |
123 |

Example of an arbitrary layout

124 |
125 |
126 |

An arbitrary field layout is a type of plot arrangement that 127 | there’s no consistant number of plots exisitng in a row/column. 128 | To deal with such scenario, users have to additionally 129 | assign centroids for the extra plots. Make sure every plot in the 130 | image get assigned a centroid, and it’s ok to have some centroids 131 | occupying empty space without plot.

132 |
133 | ../_images/centroid.png 134 |
135 |

The way to define centroids for an arbitrary layout

136 |
137 |
138 |
139 | ../_images/seg.png 140 |
141 |

GRID will automatically detect whether the centroid has plot occupied or not, 142 | shrinking the space of the empty spots and 143 | expanding borders for ones that really cover plots.

144 |
145 | 146 | 147 |
148 |
149 |
153 | 154 |
155 | 156 |
157 |

© Copyright 2022, Chun-Peng James Chen.

158 |
159 | 160 | Built with Sphinx using a 161 | theme 162 | provided by Read the Docs. 163 | 164 | 165 |
166 |
167 |
168 |
169 |
170 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /docs/ch4_adv/dl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Generate Datasets for Deep Learning — GRID 1.2.19 documentation 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 72 | 73 |
77 | 78 |
79 |
80 |
81 |
    82 |
  • »
  • 83 |
  • Generate Datasets for Deep Learning
  • 84 |
  • 85 | View page source 86 |
  • 87 |
88 |
89 |
90 |
91 |
92 | 93 | 94 | 117 |
118 |

Generate Datasets for Deep Learning

119 |

Will be done by the end of August 2020.

120 |
121 | 122 | 123 |
124 |
125 |
128 | 129 |
130 | 131 |
132 |

© Copyright 2022, Chun-Peng James Chen.

133 |
134 | 135 | Built with Sphinx using a 136 | theme 137 | provided by Read the Docs. 138 | 139 | 140 |
141 |
142 |
143 |
144 |
145 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /docs/demo/features.rst: -------------------------------------------------------------------------------- 1 | Getting Started with Sphinx 2 | =========================== 3 | 4 | .. meta:: 5 | :description lang=en: Get started writing technical documentation with Sphinx and publishing to Read the Docs. 6 | 7 | 8 | Sphinx is a powerful documentation generator that 9 | has many great features for writing technical documentation including: 10 | 11 | * Generate web pages, printable PDFs, documents for e-readers (ePub), 12 | and more all from the same sources 13 | * You can use reStructuredText or :ref:`Markdown ` 14 | to write documentation 15 | * An extensive system of cross-referencing code and documentation 16 | * Syntax highlighted code samples 17 | * A vibrant ecosystem of first and third-party extensions_ 18 | 19 | .. _extensions: http://www.sphinx-doc.org/en/master/ext/builtins.html#builtin-sphinx-extensions 20 | 21 | 22 | Quick start video 23 | ----------------- 24 | 25 | This screencast will help you get started or you can 26 | :ref:`read our guide below `. 27 | 28 | .. raw:: html 29 | 30 |
31 | 32 |
33 | 34 | 35 | Quick start 36 | ----------- 37 | 38 | Assuming you have Python already, `install Sphinx`_: 39 | 40 | .. prompt:: bash $ 41 | 42 | pip install sphinx 43 | 44 | Create a directory inside your project to hold your docs: 45 | 46 | .. prompt:: bash $ 47 | 48 | cd /path/to/project 49 | mkdir docs 50 | 51 | Run ``sphinx-quickstart`` in there: 52 | 53 | .. prompt:: bash $ 54 | 55 | cd docs 56 | sphinx-quickstart 57 | 58 | This quick start will walk you through creating the basic configuration; in most cases, you 59 | can just accept the defaults. When it's done, you'll have an ``index.rst``, a 60 | ``conf.py`` and some other files. Add these to revision control. 61 | 62 | Now, edit your ``index.rst`` and add some information about your project. 63 | Include as much detail as you like (refer to the reStructuredText_ syntax 64 | or `this template`_ if you need help). Build them to see how they look: 65 | 66 | .. prompt:: bash $ 67 | 68 | make html 69 | 70 | Your ``index.rst`` has been built into ``index.html`` 71 | in your documentation output directory (typically ``_build/html/index.html``). 72 | Open this file in your web browser to see your docs. 73 | 74 | .. figure:: ../_static/images/first-steps/sphinx-hello-world.png 75 | :figwidth: 500px 76 | :target: ../_static/images/first-steps/sphinx-hello-world.png 77 | 78 | Your Sphinx project is built 79 | 80 | Edit your files and rebuild until you like what you see, then commit your changes and push to your public repository. 81 | Once you have Sphinx documentation in a public repository, you can start using Read the Docs 82 | by :doc:`importing your docs
`. 83 | 84 | .. _install Sphinx: http://sphinx-doc.org/install.html 85 | .. _reStructuredText: http://sphinx-doc.org/rest.html 86 | .. _this template: https://www.writethedocs.org/guide/writing/beginners-guide-to-docs/#id1 87 | 88 | Using Markdown with Sphinx 89 | -------------------------- 90 | 91 | You can use Markdown and reStructuredText in the same Sphinx project. 92 | We support this natively on Read the Docs, and you can do it locally: 93 | 94 | .. prompt:: bash $ 95 | 96 | pip install recommonmark 97 | 98 | Then in your ``conf.py``: 99 | 100 | .. code-block:: python 101 | 102 | extensions = ['recommonmark'] 103 | 104 | .. warning:: Markdown doesn't support a lot of the features of Sphinx, 105 | like inline markup and directives. However, it works for 106 | basic prose content. reStructuredText is the preferred 107 | format for technical documentation, please read `this blog post`_ 108 | for motivation. 109 | 110 | .. _this blog post: https://www.ericholscher.com/blog/2016/mar/15/dont-use-markdown-for-technical-docs/ 111 | 112 | 113 | External resources 114 | ------------------ 115 | 116 | Here are some external resources to help you learn more about Sphinx. 117 | 118 | * `Sphinx documentation`_ 119 | * `RestructuredText primer`_ 120 | * `An introduction to Sphinx and Read the Docs for technical writers`_ 121 | 122 | .. _Sphinx documentation: http://www.sphinx-doc.org/ 123 | .. _RestructuredText primer: http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html 124 | .. _An introduction to Sphinx and Read the Docs for technical writers: https://www.ericholscher.com/blog/2016/jul/1/sphinx-and-rtd-for-writers/ 125 | 126 | 127 | 128 | .. csv-table:: GRID Map file 129 | :file: res/demo_map.csv 130 | :header-rows: 0 131 | 132 | -------------------------------------------------------------------------------- /docs/demo/main.rst: -------------------------------------------------------------------------------- 1 | Read the Docs: Documentation Simplified 2 | ======================================= 3 | 4 | .. meta:: 5 | :description lang=en: Automate building, versioning, and hosting of your technical documentation continuously on Read the Docs. 6 | 7 | `Read the Docs`_ simplifies software documentation 8 | by building, versioning, and hosting of your docs, automatically. 9 | Think of it as *Continuous Documentation*. 10 | 11 | Never out of sync |:arrows_counterclockwise:| 12 | Whenever you push code to your favorite version control system, 13 | whether that is Git, Mercurial, Bazaar, or Subversion, 14 | Read the Docs will automatically build your docs 15 | so your code and documentation are always up-to-date. 16 | Read more about :doc:`/webhooks`. 17 | 18 | Multiple versions |:card_index_dividers:| 19 | Read the Docs can host and build multiple versions of your docs 20 | so having a 1.0 version of your docs and a 2.0 version 21 | of your docs is as easy as having a separate branch or tag in your version control system. 22 | Read more about :doc:`/versions`. 23 | 24 | Open Source and User Focused |:heartbeat:| 25 | Our code is free and `open source `_. 26 | :doc:`Our company ` is bootstrapped and 100% user focused. 27 | |org_brand| hosts documentation for over 100,000 large 28 | and small open source projects, 29 | in almost every human and computer language. 30 | |com_brand| supports hundreds of organizations with product and internal documentation. 31 | 32 | .. _Read the docs: https://readthedocs.org/ 33 | 34 | You can find out more about our all the :doc:`/features` in these pages. 35 | 36 | First steps 37 | ----------- 38 | 39 | Are you new to software documentation 40 | or are you looking to use your existing docs with Read the Docs? 41 | Learn about documentation authoring tools such as Sphinx and MkDocs 42 | to help you create fantastic documentation for your project. 43 | 44 | * **Getting started**: 45 | :doc:`With Sphinx
` | 46 | :doc:`With MkDocs ` | 47 | :doc:`Feature Overview ` | 48 | :doc:`/choosing-a-site` 49 | 50 | * **Importing your existing documentation**: 51 | :doc:`Import guide ` 52 | 53 | 54 | .. toctree:: 55 | :maxdepth: 2 56 | :hidden: 57 | :caption: First steps 58 | 59 | /intro/getting-started-with-sphinx 60 | /intro/getting-started-with-mkdocs 61 | 62 | /intro/import-guide 63 | /features 64 | /choosing-a-site 65 | 66 | 67 | Getting started with Read the Docs 68 | ----------------------------------- 69 | 70 | Learn more about configuring your automated documentation builds 71 | and some of the core features of Read the Docs. 72 | 73 | * **Overview of core features**: 74 | :doc:`Incoming webhooks ` | 75 | :doc:`/custom_domains` | 76 | :doc:`/versions` | 77 | :doc:`/downloadable-documentation` | 78 | :doc:`/hosting` | 79 | :doc:`/server-side-search` 80 | 81 | * **Connecting with GitHub, BitBucket, or GitLab**: 82 | :doc:`Connecting your VCS account ` | 83 | :doc:`VCS webhooks ` 84 | 85 | * **Read the Docs build process**: 86 | :doc:`Configuration reference ` | 87 | :doc:`Build process ` | 88 | :doc:`/badges` | 89 | 90 | * **Troubleshooting**: 91 | :doc:`/support` | 92 | :doc:`Frequently asked questions ` 93 | 94 | .. toctree:: 95 | :maxdepth: 1 96 | :hidden: 97 | :caption: Getting started 98 | 99 | /config-file/index 100 | /webhooks 101 | /custom_domains 102 | /versions 103 | /downloadable-documentation 104 | /server-side-search 105 | /hosting 106 | 107 | /connected-accounts 108 | 109 | /builds 110 | /badges 111 | 112 | /support 113 | /faq 114 | 115 | 116 | Step-by-step Guides 117 | ------------------- 118 | 119 | These guides will help walk you through specific use cases 120 | related to Read the Docs itself, documentation tools like Sphinx and MkDocs 121 | and how to write successful documentation. 122 | 123 | * :doc:`/guides/tools` 124 | * :doc:`/guides/platform` 125 | * :doc:`/guides/commercial` 126 | 127 | .. toctree:: 128 | :maxdepth: 2 129 | :hidden: 130 | :caption: Step-by-step Guides 131 | 132 | /guides/tools 133 | /guides/platform 134 | /guides/commercial 135 | 136 | Advanced features of Read the Docs 137 | ---------------------------------- 138 | 139 | Read the Docs offers many advanced features and options. 140 | Learn more about these integrations and how you can get the most 141 | out of your documentation and Read the Docs. 142 | 143 | * **Advanced project configuration**: 144 | :doc:`subprojects` | 145 | :doc:`Single version docs ` 146 | 147 | * **Multi-language documentation**: 148 | :doc:`Translations and localization ` 149 | 150 | .. TODO: Move user-defined to Getting started, they are core functionality 151 | 152 | * **Redirects**: 153 | :doc:`User defined redirects ` | 154 | :doc:`Automatic redirects ` 155 | 156 | * **Versions** 157 | :doc:`Automation rules ` 158 | 159 | * **Topic specific guides**: 160 | :doc:`How-to guides ` 161 | 162 | * **Extending Read the Docs**: 163 | :doc:`REST API ` 164 | 165 | .. toctree:: 166 | :maxdepth: 2 167 | :hidden: 168 | :glob: 169 | :caption: Advanced features 170 | 171 | subprojects 172 | single_version 173 | 174 | localization 175 | 176 | user-defined-redirects 177 | automatic-redirects 178 | 179 | automation-rules 180 | 181 | 182 | api/index 183 | 184 | 185 | The Read the Docs project and organization 186 | ------------------------------------------ 187 | 188 | Learn about Read the Docs, the project and the company, 189 | and find out how you can get involved and contribute to the development and success 190 | of Read the Docs and the larger software documentation ecosystem. 191 | 192 | * **Getting involved with Read the Docs**: 193 | :doc:`Contributing ` | 194 | :doc:`Development setup ` | 195 | :doc:`roadmap` | 196 | :doc:`Code of conduct ` 197 | 198 | * **Policies & Process**: 199 | :doc:`security` | 200 | :doc:`Privacy policy ` | 201 | :doc:`Terms of service ` | 202 | :doc:`DMCA takedown policy ` | 203 | :doc:`Policy for abandoned projects ` | 204 | :doc:`Release notes & changelog ` 205 | 206 | * **The people and philosophy behind Read the Docs**: 207 | :doc:`About Us ` | 208 | :doc:`Team ` | 209 | :doc:`Open source philosophy ` | 210 | :doc:`Our story ` 211 | 212 | * **Financial and material support**: 213 | :doc:`advertising/index` | 214 | :doc:`Sponsors ` 215 | 216 | * **Read the Docs for Business**: 217 | :doc:`Support and additional features ` 218 | 219 | * **Running your own version of Read the Docs**: 220 | :doc:`Private installations ` 221 | 222 | 223 | .. toctree:: 224 | :maxdepth: 1 225 | :hidden: 226 | :caption: About Read the Docs 227 | 228 | contribute 229 | development/index 230 | roadmap 231 | gsoc 232 | code-of-conduct 233 | 234 | security 235 | privacy-policy 236 | terms-of-service 237 | dmca/index 238 | abandoned-projects 239 | changelog 240 | 241 | about 242 | team 243 | open-source-philosophy 244 | story 245 | 246 | advertising/index 247 | sponsors 248 | 249 | commercial/index 250 | 251 | custom_installs/index 252 | 253 | 254 | 255 | 256 | 257 | 258 | .. figure:: ../_static/images/first-steps/sphinx-hello-world.png 259 | :figwidth: 500px 260 | :target: ../_static/images/first-steps/sphinx-hello-world.png 261 | 262 | Your Sphinx project is built 263 | -------------------------------------------------------------------------------- /docs/genindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Index — GRID 1.2.19 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 70 | 71 |
75 | 76 |
77 |
78 |
79 |
    80 |
  • »
  • 81 |
  • Index
  • 82 |
  • 83 |
  • 84 |
85 |
86 |
87 |
88 |
89 | 90 | 91 |

Index

92 | 93 |
94 | 95 |
96 | 97 | 98 |
99 |
100 |
101 | 102 |
103 | 104 |
105 |

© Copyright 2022, Chun-Peng James Chen.

106 |
107 | 108 | Built with Sphinx using a 109 | theme 110 | provided by Read the Docs. 111 | 112 | 113 |
114 |
115 |
116 |
117 |
118 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /docs/grid.bib: -------------------------------------------------------------------------------- 1 | @article{Chen and Zhang:2020, 2 | author = {Chunpeng James Chen and Zhiwu Zhang}, 3 | title = {GRID: A Python Package for Field Plot Phenotyping Using Aerial Images}, 4 | month = may, 5 | year = 2020, 6 | journal = {Remote Sensing}, 7 | volume = 12, 8 | issue = 11, 9 | pages = 1697 10 | doi = {10.3390/rs12111697}, 11 | url = {https://doi.org/10.3390/rs12111697} 12 | } -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/docs/objects.inv -------------------------------------------------------------------------------- /docs/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Search — GRID 1.2.19 documentation 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 73 | 74 |
78 | 79 |
80 |
81 |
82 |
    83 |
  • »
  • 84 |
  • Search
  • 85 |
  • 86 |
  • 87 |
88 |
89 |
90 |
91 |
92 | 93 | 100 | 101 | 102 |
103 | 104 |
105 | 106 |
107 |
108 |
109 | 110 |
111 | 112 |
113 |

© Copyright 2022, Chun-Peng James Chen.

114 |
115 | 116 | Built with Sphinx using a 117 | theme 118 | provided by Read the Docs. 119 | 120 | 121 |
122 |
123 |
124 |
125 |
126 | 131 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /grid/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = "Chun-Peng James Chen " 2 | __version__ = "1.3.13" 3 | __update__ = "November 27, 2024" 4 | -------------------------------------------------------------------------------- /grid/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/__init__.pyc -------------------------------------------------------------------------------- /grid/__main__.py: -------------------------------------------------------------------------------- 1 | # imports 2 | import subprocess 3 | import json 4 | import sys 5 | from urllib import request 6 | from pkg_resources import parse_version 7 | 8 | # basic imports 9 | import sys 10 | import os 11 | 12 | # 3rd party imports 13 | from PyQt6.QtWidgets import QApplication 14 | from PyQt6.QtCore import QTimer 15 | import qdarkstyle 16 | 17 | 18 | # self imports 19 | from grid.gridGUI import * 20 | from grid.__init__ import __version__, __update__ 21 | 22 | 23 | def main(): 24 | if "__main__" not in sys.argv[0]: 25 | # prevent from re-show welcome message in gridGUI 26 | # welcome message 27 | print("~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~") 28 | print(" Welcome to GRID Ver.%s " % __version__) 29 | print("~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~") 30 | print("Author : James Chen ") 31 | print("Last update : %s " % __update__) 32 | print("User manual : https://poissonfish.github.io/GRID/") 33 | print("~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~") 34 | print("Recent update ") 35 | print(" - Now you can launch GRID by typing 'GRID' in terminal") 36 | print("~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~") 37 | 38 | # self update 39 | try: 40 | url = "https://pypi.python.org/pypi/photo_grid/json" 41 | releases = json.loads(request.urlopen(url).read())["releases"] 42 | new_version = sorted(releases, key=parse_version, reverse=True)[0] 43 | if __version__ != new_version: 44 | # Dialog 45 | ans = None 46 | bol_ans = None 47 | possible_pos_ans = ["y", "Y", "yes"] 48 | possible_neg_ans = ["n", "N", "no"] 49 | 50 | while bol_ans is None: 51 | ans = input( 52 | "A newer version of GRID (ver. %s) is now available, upgrade? (y/n) " 53 | % new_version 54 | ) 55 | if ans in possible_pos_ans: 56 | bol_ans = True 57 | elif ans in possible_neg_ans: 58 | bol_ans = False 59 | 60 | if bol_ans: 61 | subprocess.check_call( 62 | [ 63 | sys.executable, 64 | "-m", 65 | "pip", 66 | "install", 67 | "photo_grid==%s" % new_version, 68 | "--upgrade", 69 | ] 70 | ) 71 | print("\n") 72 | print("~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~") 73 | print(" Please re-launch GRID to finish the update") 74 | print("~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~") 75 | print("\n") 76 | quit() 77 | except Exception: 78 | print("\n") 79 | print("~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~") 80 | print(" Sorry, we currently have issue updating your GRID.") 81 | print("~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~*~~~~~~~~~") 82 | print("\n") 83 | 84 | app = QApplication(sys.argv) 85 | if "--light" not in sys.argv: 86 | app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5()) 87 | 88 | grid = GRID_GUI() 89 | timer = QTimer() 90 | timer.timeout.connect(lambda: None) 91 | timer.start(100) 92 | app.exec() 93 | 94 | 95 | if __name__ == "__main__": 96 | main() 97 | -------------------------------------------------------------------------------- /grid/archive/0611_shapefile.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import cv2 3 | import numpy as np 4 | from io import BytesIO as StringIO 5 | import shapefile 6 | import numpy 7 | import rasterio 8 | import rasterio.mask 9 | import os 10 | 11 | os.chdir("..") 12 | sys.path 13 | sys.path.remove("/Users/jameschen/Dropbox/photo_grid/grid") 14 | os.chdir("/Users/jameschen/Dropbox/photo_grid/") 15 | import grid as gd 16 | 17 | # 18 | os.chdir("/Users/jameschen/Dropbox/photo_grid/test/Jacob") 19 | f_tif = rasterio.open('RGB-integer.tif') 20 | f_sf = shapefile.Reader("jacob.shp") 21 | 22 | row_max = -1 23 | col_max = -1 24 | pts_crop = [(0, 0), (0, 0), (0, 0), (0, 0)] 25 | 26 | rec = f_sf.shapeRecords() 27 | n_rec = len(rec) 28 | 29 | for i in range(n_rec): 30 | # find four corners 31 | # [[W, N], [E, N], [E, S], [W, S]] 32 | # (0, 0), (0, m), (m, m), (m, 0) 33 | pts = rec[i].shape.points 34 | attr = rec[i].record 35 | row = attr[1] 36 | col = attr[2] 37 | 38 | if row == 0 and col == 0: 39 | # NW 40 | pts_crop[0] = pts[0] 41 | elif row == 0 and col > col_max: 42 | # NE 43 | pts_crop[1] = pts[1] 44 | elif row > row_max and col == 0: 45 | # SW 46 | pts_crop[3] = pts[3] 47 | elif row > row_max and col > col_max: 48 | # SE 49 | pts_crop[2] = pts[2] 50 | 51 | pts_crop = [invAffine(pts_crop[i], f_tif.transform) for i in range(4)] 52 | 53 | 54 | pts = [invAffine(pts[i], f_tif.transform) for i in range(4)] 55 | ptPlt = np.array(pts_crop).transpose() 56 | ptSm = np.array(pts).transpose() 57 | plt.scatter(ptPlt[0], ptPlt[1]) 58 | plt.scatter(ptSm[0], ptSm[1]) 59 | 60 | 61 | def invAffine(pt, affine): 62 | """ 63 | convert GIS coordinate to (x, y) 64 | 65 | NOTE: 66 | # a = width of a pixel 67 | # b = row rotation(typically zero) 68 | # c = x-coordinate of the center of the upper-left pixel 69 | # d = column rotation(typically zero) 70 | # e = height of a pixel(typically negative) 71 | # f = y-coordinate of the center of the upper-left pixel 72 | """ 73 | # transformation 74 | xg = (pt[0] - affine[2]) / affine[0] 75 | yg = (pt[1] - affine[5]) / affine[4] 76 | 77 | # return 78 | return (xg, yg) 79 | 80 | 81 | 82 | 83 | pt = (30, 50) 84 | xy = affine * pt 85 | invAffine(xy, affine) 86 | 87 | 88 | 89 | 90 | 91 | # generate sorted source point 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /grid/archive/0615_zz_image.r: -------------------------------------------------------------------------------- 1 | ############ ############ ############ 2 | # Date: Jun 15, 2020 3 | # Author: Chunpeng James Chen 4 | # Description: 5 | # It's a code showing how to load an image into a r-compatible data.frame 6 | ############ ############ ############ 7 | 8 | # DEPENDENCIES 9 | ############ ############ ############ 10 | 11 | # install.packages("imager") 12 | library(imager) 13 | 14 | # FUNCTIONS 15 | ############ ############ ############ 16 | 17 | # Description 18 | # convert imager object to data.frame (cols: x, y, c1, c2...) 19 | # Input 20 | # image: image object loaded from library imager 21 | # Output 22 | # df: a data.frame containing information of xy coordinates and channel values 23 | img2df <- function(image) { 24 | w <- dim(image)[1] 25 | h <- dim(image)[2] 26 | d <- 3 27 | df <- matrix(image[,, 1, 1:d], nrow=h * w, ncol=d) %>% 28 | data.frame(x=rep(1 : w, length=w * h), 29 | y=rep(1 : h, each=w),.) 30 | names(df) <- c("x", "y", paste0("c", 1:d)) 31 | return (df) 32 | } 33 | 34 | # Description 35 | # convert an image file to data.frame 36 | # Input 37 | # filename: file path to where the file is stored 38 | # Output 39 | # df: a data.frame containing information of xy coordinates and channel values 40 | file2df <- function(filename) { 41 | filename %>% 42 | load.image() %>% 43 | img2df() %>% 44 | return() 45 | } 46 | 47 | # Description 48 | # convert a data.frame (col: x, y, c1, c2 ...) to a imager object 49 | # Input 50 | # df: a dataframe containing information of xy coordinates and channel values 51 | # Output 52 | # img: a imagery object which is ready to be ploted 53 | df2img <- function(df) { 54 | w = max(df$x) 55 | h = max(df$y) 56 | d = ncol(df) - 2 57 | subset(df, select=-c(x, y)) %>% 58 | as.matrix() %>% 59 | as.vector() %>% 60 | as.cimg(dim=c(w, h, 1, d)) %>% 61 | return() 62 | } 63 | 64 | # Description 65 | # save an imager object as an image file 66 | # Input 67 | # image: imager object or 2d matrix (single channel image) 68 | # filename: a path where the image will be saved 69 | # Output 70 | # NA 71 | img2file <- function(image, filename) { 72 | cimg <- as.cimg(image) 73 | save.image(cimg, filename) 74 | } 75 | 76 | # SAMPLE CODE 77 | ############ ############ ############ 78 | # specify file name 79 | filename <- "test.png" 80 | 81 | # get data.frame from a file (xy coordinates + 3 channels) 82 | # OPTION 1 83 | img <- load.image(filename) 84 | df <- img2df(img) 85 | # OPTION 2 86 | df <- file2df(filename) 87 | 88 | # get PCA data.frame (xy coordinates + PCs) 89 | mat_pc <- prcomp(df[, 3 : (3 + 2)], scale=T, center=T)$x 90 | df_pc <- data.frame(df[, c("x", "y")], mat_pc) 91 | 92 | # get PCA image 93 | img_pc <- df2img(df_pc) 94 | 95 | # plot original image 96 | plot(img) 97 | 98 | # plot index: (c1 - c2) / (c1 + c2) 99 | c1 = img[,, 1] 100 | c2 = img[,, 2] 101 | img_index = (c1 - c2) / (c1 + c2) 102 | heatmap(img_index, Rowv=NA, Colv=NA) 103 | 104 | # plot all 3 PCs 105 | plot(img_pc) 106 | 107 | # plot the 2nd PC 108 | heatmap(img_pc[, , 2], Rowv=NA, Colv=NA) 109 | 110 | # plot (pc1-pc2)/(pc1+pc2) 111 | pc1 = img_pc[, , 1] 112 | pc2 = img_pc[, , 2] 113 | img_index_pc = (pc1 - pc2) / (pc1 + pc2) 114 | heatmap(img_index_pc, Rowv=NA, Colv=NA) 115 | 116 | # save images 117 | img2file(img_index, "index.png") 118 | img2file(img_pc, "pc.png") 119 | img2file(img_pc[,, 2], "pc2.png") 120 | -------------------------------------------------------------------------------- /grid/archive/CPU_Agent.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import QRect 2 | from .Misc import Dir 3 | 4 | class Agent(): 5 | def __init__(self, name, row, col, imgH, imgW): 6 | ''' 7 | ''' 8 | self.name = name 9 | self.row, self.col = row, col 10 | self.imgH, self.imgW = int(imgH), int(imgW) 11 | self.y, self.x = 0, 0 12 | self.y_reset, self.x_reset = 0, 0 13 | self.pre_rg_W, self.pre_rg_H = range(0), range(0) 14 | self.border, self.border_reset = dict(), dict() 15 | for dir in list([Dir.NORTH, Dir.EAST, Dir.SOUTH, Dir.WEST]): 16 | self.border[dir.name] = 0 17 | self.border_reset[dir.name] = 0 18 | def get_col(self): 19 | ''' 20 | ''' 21 | return self.col 22 | def get_row(self): 23 | ''' 24 | ''' 25 | return self.row 26 | def get_coordinate(self): 27 | ''' 28 | ''' 29 | return self.x, self.y 30 | def get_pre_dim(self, isHeight=True): 31 | ''' 32 | ''' 33 | return self.pre_rg_H if isHeight else self.pre_rg_W 34 | def get_border(self, dir): 35 | return self.border[dir.name] 36 | def get_rect(self): 37 | x = self.get_border(Dir.WEST) 38 | y = self.get_border(Dir.NORTH) 39 | w = self.get_border(Dir.EAST) - x 40 | h = self.get_border(Dir.SOUTH) - y 41 | return QRect(x, y, w, h) 42 | def get_score_area(self, dir, img): 43 | ''' 44 | Will ragne from 0 to 1 45 | ''' 46 | isH = dir.value%2 # E->1, S->0 47 | rg = self.get_pre_dim(isHeight=isH) 48 | bd = self.get_border(dir) 49 | # print("==== row:%d, col:%d ====" %(self.row, self.col)) 50 | # print(rg) 51 | # print(bd) 52 | return img[rg, bd].mean() if isH else img[bd, rg].mean() 53 | def get_score_grid(self, dir): 54 | ''' 55 | Will ragne from 0 to 1 56 | ''' 57 | isWE = dir.value%2 # is W, E or N, S 58 | pt_center = self.x if isWE else self.y 59 | pt_cur = self.get_border(dir) 60 | return abs(pt_cur-pt_center) 61 | def set_coordinate(self, x, y): 62 | ''' 63 | ''' 64 | self.x, self.y = int(x), int(y) 65 | self.x_reset, self.y_reset = int(x), int(y) 66 | self.set_border(Dir.NORTH, y) 67 | self.set_border(Dir.SOUTH, y) 68 | self.set_border(Dir.WEST, x) 69 | self.set_border(Dir.EAST, x) 70 | def set_pre_dim(self, rg): 71 | ''' 72 | ''' 73 | self.pre_rg_W = range(rg['WEST'], rg['EAST']) 74 | self.pre_rg_H = range(rg['NORTH'], rg['SOUTH']) 75 | self.x = int((rg['EAST']+rg['WEST'])/2) 76 | self.y = int((rg['NORTH']+rg['SOUTH'])/2) 77 | self.x_reset, self.y_reset = self.x, self.y 78 | for dir in list([Dir.NORTH, Dir.WEST, Dir.SOUTH, Dir.EAST]): 79 | self.border_reset[dir.name] = self.border[dir.name] 80 | def set_border(self, dir, value): 81 | ''' 82 | ''' 83 | self.border[dir.name] = int(value) 84 | self.check_border() 85 | def update_border(self, dir, value): 86 | ''' 87 | ''' 88 | self.border[dir.name] += int(value) 89 | self.check_border() 90 | def update_coordinate(self, val, axis=0): 91 | ''' 92 | ''' 93 | val = int(val) 94 | if axis==0: 95 | self.y += val 96 | self.border[Dir.NORTH.name] += val 97 | self.border[Dir.SOUTH.name] += val 98 | elif axis==1: 99 | self.x += val 100 | self.border[Dir.WEST.name] += val 101 | self.border[Dir.EAST.name] += val 102 | self.check_border() 103 | def check_border(self): 104 | if self.border[Dir.NORTH.name]<0: 105 | self.border[Dir.NORTH.name] = 0 106 | if self.border[Dir.WEST.name]<0: 107 | self.border[Dir.WEST.name] = 0 108 | if self.border[Dir.SOUTH.name]>=self.imgH: 109 | self.border[Dir.SOUTH.name] = self.imgH-1 110 | if self.border[Dir.EAST.name]>=self.imgW: 111 | self.border[Dir.EAST.name] = self.imgW-1 112 | def set_save(self, save=False): 113 | "do nothing" 114 | def reset_coordinate(self): 115 | self.x = self.x_reset 116 | self.y = self.y_reset 117 | self.reset_border() 118 | def reset_border(self): 119 | for dir in list([Dir.NORTH, Dir.WEST, Dir.SOUTH, Dir.EAST]): 120 | self.border[dir.name] = self.border_reset[dir.name] 121 | -------------------------------------------------------------------------------- /grid/archive/Misc.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | from enum import Enum 4 | from PyQt5.QtWidgets import * 5 | from PyQt5.QtCore import * 6 | from PyQt5.QtGui import * 7 | 8 | 9 | 10 | def get_peak(img, map, n_smooth=100, axis=0): 11 | ''' 12 | ''' 13 | from scipy.signal import find_peaks 14 | import numpy as np 15 | # compute signal 16 | ls_mean = img.mean(axis=(not axis)*1) # 0:nrow 17 | # gaussian smooth signal 18 | for i in range(n_smooth): 19 | ls_mean = np.convolve(np.array([1, 2, 4, 2, 1])/10, ls_mean, mode='same') 20 | peaks, _ = find_peaks(ls_mean) 21 | if map is not None: 22 | if len(peaks) > map.shape[axis]: 23 | while len(peaks) > map.shape[axis]: 24 | ls_diff = [peaks[i+1]-peaks[i] for i in range(len(peaks)-1)] 25 | idx_diff = np.argmin(ls_diff) 26 | idx_kick = idx_diff if (ls_mean[peaks[idx_diff]] < ls_mean[peaks[idx_diff+1]]) else (idx_diff+1) 27 | peaks = np.delete(peaks, idx_kick) 28 | elif len(peaks) < map.shape[axis]: 29 | while len(peaks) < map.shape[axis]: 30 | ls_diff = [peaks[i+1]-peaks[i] for i in range(len(peaks)-1)] 31 | idx_diff = np.argmax(ls_diff) 32 | peak_insert = (peaks[idx_diff]+peaks[idx_diff+1])/2 33 | peaks = np.sort(np.append(peaks, int(peak_insert))) 34 | return peaks, ls_mean 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /grid/archive/archive.py: -------------------------------------------------------------------------------- 1 | def read_jpg(filename): 2 | from PIL import Image 3 | Image.MAX_IMAGE_PIXELS = 1e+9 4 | img = np.array(Image.open(filename)).astype(np.uint8) 5 | return img 6 | 7 | 8 | def read_tiff(filename, bands=None, xBSize=5000, yBSize=5000): 9 | '''import''' 10 | import gdal 11 | from tqdm import tqdm_gui 12 | '''program''' 13 | ds = gdal.Open(filename) 14 | gdal.UseExceptions() 15 | nrow = ds.RasterYSize 16 | ncol = ds.RasterXSize 17 | if bands == None: 18 | bands = range(ds.RasterCount) 19 | data = np.zeros((nrow, ncol, len(bands))) 20 | for b in bands: 21 | band = ds.GetRasterBand(b+1) 22 | for i in tqdm_gui(range(0, nrow, yBSize), desc="Channel %d/%d" % (b, len(bands)-1), leave=False): 23 | if i + yBSize < nrow: 24 | numRows = yBSize 25 | else: 26 | numRows = nrow - i 27 | for j in range(0, ncol, xBSize): 28 | if j + xBSize < ncol: 29 | numCols = xBSize 30 | else: 31 | numCols = ncol - j 32 | data[i:(i+numRows), j:(j+numCols), 33 | b] = band.ReadAsArray(j, i, numCols, numRows) 34 | return data.astype(np.uint8) 35 | 36 | 37 | def write_tiff(array, outname): 38 | driver = gdal.GetDriverByName("GTiff") 39 | out_info = driver.Create(outname+".tif", 40 | array.shape[1], # x 41 | array.shape[0], # y 42 | array.shape[2], # channels 43 | gdal.GDT_Byte) 44 | for i in range(array.shape[2]): 45 | out_info.GetRasterBand(i+1).WriteArray(array[:, :, i]) 46 | -------------------------------------------------------------------------------- /grid/archive/grid2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from PyQt5.QtWidgets import * 3 | from PyQt5.QtCore import * 4 | from PyQt5.QtGui import * 5 | from .CPU_Agent import * 6 | from .CPU_Field import * 7 | from .GPU_Input import * 8 | from .GPU_Cropper import * 9 | from .GPU_Kmeaner import * 10 | from .GPU_Anchor import * 11 | from .GPU_Output import * 12 | 13 | class GRID(QMainWindow): 14 | def __init__(self): 15 | super().__init__() 16 | self.setStyleSheet(""" 17 | QWidget {\ 18 | font: 20pt Trebuchet MS 19 | } 20 | QGroupBox::title{ 21 | subcontrol-origin: margin; 22 | left: 10px; 23 | padding: 0 3px 0 3px; 24 | } 25 | QGroupBox { 26 | border: 1px solid gray; 27 | border-radius: 9px; 28 | margin-top: 0.5em; 29 | } 30 | """) 31 | '''attr''' 32 | # GUI components 33 | self.pn_content = QWidget() 34 | self.pn_main = QStackedWidget() 35 | self.pn_navi = QWidget() 36 | self.bt_next = QPushButton() 37 | self.bt_back = QPushButton() 38 | self.Layout = None 39 | # params 40 | self.params = dict() 41 | # image-related 42 | self.img_raw = None 43 | self.img_crop = None 44 | self.img_bin = None 45 | self.k_center = None 46 | # info-=- 47 | self.title = "GRID" 48 | self.width = 1440 49 | self.height = 900 50 | '''initialize UI''' 51 | self.initUI() 52 | def initUI(self): 53 | self.setWindowTitle(self.title) 54 | self.setMinimumSize(self.width, self.height) 55 | '''first input''' 56 | self.show_input() 57 | '''set up windows''' 58 | center = QApplication.desktop().availableGeometry().center() 59 | rect = self.geometry() 60 | rect.moveCenter(center) 61 | self.setGeometry(rect) 62 | def show_input(self, isNewImg=True): 63 | '''input panel''' 64 | if isNewImg: 65 | self.pn_main.addWidget(Panel_Input()) 66 | else: 67 | self.pn_main.removeWidget(self.pn_main.widget(Panels.CROPPER.value)) 68 | self.pn_main.setCurrentIndex(Panels.INPUT.value) 69 | '''navigation bar''' 70 | self.assemble_navigation(name_next="Load Files ->", oneSide=True) 71 | self.bt_next.clicked.connect(lambda: self.show_cropper(isNewImg=True)) 72 | '''finalize''' 73 | self.assemble_and_show() 74 | def show_cropper(self, isNewImg=True): 75 | '''input panel''' 76 | if isNewImg: 77 | self.params['raw'], self.params['map'] = self.pn_main.widget(Panels.INPUT.value).get_img() 78 | self.pn_main.addWidget(Panel_Cropper(np_img=self.params['raw'])) 79 | else: 80 | self.pn_main.removeWidget(self.pn_main.widget(Panels.KMEANER.value)) 81 | self.pn_main.setCurrentIndex(Panels.CROPPER.value) 82 | '''navigation bar''' 83 | self.assemble_navigation() 84 | self.bt_back.clicked.connect(lambda: self.show_input(isNewImg=False)) 85 | self.bt_next.clicked.connect(lambda: self.show_kmeaner(isNewImg=True)) 86 | '''finalize''' 87 | self.assemble_and_show() 88 | def show_kmeaner(self, isNewImg=True): 89 | '''input panel''' 90 | if isNewImg: 91 | self.params['crop'] = self.pn_main.widget(Panels.CROPPER.value).get_img() 92 | self.pn_main.addWidget(Panel_Kmeaner(np_img=self.params['crop'])) 93 | else: 94 | self.pn_main.removeWidget(self.pn_main.widget(Panels.ANCHOR.value)) 95 | self.pn_main.setCurrentIndex(Panels.KMEANER.value) 96 | '''navigation bar''' 97 | self.assemble_navigation() 98 | self.bt_back.clicked.connect(lambda: self.show_cropper(isNewImg=False)) 99 | self.bt_next.clicked.connect(lambda: self.show_anchor(isNewImg=True)) 100 | '''finalize''' 101 | self.assemble_and_show() 102 | def show_anchor(self, isNewImg=True): 103 | '''input panel''' 104 | if isNewImg: 105 | self.params['crop'], self.params['k'], self.params['bin'], self.params['ch_nir'], self.params['ch_red'], self.params['ls_bin'] = self.pn_main.widget(Panels.KMEANER.value).get_img() 106 | self.pn_main.addWidget(Panel_Anchor(img=self.params['bin'], map=self.params['map'])) 107 | else: 108 | self.pn_main.removeWidget(self.pn_main.widget(Panels.OUTPUT.value)) 109 | self.pn_main.setCurrentIndex(Panels.ANCHOR.value) 110 | '''navigation bar''' 111 | self.assemble_navigation() 112 | self.bt_back.clicked.connect(lambda: self.show_kmeaner(isNewImg=False)) 113 | self.bt_next.clicked.connect(lambda: self.show_output(isNewImg=True)) 114 | '''finalize''' 115 | self.assemble_and_show() 116 | def show_output(self, isNewImg=True): 117 | '''input panel''' 118 | if isNewImg: 119 | self.params['anchors'], self.params['nc'], self.params['nr'] = self.pn_main.widget(Panels.ANCHOR.value).get_anchors() 120 | self.pn_main.addWidget(Panel_Output(**self.params)) 121 | self.pn_main.setCurrentIndex(Panels.OUTPUT.value) 122 | # test 123 | self.test() 124 | # test 125 | '''navigation bar''' 126 | self.assemble_navigation(name_next="Finish") 127 | self.bt_back.clicked.connect(lambda: self.show_anchor(isNewImg=False)) 128 | self.bt_next.clicked.connect(self.finish) 129 | '''finalize''' 130 | self.assemble_and_show() 131 | def finish(self): 132 | msgBox = QMessageBox() 133 | msgBox.setIcon(QMessageBox.Information) 134 | msgBox.setText("Start another job?") 135 | msgBox.setWindowTitle("Finish") 136 | msgBox.setStandardButtons(QMessageBox.Cancel | QMessageBox.Yes | QMessageBox.Save) 137 | returnValue = msgBox.exec() 138 | if returnValue == QMessageBox.Yes: 139 | self.pn_main.widget(Panels.OUTPUT.value).output() 140 | self.show_input() 141 | elif returnValue == QMessageBox.Save: 142 | self.pn_main.widget(Panels.OUTPUT.value).output() 143 | self.show_output(isNewImg=False) 144 | def assemble_navigation(self, name_next="Next ->", name_back="<- Back", oneSide=False): 145 | self.pn_navi = QWidget() 146 | self.bt_next = QPushButton(name_next) 147 | self.bt_back = QPushButton(name_back) 148 | layout_navi = QHBoxLayout() 149 | if oneSide: 150 | layout_navi.addStretch(1) 151 | else: 152 | layout_navi.addWidget(self.bt_back) 153 | layout_navi.addWidget(self.bt_next) 154 | self.pn_navi.setLayout(layout_navi) 155 | def assemble_and_show(self): 156 | self.Layout = QVBoxLayout() 157 | self.Layout.addWidget(self.pn_main, Qt.AlignCenter) 158 | self.Layout.addWidget(self.pn_navi) 159 | self.pn_content = QWidget() 160 | self.pn_content.setLayout(self.Layout) 161 | self.setCentralWidget(self.pn_content) 162 | self.show() 163 | def test(self): 164 | import json 165 | with open('anchors', 'w') as fout: 166 | json.dump(self.params['anchors'], fout) 167 | np.save("img_crop", self.params['crop']) 168 | np.save("img_bin", self.params['bin']) 169 | np.save("map", self.params['map']) 170 | np.save("img_k", self.params['k']) 171 | np.save("ls_bin", self.params['ls_bin']) 172 | print("nc:%d"%(self.params['nc'])) 173 | print("nr:%d"%(self.params['nr'])) 174 | -------------------------------------------------------------------------------- /grid/archive/test.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import sys 3 | import grid as gd 4 | import pandas as pd 5 | import numpy as np 6 | 7 | import matplotlib.pyplot as plt 8 | from matplotlib import patches 9 | from matplotlib.lines import Line2D 10 | # 3rd party imports 11 | from PyQt5.QtWidgets import * 12 | from PyQt5.QtCore import * 13 | from PyQt5.QtGui import * 14 | 15 | from grid.gridGUI import * 16 | 17 | 18 | grid = gd.GRID() 19 | grid.loadData("/Users/jameschen/Dropbox/UAV/James/Alfalfa/20190505RGB_RDNIR_H.tif") 20 | grid.cropImg() 21 | 22 | grid.binarizeImg(k=3, lsSelect=[0]) 23 | grid.imgs.get("crop") 24 | 25 | # grid.loadData("/Users/jameschen/Dropbox/James Chen/GRID/Modeling/Rhombus.jpg") 26 | grid.loadData("/Users/jameschen/demo.png", pathMap="/Users/jameschen/demo.csv") 27 | grid.binarizeImg(k=3, lsSelect=[0]) 28 | grid.findPlots() 29 | grid.cpuSeg(outplot=False) 30 | 31 | grid.save() 32 | 33 | dt_TE = grid.getDfIndex(ch_1=3, ch_2=0, isContrast=True, name_index="ttt") 34 | dt_org = grid.map.dt 35 | 36 | dt_TE[:10] 37 | dt_org[:10] 38 | dt_TE['var'] 39 | dt_org['var'] 40 | pd.merge(dt_org, dt_TE, on='var', how='left') 41 | 42 | 43 | # find best angle 44 | # plot 45 | grid = gd.GRID() 46 | grid.loadData("/Users/jameschen/Dropbox/James Chen/GRID/Prototype/Rhombus Pumptree.jpg") 47 | grid.binarizeImg(k=5, lsSelect=[3, 4], valShad=0, valSmth=3, outplot=True) 48 | grid.findPlots(nCol=5, nRow=6, nSmooth=0, outplot=True) 49 | grid.map.angles 50 | plt.imshow(grid.map.imgs[1]) 51 | plt.plot(grid.map.imgs[1].sum(axis=0)) 52 | plt.show() 53 | 54 | signal = grid.map.imgs[1].mean(axis=0) 55 | # gaussian smooth 56 | for i in range(3): 57 | signal = np.convolve( 58 | np.array([1, 2, 4, 2, 1])/10, signal, mode='same') 59 | peaks, _ = find_peaks(signal) 60 | peaks 61 | plt.plot(signal) 62 | plt.show() 63 | 64 | 65 | app = QApplication(sys.argv) 66 | grid = PnAnchor(grid) 67 | 68 | 69 | 70 | # TEST code 71 | # cp -r grid/* env/lib/python3.6/site-packages/grid/ | python3 -m grid 72 | 73 | # GUI TEST 74 | grid = gd.GRID() 75 | grid.loadData( 76 | "/Users/jameschen/Dropbox/James_Git/FN/data/demo.png") 77 | grid.binarizeImg(k=3, lsSelect=[0, 1], valShad=0, valSmth=0, outplot=False) 78 | app = QApplication(sys.argv) 79 | grid = PnAnchor(grid) 80 | 81 | 82 | # ZZ FINAL TEST 83 | grid = gd.GRID() 84 | grid.loadData( 85 | "/Users/jameschen/Dropbox/James Chen/GRID/Prototype/F5SAS_Overview.jpg") 86 | grid.binarizeImg(k=9, lsSelect=[0,1,2,3], valShad=0, valSmth=10, outplot=True) 87 | grid.findPlots(outplot=True) 88 | grid.cpuSeg(outplot=True) 89 | 90 | 91 | 92 | grid = gd.GRID() 93 | grid.loadData( 94 | "/Users/jameschen/Dropbox/James_Git/FN/data/demo.png") 95 | grid.binarizeImg(k=3, lsSelect=[0, 1], valShad=0, valSmth=0, outplot=True) 96 | grid.findPlots(outplot=True) 97 | grid.cpuSeg(outplot=True) 98 | 99 | grid = gd.GRID() 100 | grid.loadData("/Users/jameschen/Dropbox/James Chen/GRID/Modeling/Rhombus.jpg") 101 | grid.binarizeImg(k=5, lsSelect=[4], valShad=0, valSmth=0, outplot=False) 102 | grid.findPlots(outplot=False) 103 | 104 | 105 | grid.cpuSeg(outplot=True) 106 | 107 | 108 | img = grid.imgs.get("binSeg") 109 | 110 | def rotateBinNdArray(img, angel): 111 | # create border for the image 112 | img[:, 0] = 1 113 | img[0, :] = 1 114 | img[:, -1] = 1 115 | img[-1, :] = 1 116 | 117 | # padding 118 | sizePad = max(img.shape) 119 | imgP = np.pad(img, [sizePad, sizePad], 'constant') 120 | 121 | # rotate 122 | pivot = tuple((np.array(imgP.shape[:2])/2).astype(int)) 123 | matRot = cv2.getRotationMatrix2D(pivot, angel, 1.0) 124 | imgR = cv2.warpAffine( 125 | imgP.astype(np.float32), matRot, imgP.shape, flags=cv2.INTER_LINEAR).astype(np.int8) 126 | 127 | # crop 128 | sigX = np.where(imgR.sum(axis=0)!=0)[0] 129 | sigY = np.where(imgR.sum(axis=1)!=0)[0] 130 | imgC = imgR[sigY[0]:sigY[-1], sigX[0]:sigX[-1]] 131 | 132 | # return 133 | return imgC 134 | 135 | angel = 60 136 | imgR = rotateBinNdArray(img, angel) 137 | ictA = gd.findPeaks(imgR, axis=1)[0] * (1/np.sin(np.pi/180*angel)) 138 | slpA = -1/np.tan(np.pi/180*angel) 139 | 140 | 141 | 142 | 143 | 144 | plt.imshow(img) 145 | 146 | def 147 | for intercept in ictA: 148 | axes = plt.gca() 149 | x_vals = np.array(axes.get_xlim()) 150 | y_vals = intercept + slpA * x_vals 151 | plt.plot(x_vals, y_vals, '--') 152 | 153 | plt.show() 154 | 155 | 156 | 157 | plt.plot(imgR.mean(axis=0)) 158 | plt.show() 159 | plt.imshow(imgR) 160 | plt.show() 161 | 162 | 163 | 164 | def getFourierTransform(sig): 165 | sigf = np.fft.fft(sig)/len(sig) 166 | # return sigf[2:int(len(sigf)/2)] 167 | return sigf[2:25] 168 | 169 | import math 170 | 171 | math.degrees(np.pi/180) 172 | 173 | def getCardY(value, deg): 174 | return value * (np.cos(np.pi/180*deg)) 175 | 176 | 177 | getCardY(4, deg=30) 178 | 179 | 180 | 181 | np.tan(np.pi/180*45) 182 | 183 | 184 | lsPxRow, _ = findPeaks(GImg.get("binSeg"), nPeaks=self.nRow, axis=0, nSmooth=nSmooth) 185 | 186 | 187 | row = 9 188 | col = 3 189 | degs = [] 190 | sigs = [] 191 | maxs = [] 192 | for i in range(row): 193 | deg = i*15 194 | degs.append(deg) 195 | imgr = rotateBinNdArray(img, deg) 196 | sigr = imgr.mean(axis=0) 197 | sigrf = getFourierTransform(sigr) 198 | sigabs = abs(sigrf) 199 | plt.subplot(row, col, 1+i*col+0) 200 | plt.imshow(imgr) 201 | plt.subplot(row, col, 1+i*col+1) 202 | plt.plot(sigr) 203 | plt.subplot(row, col, 1+i*col+2) 204 | plt.ylim(0, 0.15) 205 | plt.plot(sigabs) 206 | sigs.append(round(sum(sigabs), 2)) 207 | maxs.append(round(max(sigabs), 2)) 208 | 209 | print(degs) 210 | print(sigs) 211 | print(maxs) 212 | plt.show() 213 | 214 | 215 | 216 | ######## 217 | 218 | pil = Image.fromarray(np.uint8(img*255)) 219 | 220 | plt.imshow(pil.rotate(45)) 221 | plt.show() 222 | 223 | npimg = np.asarray(pil.rotate(45), dtype=np.uint8)/255 224 | plt.imshow(npimg) 225 | plt.show() 226 | 227 | # rotate 228 | rotation_matrix = cv2.getRotationMatrix2D((num_cols/2, num_rows/2), 30, 1) 229 | img_rotation = cv2.warpAffine(img, rotation_matrix, (num_cols, num_rows)) 230 | cv2.imshow('Rotation', img_rotation) 231 | cv2.waitKey() 232 | 233 | 234 | num_rows, num_cols = img.shape[:2] 235 | 236 | translation_matrix = np.float32( 237 | [[1, 0, int(0.5*num_cols)], [0, 1, int(0.5*num_rows)]]) 238 | 239 | rotation_matrix=cv2.getRotationMatrix2D((num_cols, num_rows), 30, img_translation=cv2.warpAffine(img, translation_matrix, 1)) 240 | 241 | img_rotation = cv2.warpAffine(img_translation, rotation_matrix, (2*num_cols, 2*num_rows)) 242 | 243 | cv2.imshow('Rotation', img_rotation) 244 | cv2.waitKey() 245 | 246 | 247 | 248 | 249 | 250 | grid = gd.GRID() 251 | grid.run(pathImg="/Users/jameschen/Dropbox/James_Git/FN/data/demo.png", 252 | lsSelect=[0, 1], valShad=0, valSmth=5, 253 | nRow=11, nCol=7) 254 | grid.output() 255 | 256 | grid = gd.GRID() 257 | grid.run(pathImg="/Users/jameschen/Dropbox/James_Git/FN/data/demo.png", 258 | preset="/Users/jameschen/GRID.grid", outplot=True) 259 | 260 | grid = gd.GRID() 261 | 262 | 263 | 264 | 265 | 266 | 267 | grid.findPlots(nRow=11, nCol=7, plot=True) 268 | grid.cpuSeg(plot=True) 269 | 270 | # display seg 271 | # Create figure and axes 272 | fig, ax = plt.subplots(1) 273 | # Display the image 274 | ax.imshow(grid.imgs.get('visSeg')) 275 | for row in range(11): 276 | for col in range(7): 277 | recAg = grid.agents.get(row=row, col=col).getQRect() 278 | rect = patches.Rectangle( 279 | (recAg.x(), recAg.y()), recAg.width(), recAg.height(), 280 | linewidth=1, edgecolor='r', facecolor='none') 281 | ax.add_patch(rect) 282 | 283 | plt.show() 284 | 285 | # preview for fin 286 | fig, ax = plt.subplots() 287 | ax.imshow(grid.imgs.get('visSeg')) 288 | for row in range(11): 289 | for col in range(7): 290 | agent = grid.agents.get(row=row, col=col) 291 | recAg = agent.getQRect() 292 | line1, line2 = pltCross(agent.x, agent.y, width=1) 293 | rect = patches.Rectangle( 294 | (recAg.x(), recAg.y()), recAg.width(), recAg.height(), 295 | linewidth=1, edgecolor='r', facecolor='none') 296 | ax.add_line(line1) 297 | ax.add_line(line2) 298 | ax.add_patch(rect) 299 | 300 | plt.show() 301 | 302 | plt.imshow(grid.imgs.get("raw")) 303 | 304 | pltImShow(grid.imgs.get("raw")) 305 | 306 | pltSegPlot(grid.agents, grid.imgs.get("visSeg")) 307 | 308 | 309 | def test(a, b, c=0): 310 | return a+b+c 311 | 312 | parm = { 313 | "a":1, 314 | "b":3, 315 | "c":4, 316 | "d":5, 317 | } 318 | 319 | test(**parm) 320 | -------------------------------------------------------------------------------- /grid/archive/test.r: -------------------------------------------------------------------------------- 1 | required_pkg = c("MASS", "LDheatmap", "genetics", "EMMREML", "biganalytics", 2 | "ape", "bigmemory", "gplots", "compiler", "scatterplot3d", 3 | "R.utils", "data.table", "magrittr", "ggplot2", "rrBLUP", "BGLR") 4 | missing_pkg = required_pkg[!(required_pkg %in% installed.packages()[,"Package"])] 5 | if(length(missing_pkg)) 6 | install.packages(missing_pkg, repos="http://cran.rstudio.com/") 7 | 8 | tryCatch({ 9 | # for R 3.5 and above 10 | if (!requireNamespace("BiocManager", quietly = TRUE)) 11 | install.packages("BiocManager") 12 | BiocManager::install("multtest") 13 | BiocManager::install("snpStats") 14 | }, error = function(e) { 15 | # for earlier version of R (< 3.5) 16 | source("http://www.bioconductor.org/biocLite.R") 17 | biocLite("multtest") 18 | biocLite("snpStats") 19 | }) 20 | -------------------------------------------------------------------------------- /grid/archive/testAnchor.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import sys 3 | import grid as gd 4 | import pandas as pd 5 | import numpy as np 6 | 7 | import matplotlib.pyplot as plt 8 | from matplotlib import patches 9 | from matplotlib.lines import Line2D 10 | # 3rd party imports 11 | from PyQt5.QtWidgets import * 12 | from PyQt5.QtCore import * 13 | from PyQt5.QtGui import * 14 | 15 | # GUI TEST 16 | grid = gd.GRID() 17 | grid.loadData( 18 | "/Users/jameschen/Dropbox/James_Git/FN/data/demo.png") 19 | grid.binarizeImg(k=3, lsSelect=[0, 1], valShad=0, valSmth=0, outplot=False) 20 | app = QApplication(sys.argv) 21 | grid = PnAnchor(grid) 22 | -------------------------------------------------------------------------------- /grid/archive/testFindAngel.py: -------------------------------------------------------------------------------- 1 | import timeit 2 | import math 3 | import cv2 4 | import grid as gd 5 | import pandas as pd 6 | 7 | import matplotlib.pyplot as plt 8 | from matplotlib import patches 9 | from matplotlib.lines import Line2D 10 | 11 | 12 | # find best angle 13 | import numpy as np 14 | 15 | grid = gd.GRID() 16 | # grid.loadData( 17 | # "/Users/jameschen/Dropbox/James_Git/FN/data/demo.png") 18 | grid.loadData("/Users/jameschen/Dropbox/James Chen/GRID/Modeling/Rhombus.jpg") 19 | grid.binarizeImg(k=5, lsSelect=[4], valShad=0, valSmth=0, outplot=False) 20 | grid.findPlots(outplot=False) 21 | grid.cpuSeg(outplot=True) 22 | 23 | gd.pltSegPlot(grid.agents, grid.imgs.get("visSeg"), isRect=True) 24 | 25 | dt = grid.map.dt 26 | grid.map.nRow 27 | grid.map.nCol 28 | 29 | entry = dt[(dt.row == 0) & (dt.col == 1)].iloc[0] 30 | co = (dt.row == 0) & (dt.col == 1) 31 | 32 | entry["row"] 33 | entry["pt"] 34 | 35 | grid.cpuSeg(outplot=True) 36 | 37 | 38 | img = grid.imgs.get("binSeg") 39 | 40 | 41 | def rotateBinNdArray(img, angel): 42 | # create border for the image 43 | img[:, 0] = 1 44 | img[0, :] = 1 45 | img[:, -1] = 1 46 | img[-1, :] = 1 47 | 48 | # padding 49 | sizePad = max(img.shape) 50 | imgP = np.pad(img, [sizePad, sizePad], "constant") 51 | 52 | # rotate 53 | pivot = tuple((np.array(imgP.shape[:2]) / 2).astype(int)) 54 | matRot = cv2.getRotationMatrix2D(pivot, angel, 1.0) 55 | imgR = cv2.warpAffine( 56 | imgP.astype(np.float32), matRot, imgP.shape, flags=cv2.INTER_LINEAR 57 | ).astype(np.int8) 58 | 59 | # crop 60 | sigX = np.where(imgR.sum(axis=0) != 0)[0] 61 | sigY = np.where(imgR.sum(axis=1) != 0)[0] 62 | imgC = imgR[sigY[0] : sigY[-1], sigX[0] : sigX[-1]] 63 | 64 | # return 65 | return imgC 66 | 67 | 68 | def getFourierTransform(sig): 69 | sigf = abs(np.fft.fft(sig) / len(sig)) 70 | return sigf[2 : int(len(sigf) / 2)] 71 | # return sigf[2:25] 72 | 73 | 74 | def getCardIntercept(lsValues, angel): 75 | coef = 1 if angel == 0 else (1 / np.sin(np.pi / 180 * angel)) 76 | return lsValues * coef 77 | 78 | 79 | def getLineABC(slope, intercept): 80 | if np.isinf(slope): 81 | A = 1 82 | B = 0 83 | C = intercept 84 | else: 85 | A = slope 86 | B = -1 87 | C = -intercept 88 | return A, B, C 89 | 90 | 91 | def solveLines(slope1, intercept1, slope2, intercept2): 92 | A1, B1, C1 = getLineABC(slope1, intercept1) 93 | A2, B2, C2 = getLineABC(slope2, intercept2) 94 | D = A1 * B2 - A2 * B1 95 | Dx = C1 * B2 - B1 * C2 96 | Dy = A1 * C2 - C1 * A2 97 | if D != 0: 98 | x, y = Dx / D, Dy / D 99 | return x, y 100 | else: 101 | return False 102 | 103 | 104 | img = grid.imgs.get("binSeg") 105 | 106 | degRot = range(0, 90 + 1, 15) 107 | 108 | # find 2 axes 109 | sc = [] 110 | for angel in degRot: 111 | imgR = rotateBinNdArray(img, angel) 112 | sig = imgR.mean(axis=0) 113 | sigFour = getFourierTransform(sig) 114 | sc.append(max(sigFour)) 115 | 116 | 117 | def plotLine(axes, slope, intercept): 118 | if abs(slope) > 1e9: 119 | # vertical line 120 | y_vals = np.array(axes.get_ylim()) 121 | x_vals = np.repeat(intercept, len(y_vals)) 122 | else: 123 | # usual line 124 | x_vals = np.array(axes.get_xlim()) 125 | y_vals = intercept + slope * x_vals 126 | axes.plot(x_vals, y_vals, "--", color="red") 127 | 128 | 129 | def pltCross(x, y, size=3, width=1, color="red"): 130 | pt1X = [x - size, x + size] 131 | pt1Y = [y - size, y + size] 132 | line1 = Line2D(pt1X, pt1Y, linewidth=width, color=color) 133 | pt2X = [x - size, x + size] 134 | pt2Y = [y + size, y - size] 135 | line2 = Line2D(pt2X, pt2Y, linewidth=width, color=color) 136 | return line1, line2 137 | 138 | 139 | # plotting 140 | fig, ax = plt.subplots() 141 | ax.imshow(img) 142 | for i in range(2): 143 | for intercept in intercepts[i]: 144 | plotLine(ax, slopes[i], intercept) 145 | for pt in pts: 146 | line1, line2 = pltCross(pt[0], pt[1], width=1) 147 | ax.add_line(line1) 148 | ax.add_line(line2) 149 | 150 | plt.show() 151 | 152 | 153 | # DEMO 154 | row = 9 155 | col = 3 156 | degs = [] 157 | sigs = [] 158 | maxs = [] 159 | for i in range(row): 160 | deg = i * 15 161 | degs.append(deg) 162 | imgr = rotateBinNdArray(img, deg) 163 | sigr = imgr.mean(axis=0) 164 | sigrf = getFourierTransform(sigr) 165 | sigabs = abs(sigrf) 166 | plt.subplot(row, col, 1 + i * col + 0) 167 | plt.imshow(imgr) 168 | plt.subplot(row, col, 1 + i * col + 1) 169 | plt.plot(sigr) 170 | plt.subplot(row, col, 1 + i * col + 2) 171 | plt.ylim(0, 0.15) 172 | plt.plot(sigabs) 173 | sigs.append(round(sum(sigabs), 2)) 174 | maxs.append(round(max(sigabs), 2)) 175 | 176 | print(degs) 177 | print(sigs) 178 | print(maxs) 179 | plt.show() 180 | -------------------------------------------------------------------------------- /grid/archive/testRecoverSize.py: -------------------------------------------------------------------------------- 1 | import math 2 | import cv2 3 | import os 4 | import sys 5 | import grid as gd 6 | import pandas as pd 7 | import numpy as np 8 | 9 | import matplotlib.pyplot as plt 10 | from matplotlib import patches 11 | from matplotlib.lines import Line2D 12 | 13 | 14 | # seedling "/Users/jameschen/Dropbox/photo_grid/test/pheno/lsh_20200223.tif" 15 | # [[7931.0036832412525, 6546.418047882136], [6971.510128913444, 8684.02394106814], 16 | # [2295.497237569061, 6595.0], [3218.5543278084715, 4408.812154696133]] 17 | # matured "/Users/jameschen/Dropbox/photo_grid/test/pheno/lsh_20200331.tif" 18 | # [[5450.830570902393, 4299.983425414364], [7668.826887661141, 5293.896869244935], 19 | # [6706.300184162062, 7407.270718232044], [4519.690607734807, 6455.206261510129]] 20 | 21 | 22 | grid = gd.GRID() 23 | grid.loadData( 24 | "/Users/jameschen/Dropbox/photo_grid/test/pheno/lsh_20200223.tif") 25 | pts = np.array([[7931.0036832412525, 6546.418047882136, 1], 26 | [6971.510128913444, 8684.02394106814, 1], 27 | [2295.497237569061, 6595.0, 1], 28 | [3218.5543278084715, 4408.812154696133, 1]], dtype=np.float32) 29 | grid.cropImg(pts=pts) 30 | 31 | grid.binarizeImg(k=3, lsSelect=[0, 1], valShad=0, valSmth=0, outplot=False) 32 | 33 | img = grid.imgs.get("crop") 34 | 35 | shape = img.shape 36 | 37 | 38 | pts = np.array([[7931.0036832412525, 6546.418047882136, 1], 39 | [6971.510128913444, 8684.02394106814, 1], 40 | [2295.497237569061, 6595.0, 1], 41 | [3218.5543278084715, 4408.812154696133, 1]], dtype=np.float32) 42 | 43 | pts2 = np.float32([[0, 0, 1], 44 | [shape[0], 0, 1], 45 | [0, shape[1], 1], 46 | [shape[0], shape[1], 1]]) 47 | 48 | 49 | pts = np.array([[7931.0036832412525, 6546.418047882136], 50 | [6971.510128913444, 8684.02394106814], 51 | [2295.497237569061, 6595.0], 52 | [3218.5543278084715, 4408.812154696133]], dtype=np.float32) 53 | 54 | pts2 = np.float32([[0, 0], 55 | [shape[0], 0], 56 | [0, shape[1]], 57 | [shape[0], shape[1]]]) 58 | 59 | M = cv2.getPerspectiveTransform(pts2, pts) 60 | 61 | np.matmul(M, pts2.transpose()).transpose() 62 | pts 63 | 64 | pts.shape 65 | M.shape 66 | 67 | 68 | st = cv2.warpPerspective(img, M, (shape[0], shape[1])) 69 | 70 | dst.shape 71 | dst = np.array(dst).astype(np.uint8) 72 | 73 | ####### 74 | pts1 = np.float32([[200, 200], [600, 300], 75 | [100, 800], [500, 900]]) 76 | pts13 = np.float32([[200, 200, 1], [600, 300, 1], 77 | [100, 800, 1], [500, 900, 1]]) 78 | fix = 123 79 | pts2 = np.float32([[0, 0], [fix, 0], 80 | [0, fix], [fix, fix]]) 81 | pts23 = np.float32([[0, 0, 1], [fix, 0, 1], 82 | [0, fix, 1], [fix, fix, 1]]) 83 | 84 | M = cv2.getPerspectiveTransform(pts1, pts2) 85 | 86 | recover_scale(pts2, M) 87 | 88 | pts2_f = np.matmul(M, pts13.transpose()) 89 | pts2 90 | pts2_f.transpose() 91 | 92 | pts1_f = np.matmul(pts23, M) 93 | pts1_f 94 | 95 | app = QApplication(sys.argv) 96 | grid = PnAnchor(grid) 97 | 98 | 99 | pts1_r = np.matmul(np.linalg.inv(M), pts23.transpose()).transpose() 100 | i = 0 101 | 102 | ls = [pts1_r[i, :2] for i in range(len(pts1_r))] 103 | np.matrix(ls) 104 | 105 | pts1 106 | 107 | pts2.shape 108 | [list(pts2[i, :]) + [1] for i in range(4)] 109 | pts23.shape 110 | 111 | pts23 112 | 113 | 114 | M.shape 115 | np.linalg.inv(pts1) 116 | 117 | pts1.shape 118 | 119 | 120 | def recover_scale(mat_in, mat_H): 121 | """ 122 | recover the cropped shaped into original scale 123 | 124 | parameters 125 | ---------- 126 | mat_in: 4 x 2 matrix 127 | mat_H: 3 x 3 matrix 128 | """ 129 | 130 | n_points = len(mat_in) 131 | # conver mat_in into 4 x 3 matrix 132 | mat_in = np.array([list(mat_in[i, :]) + [1] for i in range(4)]) 133 | 134 | # solve recovered matrix 135 | mat_recover = np.matmul(np.linalg.inv(mat_H), mat_in.transpose()) 136 | 137 | # transpose back to the right dimension (4 x 3) 138 | mat_recover = mat_recover.transpose() 139 | 140 | # extract the first 2 elements in each point (4 x 2) 141 | mat_recover = [mat_recover[i, :2] for i in range(n_points)] 142 | 143 | # return 144 | return np.array(np.matrix(mat_recover)) 145 | 146 | 147 | pts1 = np.float32([[200, 200], [600, 300], 148 | [100, 800], [500, 900]]) 149 | pts13 = np.float32([[200, 200, 1], [600, 300, 1], 150 | [100, 800, 1], [500, 900, 1]]) 151 | fix = 123 152 | pts2 = np.float32([[0, 0], [fix, 0], 153 | [0, fix], [fix, fix]]) 154 | pts23 = np.float32([[0, 0, 1], [fix, 0, 1], 155 | [0, fix, 1], [fix, fix, 1]]) 156 | 157 | M = cv2.getPerspectiveTransform(pts1, pts2) 158 | 159 | recover_scale(pts2, M) 160 | 161 | 162 | np.rot90(pts1, 2) 163 | 164 | import math 165 | 166 | np.cos(np.pi/180*0) 167 | 168 | pts1 = np.float32([[400, 200], [600, 600], 169 | [300, 800], [500, 1200]]) 170 | pts1 = np.float32([[200, 400], [800, 600], 171 | [200, 600], [800, 400]]) 172 | origin = np.median(pts1, axis=0) 173 | # ptr1 = np.array([rotatePts((500, 500), pts1[i], 90) for i in range(4)]) 174 | ptr1 = rotatePts(pts1, 90, (500, 500)) 175 | plt.scatter(pts1.transpose()[0], pts1.transpose()[1]) 176 | plt.scatter(ptr1.transpose()[0], ptr1.transpose()[1]) 177 | 178 | 179 | M = cv2.getPerspectiveTransform(pts, pts2) 180 | dst = cv2.warpPerspective(img, M, (shape[0], shape[1])) 181 | transformed_points = cv2.warpPerspective( 182 | p_array, matrix, (2, 1), cv2.WARP_INVERSE_MAP) 183 | 184 | def rotatePts(pts, angle, org=(0, 0)): 185 | """ 186 | ---------- 187 | Parameters 188 | ---------- 189 | """ 190 | ox, oy = org 191 | ptx = np.array([pts[i, 0] for i in range(len(pts))]) 192 | pty = np.array([pts[i, 1] for i in range(len(pts))]) 193 | qx = ox + math.cos(math.radians(angle))*(ptx - ox) - \ 194 | math.sin(math.radians(angle))*(pty - oy) 195 | qy = oy + math.sin(math.radians(angle))*(ptx - ox) + \ 196 | math.cos(math.radians(angle))*(pty - oy) 197 | qpts = [[qx[i], qy[i]] for i in range(len(pts))] 198 | return np.array(qpts) 199 | 200 | ox, oy = origin 201 | px, py = point 202 | 203 | ag = np.pi / 180 * angle 204 | qx = ox + np.cos(ag) * (px - ox) - np.sin(ag) * (py - oy) 205 | qy = oy + np.sin(ag) * (px - ox) + np.cos(ag) * (py - oy) 206 | return qx, qy 207 | 208 | -------------------------------------------------------------------------------- /grid/archive/test_shapefile.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO as StringIO 2 | import shapefile 3 | import numpy 4 | import rasterio 5 | import rasterio.mask 6 | import fiona 7 | import os 8 | 9 | os.chdir("/Users/jameschen/Dropbox/photo_grid/test/Jacob") 10 | os.listdir() 11 | 12 | 13 | f = rasterio.open('RGB-integer.tif') 14 | f.profile 15 | f.width 16 | f.height 17 | 18 | f.bounds 19 | # BoundingBox(left=474888.6157, bottom=5140151.9396, right=474942.6952, top=5140222.4708) 20 | # ^, -> 21 | f.transform * (0, 0) 22 | 23 | [list(f.transform * pts[i]) for i in range(4)] 24 | 25 | f.transform * (f.width, f.height) 26 | f.close() 27 | 28 | with rasterio.open('RGB-integer.tif') as dataset: 29 | 30 | # Read the dataset's valid data mask as a ndarray. 31 | mask = dataset.dataset_mask() 32 | 33 | # Extract feature shapes and values from the array. 34 | for geom, val in rasterio.features.shapes( 35 | mask, transform=dataset.transform): 36 | 37 | # Transform shapes from the dataset's own coordinate 38 | # reference system to CRS84 (EPSG:4326). 39 | geom = rasterio.warp.transform_geom( 40 | dataset.crs, 'EPSG:4326', geom, precision=6) 41 | 42 | # Print GeoJSON shapes to stdout. 43 | print(geom) 44 | 45 | plt.imshow(mask) 46 | geom['type'] 47 | list(geom) 48 | 49 | 50 | os.chdir("/Users/jameschen/Dropbox/photo_grid/data/shapefile") 51 | 52 | file_sf = shapefile.Reader("IPNI_N_Trial_overlay.shp") 53 | file_fi = fiona.open("IPNI_N_Trial_overlay.shp", "r") 54 | len(file_sf) 55 | i = 150 56 | file_fi[i] 57 | 58 | file_sf.record(0) 59 | rec = file_sf.shapeRecords() 60 | rec[i].__dict__ 61 | rec[i].record 62 | rec[i].shape.points 63 | rec[i].shape.__dict__ 64 | 65 | 66 | file_sf[i].point 67 | file_sf.bbox 68 | print(file_sf) 69 | 70 | # writer 71 | w = shapefile.Writer("test") 72 | w.field('id', 'C', 20, 20) 73 | w.field("mean", 'N', 8, 3) 74 | # w.field('name2', 'C') 75 | w.poly([[[122, 37], [117, 36], [115, 32], [118, 20], [113, 24]]]) 76 | w.record(**dict({'id': 'polygon2', "mean": 3.525})) 77 | w.poly([[[122, 37], [118, 36], [105, 32], [118, 23], [113, 24]]]) 78 | w.record(**dict({'id': 'polygon2', "mean": 3.525})) 79 | w.close() 80 | 81 | r = shapefile.Reader('test') 82 | r.record(0) 83 | r.shapeRecords()[0].shape.points 84 | r.record(1) 85 | 86 | r.close() 87 | 88 | r.__dict__ 89 | 90 | 91 | # fiona 92 | ftest = fiona.open("/Users/jameschen/GRID.shp", "r") 93 | ftest.__dict__ 94 | ftest[0] 95 | ftest.close() 96 | 97 | file_fi.__dict__ 98 | np.array(file_fi[0]["geometry"]["coordinates"][0]).round() 99 | 100 | 101 | ###### 102 | 103 | # imports 104 | from scipy.stats import pearsonr 105 | from sklearn.linear_model import LogisticRegressionCV, LogisticRegression 106 | from sklearn.datasets import load_iris 107 | from sklearn.mixture import GaussianMixture 108 | from matplotlib.path import Path 109 | from sklearn.cluster import KMeans 110 | from scipy.signal import find_peaks 111 | from scipy.signal import convolve2d 112 | import grid as gd 113 | from PIL import Image 114 | import os 115 | import sys 116 | import numpy as np 117 | import pandas as pd 118 | import h5py as h5 119 | import cv2 120 | import random 121 | import warnings 122 | import matplotlib.pyplot as plt 123 | import matplotlib.patches as patches 124 | import math 125 | 126 | warnings.filterwarnings("ignore") 127 | 128 | # global params 129 | _RGB = (68, 51, 17) 130 | _HOME = os.path.expanduser("~") 131 | 132 | # self imports 133 | os.chdir(os.path.join(_HOME, "Dropbox", "James_Git")) 134 | import os 135 | os.chdir("..") 136 | sys.path 137 | sys.path.remove("/Users/jameschen/Dropbox/photo_grid/grid") 138 | import grid as gd 139 | 140 | # load image 141 | grid = gd.GRID() 142 | grid.loadData( 143 | "/Users/jameschen/Dropbox/James Chen/Projects/GRID/Prototype/GRID_Demo_Croped.jpg") 144 | grid.binarizeImg(k=3, features=[0, 1], 145 | lsSelect=[0], 146 | valShad=10, valSmth=5, outplot=True) 147 | grid.findPlots(nRow=23, nCol=12) 148 | grid.cpuSeg(outplot=True) 149 | 150 | grid.save(prefix="test") 151 | 152 | 153 | imgH = grid.map.imgH 154 | imgW = grid.map.imgW 155 | 156 | dt = pd.read_csv("~/test_data.csv") 157 | cols = dt.columns 158 | 159 | for col in cols: 160 | instance = dt[col][0] 161 | 162 | if isinstance(instance, object): 163 | # characters 164 | mode = "C" 165 | arg1, arg2 = 20, 20 166 | else: 167 | # integer, floating 168 | mode = "N" 169 | arg1, arg2 = 10, 10 170 | 171 | f.field(col, mode, arg1, arg2) 172 | 173 | for idx, entry in dt.iterrows(): 174 | # get agents 175 | row = entry["row"] 176 | col = entry["col"] 177 | agent = grid.agents.get(row, col) 178 | 179 | # polygon 180 | bN = imgH - agent.border["NORTH"] 181 | bW = agent.border["WEST"] 182 | bS = imgH - agent.border["SOUTH"] 183 | bE = agent.border["EAST"] 184 | f.poly([[[bW, bN], [bE, bN], [bE, bS], [bW, bS], [bW, bN]]]) 185 | 186 | # attributes 187 | dc = {c: entry[c] for c in dt.columns} 188 | f.record(**dict(dc)) 189 | 190 | 191 | -------------------------------------------------------------------------------- /grid/archive/thread.py: -------------------------------------------------------------------------------- 1 | 2 | class Prog(QWidget): 3 | def __init__(self): 4 | super().__init__() 5 | layout = QVBoxLayout(self) 6 | 7 | # Create a progress bar and a button and add them to the main layout 8 | self.progressBar = QProgressBar(self) 9 | self.progressBar.setRange(0, 5) 10 | layout.addWidget(self.progressBar) 11 | button = QPushButton("Start", self) 12 | layout.addWidget(button) 13 | 14 | button.clicked.connect(self.onStart) 15 | 16 | self.myLongTask = TaskThread() 17 | self.myLongTask.taskFinished.connect(self.onFinished) 18 | 19 | def activate(self): 20 | for _ in range(5): 21 | self.onStart() 22 | time.sleep(1) 23 | 24 | def onStart(self): 25 | # self.progressBar.setRange(0,0) 26 | self.myLongTask.start() 27 | 28 | def onFinished(self): 29 | # Stop the pulsation 30 | value = self.progressBar.value() 31 | # self.progressBar.setRange(0,1) 32 | self.progressBar.setValue(value+1) 33 | 34 | 35 | class TaskThread(QThread): 36 | taskFinished = pyqtSignal() 37 | 38 | def run(self): 39 | # time.sleep(1) 40 | print("run") 41 | self.taskFinished.emit() 42 | -------------------------------------------------------------------------------- /grid/benchmark.py: -------------------------------------------------------------------------------- 1 | import time 2 | import matplotlib.pyplot as plt 3 | import cv2 4 | import numpy as np 5 | import rasterio 6 | import os 7 | os.chdir("/Users/jameschen/Dropbox/photo_grid") 8 | import grid as gd 9 | os.chdir("/Users/jameschen/Dropbox/James Chen/GRID/Manuscript/Remote Sensing/") 10 | os.chdir("First Revision/demo/") 11 | 12 | # load the 1st demo file 13 | img = gd.loadImg("demo_1.tif") 14 | 15 | 16 | # define output function 17 | def out_diff_size(imgIn, pts, path, size): 18 | img = gd.cropImg(imgIn, pts, img_W=size, img_H=size).astype(rasterio.uint8) 19 | bands = img.shape[2] 20 | with rasterio.open(path, 'w', driver="GTiff", 21 | height=img.shape[0], width=img.shape[1], 22 | count=bands, dtype=rasterio.uint8) as dst: 23 | for i in range(bands): 24 | dst.write(img[:, :, i], i+1) 25 | 26 | 27 | # crop AOI for demo 28 | pts = [[2879.464480874317, 5707.814207650273], 29 | [5758.928961748634, 5861.158469945355], 30 | [5571.508196721311, 8945.081967213115], 31 | [2709.0819672131147, 8808.775956284153]] 32 | 33 | # output different file size of data from the same AOI 34 | out_diff_size(img, pts, "size/size_2_0gb.tif", 18500) 35 | out_diff_size(img, pts, "size/size_1_5gb.tif", 15700) 36 | out_diff_size(img, pts, "size/size_1_0gb.tif", 13000) 37 | out_diff_size(img, pts, "size/size_0_5gb.tif", 9150) 38 | out_diff_size(img, pts, "size/size_0_1gb.tif", 4100) 39 | 40 | # output different plot number of data from the same file size 500 MB 41 | # 5*10 = 50 42 | out_diff_size(img, 43 | [[2860.333586050038, 5697.028051554207], 44 | [2777.5966641395, 6985.360121304018], 45 | [4125.02653525398, 5779.764973464746], 46 | [4065.92873388931, 7044.457922668688]], 47 | "plot/plot_50.tif", 9150) 48 | 49 | # 10*10 = 100 50 | out_diff_size(img, 51 | [[2860.333586050038, 5697.028051554207], 52 | [4136.846095526915, 5767.945413191812], 53 | [3971.3722517058377, 8297.331311599697], 54 | [2730.3184230477636, 8226.413949962092]], 55 | "plot/plot_100.tif", 9150) 56 | 57 | # 10*15 = 150 58 | out_diff_size(img, 59 | [[2860.333586050038, 5708.847611827142], 60 | [2718.4988627748294, 8226.413949962092], 61 | [4621.44806671721, 8344.609552691432], 62 | [4763.282789992419, 5815.223654283548]], 63 | "plot/plot_150.tif", 9150) 64 | 65 | # 10*20 = 200 66 | out_diff_size(img, 67 | [[2848.5140257771036, 5697.028051554207], 68 | [2718.4988627748294, 8226.413949962092], 69 | [5236.06520090978, 8380.068233510236], 70 | [5401.539044730856, 5838.862774829417]], 71 | "plot/plot_200.tif", 9150) 72 | 73 | 74 | # output benchmark results for size comparison 75 | with open("benchmark_size.csv", "w") as f: 76 | f.write("size,elapse,iter\n") 77 | 78 | dic_size = {"2_0": 2, 79 | "1_5": 1.5, 80 | "1_0": 1.0, 81 | "0_5": 0.5, 82 | "0_1": 0.1} 83 | 84 | for i in range(100): 85 | for key, val in dic_size.items(): 86 | # time the loading process 87 | t_cur = time.time() 88 | grid = gd.GRID() 89 | grid.loadData("size/size_%sgb.tif" % key) 90 | elapse = round(time.time() - t_cur, 4) 91 | # export results 92 | with open("benchmark_size.csv", "a") as f: 93 | f.write("%d,%.4f,%d\n" % (val, elapse, i)) 94 | 95 | 96 | # output benchmark results for plots comparison 97 | with open("benchmark_plot.csv", "w") as f: 98 | f.write("plot,ep_plots,ep_seg,iter\n") 99 | 100 | dic_plot = {"50": (50, 5, 10), 101 | "100": (100, 10, 10), 102 | "150": (150, 10, 15), 103 | "200": (200, 10, 20)} 104 | 105 | for i in range(100): 106 | for key, val in dic_plot.items(): 107 | nplot, nrow, ncol = val 108 | grid = gd.GRID() 109 | grid.loadData("plot/plot_%d.tif" % nplot) 110 | w, h, d = grid.imgs.get("raw").shape 111 | grid.cropImg(pts=[[0, 0], [0, w], [h, 0], [h, w]]) 112 | grid.binarizeImg(k=3, lsSelect=[0], valShad=0, valSmth=0) 113 | # locate center 114 | t_cur = time.time() 115 | grid.findPlots(nRow=nrow, nCol=ncol) 116 | ep_pt = round(time.time() - t_cur, 4) 117 | # expand boundaries 118 | t_cur = time.time() 119 | grid.cpuSeg() 120 | ep_sg = round(time.time() - t_cur, 4) 121 | # export results 122 | with open("benchmark_plot.csv", "a") as f: 123 | f.write("%d,%.4f,%.4f,%d\n" % (nplot, ep_pt, ep_sg, i)) 124 | -------------------------------------------------------------------------------- /grid/benchmark.r: -------------------------------------------------------------------------------- 1 | library(ggplot2) 2 | library(magrittr) 3 | library(data.table) 4 | library(tidyr) 5 | setwd("~/Dropbox/James Chen/Projects/GRID/Manuscript/Remote Sensing/First Revision/demo") 6 | 7 | dt_plot = fread("plot/benchmark_plot.csv") %>% 8 | gather(metrics, elapse, -plot, -iter) %>% data.table() 9 | dt_size = fread("size/benchmark_size.csv") 10 | 11 | 12 | 13 | ggplot(data=dt_plot, aes(x=plot, y=elapse, group=plot)) + 14 | geom_boxplot() + 15 | facet_grid(.~metrics) + 16 | scale_y_continuous(limits=c(1.5, 3.5)) 17 | 18 | ggplot(data=dt_size, aes(x=size, y=elapse, group=size)) + geom_boxplot() 19 | -------------------------------------------------------------------------------- /grid/demo/seg_img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/demo/seg_img.jpg -------------------------------------------------------------------------------- /grid/dir.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class Dir(Enum): 4 | NORTH = 0 5 | WEST = 1 6 | SOUTH = 2 7 | EAST = 3 8 | -------------------------------------------------------------------------------- /grid/gimage.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gimage.pyc -------------------------------------------------------------------------------- /grid/grid.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/grid.pyc -------------------------------------------------------------------------------- /grid/gui/__init__.py: -------------------------------------------------------------------------------- 1 | from grid.gui.inputer import * 2 | from grid.gui.cropper import * 3 | from grid.gui.kmeaner import * 4 | from grid.gui.anchor import * 5 | from grid.gui.outputer import * 6 | -------------------------------------------------------------------------------- /grid/gui/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/anchor.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/anchor.cpython-310.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/anchor.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/anchor.cpython-36.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/anchor.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/anchor.cpython-37.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/anchor.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/anchor.cpython-39.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/cropper.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/cropper.cpython-310.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/cropper.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/cropper.cpython-36.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/cropper.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/cropper.cpython-37.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/cropper.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/cropper.cpython-39.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/customQt.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/customQt.cpython-310.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/customQt.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/customQt.cpython-36.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/customQt.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/customQt.cpython-37.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/customQt.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/customQt.cpython-39.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/inputer.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/inputer.cpython-310.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/inputer.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/inputer.cpython-36.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/inputer.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/inputer.cpython-37.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/inputer.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/inputer.cpython-39.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/kmeaner.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/kmeaner.cpython-310.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/kmeaner.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/kmeaner.cpython-36.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/kmeaner.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/kmeaner.cpython-37.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/kmeaner.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/kmeaner.cpython-39.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/outputer.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/outputer.cpython-310.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/outputer.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/outputer.cpython-36.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/outputer.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/outputer.cpython-37.pyc -------------------------------------------------------------------------------- /grid/gui/__pycache__/outputer.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/gui/__pycache__/outputer.cpython-39.pyc -------------------------------------------------------------------------------- /grid/gui/customQt.py: -------------------------------------------------------------------------------- 1 | # basic imports 2 | import numpy as np 3 | import sys 4 | 5 | # 3-rd party imports 6 | from PyQt6.QtWidgets import * 7 | from PyQt6.QtCore import * 8 | from PyQt6.QtGui import * 9 | 10 | # self import 11 | from grid.lib import * 12 | 13 | 14 | class Widget_Img(QWidget): 15 | """ 16 | Will keep imgRaw, imgVis and imgQmap 17 | """ 18 | 19 | def __init__(self): 20 | super().__init__() 21 | """attr""" 22 | # self.img_raw = img 23 | # self.img_vis = img[:, :, :3].copy() 24 | self.qimg = None 25 | self.isFitWidth = None 26 | self.rgX, self.rgY = (0, 0), (0, 0) 27 | self.sizeImg = (0, 0) 28 | 29 | def make_rgb_img(self, img): 30 | self.qimg = getRGBQImg(img[:, :, :3]) 31 | self.updateDim() 32 | 33 | def make_bin_img(self, img): 34 | self.qimg = getBinQImg(img) 35 | self.updateDim() 36 | 37 | def make_idx8_img(self, img, k): 38 | self.qimg = getIdx8QImg(img, k) 39 | self.updateDim() 40 | 41 | def make_gray_img(self, img): 42 | self.qimg = getGrayQImg(img) 43 | self.updateDim() 44 | 45 | def updateDim(self): 46 | self.sizeImg = self.qimg.size().scaled(self.rect().size(), Qt.KeepAspectRatio) 47 | if self.sizeImg.width() == self.width(): 48 | self.isFitWidth = True 49 | marginY = int((self.height() - self.sizeImg.height()) / 2) 50 | self.rgX = (0, self.sizeImg.width()) 51 | self.rgY = (marginY, marginY + self.sizeImg.height()) 52 | elif self.sizeImg.height() == self.height(): 53 | self.isFitWidth = False 54 | marginX = int((self.width() - self.sizeImg.width()) / 2) 55 | self.rgX = (marginX, marginX + self.sizeImg.width()) 56 | self.rgY = (0, self.sizeImg.height()) 57 | 58 | def isInRange(self, x, y): 59 | if ( 60 | x >= self.rgX[0] 61 | and x <= self.rgX[1] 62 | and y >= self.rgY[0] 63 | and y <= self.rgY[1] 64 | ): 65 | return True 66 | else: 67 | return False 68 | 69 | def getImgRange(self): 70 | return self.rgX, self.rgY 71 | 72 | def paintImage(self, painter): 73 | painter.setRenderHint(QPainter.Antialiasing, True) 74 | self.updateDim() 75 | painter.drawPixmap( 76 | self.rgX[0], 77 | self.rgY[0], 78 | self.sizeImg.width(), 79 | self.sizeImg.height(), 80 | self.qimg, 81 | ) 82 | 83 | 84 | def getRGBQImg(img): 85 | h, w = img.shape[0], img.shape[1] 86 | qImg = QImage(img.astype(np.uint8).copy(), w, h, w * 3, QImage.Format_RGB888) 87 | return QPixmap(qImg) 88 | 89 | 90 | def getBinQImg(img): 91 | h, w = img.shape[0], img.shape[1] 92 | qImg = QImage(img.astype(np.uint8).copy(), w, h, w * 1, QImage.Format_Indexed8) 93 | qImg.setColor(0, qRgb(0, 0, 0)) 94 | qImg.setColor(1, qRgb(241, 225, 29)) 95 | return QPixmap(qImg) 96 | 97 | 98 | def getIdx8QImg(img, k): 99 | colormap = [ 100 | qRgb(228, 26, 28), 101 | qRgb(55, 126, 184), 102 | qRgb(77, 175, 74), 103 | qRgb(152, 78, 163), 104 | qRgb(255, 127, 0), 105 | qRgb(255, 255, 51), 106 | qRgb(166, 86, 40), 107 | qRgb(247, 129, 191), 108 | qRgb(153, 153, 153), 109 | ] 110 | h, w = img.shape[0], img.shape[1] 111 | qImg = QImage(img.astype(np.uint8).copy(), w, h, w * 1, QImage.Format_Indexed8) 112 | for i in range(k): 113 | qImg.setColor(i, colormap[i]) 114 | return QPixmap(qImg) 115 | 116 | 117 | def getGrayQImg(img): 118 | h, w = img.shape[0], img.shape[1] 119 | qImg = QImage(img.astype(np.uint8).copy(), w, h, w * 1, QImage.Format_Grayscale8) 120 | return QPixmap(qImg) 121 | 122 | 123 | def magnifying_glass(widget, pos, area=200, zoom=4): 124 | size = int(area / zoom) 125 | pixmap = widget.grab( 126 | QRect( 127 | QPoint(pos.x() - int(size / 2), pos.y() - int(size / 2)), QSize(size, size) 128 | ) 129 | ) 130 | try: 131 | rate_screen = size / pixmap.width() 132 | pixmap = pixmap.scaled(int(area / rate_screen), int(area / rate_screen)) 133 | painter = QPainter(pixmap) 134 | "Rect" 135 | pen = QPen() 136 | pen.setWidth(2) 137 | pen.setColor(Qt.black) 138 | painter.setPen(pen) 139 | # define rect 140 | rect = QRect(QPoint(0, 0), pixmap.size() * rate_screen) 141 | # draw rect 142 | painter.drawRect(rect) 143 | """Cursor""" 144 | pen.setWidth(2) 145 | pen.setColor(Qt.red) 146 | painter.setPen(pen) 147 | size_m = 10 148 | space = 4 149 | # define lines 150 | line1 = QLine(QPoint(size_m, 0), QPoint(space, 0)) 151 | line2 = QLine(QPoint(0, size_m), QPoint(0, space)) 152 | line3 = QLine(QPoint(0, -space), QPoint(0, -size_m)) 153 | line4 = QLine(QPoint(-size_m, 0), QPoint(-space, 0)) 154 | line1.translate(pixmap.rect().center() * rate_screen - QPoint(0, 0)) 155 | line2.translate(pixmap.rect().center() * rate_screen - QPoint(0, 0)) 156 | line3.translate(pixmap.rect().center() * rate_screen - QPoint(0, 0)) 157 | line4.translate(pixmap.rect().center() * rate_screen - QPoint(0, 0)) 158 | # draw lines 159 | painter.drawLine(line1) 160 | painter.drawLine(line2) 161 | painter.drawLine(line3) 162 | painter.drawLine(line4) 163 | 164 | # # remove central 165 | # pt_center = pixmap.rect().center() * rate_screen 166 | # x, y = pt_center.x(), pt_center.y() 167 | # painter.eraseRect(QRect(x-2, y-2, 4, 4)) 168 | """finish""" 169 | painter.end() 170 | cursor = QCursor(pixmap) 171 | widget.setCursor(cursor) 172 | except Exception: 173 | """not in a valid region""" 174 | 175 | 176 | def drawCross(x, y, painter, size=2): 177 | l1_st_x, l1_st_y = x - size, y - size 178 | l1_ed_x, l1_ed_y = x + size, y + size 179 | l2_st_x, l2_st_y = x - size, y + size 180 | l2_ed_x, l2_ed_y = x + size, y - size 181 | painter.drawLine(int(l1_st_x), int(l1_st_y), int(l1_ed_x), int(l1_ed_y)) 182 | painter.drawLine(int(l2_st_x), int(l2_st_y), int(l2_ed_x), int(l2_ed_y)) 183 | 184 | 185 | def drawTriangle(x, y, dir, painter, range=7, peak=30): 186 | path = QPainterPath() 187 | path.moveTo(x, y) 188 | if dir == "North": 189 | path.lineTo(x - range, y + peak) 190 | path.lineTo(x + range, y + peak) 191 | elif dir == "South": 192 | path.lineTo(x - range, y - peak) 193 | path.lineTo(x + range, y - peak) 194 | elif dir == "West": 195 | path.lineTo(x + peak, y - range) 196 | path.lineTo(x + peak, y + range) 197 | elif dir == "East": 198 | path.lineTo(x - peak, y - range) 199 | path.lineTo(x - peak, y + range) 200 | path.lineTo(x, y) 201 | painter.drawPath(path) 202 | -------------------------------------------------------------------------------- /grid/gui/inputer.py: -------------------------------------------------------------------------------- 1 | # 3rd party imports 2 | from PyQt6.QtWidgets import * 3 | from PyQt6.QtCore import * 4 | from PyQt6.QtGui import * 5 | 6 | # self imports 7 | from grid.grid import * 8 | 9 | 10 | class PnInputer(QWidget): 11 | """ """ 12 | 13 | def __init__(self, grid): 14 | """ """ 15 | 16 | super().__init__() 17 | self.grid = grid 18 | # user define 19 | self.gr_user = QGroupBox("User's Input (You can drag and drop files)") 20 | self.lo_user = QGridLayout() 21 | self.lb_img = QLabel() 22 | self.lb_map = QLabel() 23 | self.lb_shp = QLabel() 24 | self.fd_img = DnDLineEdit() 25 | self.fd_map = DnDLineEdit() 26 | self.fd_shp = DnDLineEdit() 27 | self.bt_img = QPushButton() 28 | self.bt_map = QPushButton() 29 | self.bt_shp = QPushButton() 30 | # demo 31 | self.gr_demo = QGroupBox("Demo") 32 | self.lo_demo = QVBoxLayout() 33 | self.lb_demo = QLabel( 34 | "Will use sample files to demo the program. Or go to User Manual " 35 | ) 36 | self.lb_demo.setOpenExternalLinks(True) 37 | 38 | # self 39 | self.layout = QVBoxLayout() 40 | 41 | self.initUI() 42 | 43 | def initUI(self): 44 | """ """ 45 | 46 | # USER 47 | ## GUI components 48 | self.gr_user.setCheckable(True) 49 | self.gr_user.setChecked(False) 50 | self.gr_user.clicked.connect(lambda: self.toggle(self.gr_user)) 51 | self.lb_img.setText("Image (.tif, .jpg, .png):") 52 | self.lb_map.setText("Map (.csv) (OPTIONAL):") 53 | self.lb_shp.setText("Shape (.shp) (OPTIONAL):") 54 | font = self.fd_img.font() 55 | font.setPointSize(25) 56 | fm = QFontMetrics(font) 57 | self.fd_img.setFixedHeight(fm.height()) 58 | self.fd_map.setFixedHeight(fm.height()) 59 | self.fd_shp.setFixedHeight(fm.height()) 60 | self.bt_img.setText("Browse") 61 | self.bt_img.clicked.connect(self.assign_PathImg) 62 | self.bt_map.setText("Browse") 63 | self.bt_map.clicked.connect(self.assign_PathMap) 64 | self.bt_shp.setText("Browse") 65 | self.bt_shp.clicked.connect(self.assign_PathShp) 66 | ## layout 67 | self.lo_user.addWidget(self.lb_img, 0, 0) 68 | self.lo_user.addWidget(self.fd_img, 0, 1) 69 | self.lo_user.addWidget(self.bt_img, 0, 2) 70 | self.lo_user.addWidget(self.lb_map, 1, 0) 71 | self.lo_user.addWidget(self.fd_map, 1, 1) 72 | self.lo_user.addWidget(self.bt_map, 1, 2) 73 | self.lo_user.addWidget(self.lb_shp, 2, 0) 74 | self.lo_user.addWidget(self.fd_shp, 2, 1) 75 | self.lo_user.addWidget(self.bt_shp, 2, 2) 76 | self.gr_user.setLayout(self.lo_user) 77 | 78 | # DEMO 79 | ## GUI components 80 | self.gr_demo.setCheckable(True) 81 | self.gr_demo.setChecked(True) 82 | self.gr_demo.clicked.connect(lambda: self.toggle(self.gr_demo)) 83 | ## layout 84 | self.lo_demo.addWidget(self.lb_demo) 85 | self.gr_demo.setLayout(self.lo_demo) 86 | 87 | # LAYOUT 88 | self.layout.setContentsMargins(200, 50, 200, 50) 89 | self.layout.addWidget(self.gr_user) 90 | self.layout.addWidget(self.gr_demo) 91 | 92 | # FINALIZE 93 | self.setLayout(self.layout) 94 | self.show() 95 | 96 | def toggle(self, groupbox): 97 | """ """ 98 | 99 | if groupbox.title() == "Demo": 100 | self.gr_user.setChecked(not self.gr_user.isChecked()) 101 | elif groupbox.title() != "Demo": 102 | self.gr_demo.setChecked(not self.gr_demo.isChecked()) 103 | 104 | def assign_PathImg(self): 105 | """ """ 106 | 107 | fileter = "Images (*.tif *.jpg *.jpeg *.png)" 108 | path = QFileDialog().getOpenFileName(self, "", "", fileter)[0] 109 | self.fd_img.setText(path) 110 | 111 | def assign_PathMap(self): 112 | """ """ 113 | 114 | fileter = "Map (*.csv *.txt)" 115 | path = QFileDialog().getOpenFileName(self, "", "", fileter)[0] 116 | self.fd_map.setText(path) 117 | 118 | def assign_PathShp(self): 119 | """ """ 120 | 121 | fileter = "Shape (*.shp)" 122 | path = QFileDialog().getOpenFileName(self, "", "", fileter)[0] 123 | self.fd_shp.setText(path) 124 | 125 | def run(self): 126 | """ """ 127 | if self.gr_user.isChecked(): 128 | self.grid.loadData( 129 | pathImg=self.fd_img.text(), 130 | pathMap=self.fd_map.text(), 131 | pathShp=self.fd_shp.text(), 132 | ) 133 | else: 134 | self.grid.loadData() # load demo files 135 | 136 | 137 | class DnDLineEdit(QLineEdit): 138 | def __init__(self): 139 | super().__init__() 140 | self.setAcceptDrops(True) 141 | 142 | def dragEnterEvent(self, event): 143 | if event.mimeData().hasUrls: 144 | event.accept() 145 | else: 146 | event.ignore() 147 | 148 | def dragMoveEvent(self, event): 149 | if event.mimeData().hasUrls: 150 | event.setDropAction(Qt.CopyAction) 151 | event.accept() 152 | else: 153 | event.ignore() 154 | 155 | def dropEvent(self, event): 156 | if event.mimeData().hasUrls: 157 | event.setDropAction(Qt.CopyAction) 158 | event.accept() 159 | text = "" 160 | for url in event.mimeData().urls(): 161 | text = str(url.toLocalFile()) 162 | self.setText(text) 163 | else: 164 | event.ignore() 165 | -------------------------------------------------------------------------------- /grid/guser.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import os 3 | 4 | 5 | class GUser(): 6 | """ 7 | """ 8 | def __init__(self): 9 | """ 10 | """ 11 | self.platform = platform.system() 12 | self.architecture = platform.architecture()[0] 13 | self.release = platform.release() 14 | self.machine = platform.machine() 15 | self.dirHome = os.path.expanduser("~") 16 | self.dirGrid = os.path.split(__file__)[0] 17 | 18 | def print_conflict_info(self): 19 | None 20 | 21 | def printInfo(self): 22 | print("GRID User's Info") 23 | print("----------------") 24 | print("Platform: ", self.platform) 25 | print("Architecture: ", self.architecture) 26 | print("Release: ", self.release) 27 | print("Machine: ", self.machine) 28 | print("Home Dir: ", self.dirHome) 29 | print("GRID Dir: ", self.dirGrid) 30 | 31 | 32 | # print conflict message if Mac users 33 | user = GUser() 34 | 35 | if user.platform == "Darwin": 36 | print("For Mac OS users, please ignore the messages below relating to duplicated implementations. \n\ 37 | It's due to the conflict of OpenCV and PyQt, but it won't affect the performance and the results.\n") 38 | -------------------------------------------------------------------------------- /grid/guser.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/guser.pyc -------------------------------------------------------------------------------- /grid/io.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/io.pyc -------------------------------------------------------------------------------- /grid/res/rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/grid/res/rotate.png -------------------------------------------------------------------------------- /grid/test/test_gui.py: -------------------------------------------------------------------------------- 1 | # 3rd party imports 2 | from PyQt6.QtWidgets import * 3 | from PyQt6.QtCore import * 4 | from PyQt6.QtGui import * 5 | import sys 6 | import qdarkstyle 7 | import os 8 | 9 | app = QApplication(sys.argv) 10 | app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5()) 11 | 12 | msgBox = QMessageBox() 13 | msgBox.setIcon(QMessageBox.Icon.Information) 14 | msgBox.setText("Finished!") 15 | msgBox.setInformativeText("Save and start another job?") 16 | msgBox.setStandardButtons( 17 | QMessageBox.StandardButton.Yes| 18 | QMessageBox.StandardButton.No| 19 | QMessageBox.StandardButton.Discard) 20 | 21 | msgBox.button(QMessageBox.StandardButton.Yes).setText("Save and stay in current work") 22 | msgBox.button(QMessageBox.StandardButton.No).setText("Save and start new job") 23 | msgBox.button(QMessageBox.StandardButton.Discard).setText("Cancel") 24 | # layout = msgBox.layout() 25 | msgBox.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) 26 | 27 | msgBox.show() 28 | app.exec() 29 | 30 | 31 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | pandas>=0.19.2 3 | h5py 4 | pyshp 5 | scikit-learn 6 | scipy 7 | matplotlib 8 | image 9 | opencv-python 10 | rasterio 11 | PyQt6 12 | qdarkstyle 13 | tqdm -------------------------------------------------------------------------------- /res/GRID_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/res/GRID_banner.png -------------------------------------------------------------------------------- /res/GRID_logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/res/GRID_logo2.png -------------------------------------------------------------------------------- /res/abstract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poissonfish/GRID/ed6b0b44cf60f0cda986c5897c14307f30ae8803/res/abstract.png -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r", encoding="utf-8") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="photo_grid", 8 | version="1.3.13", 9 | description="A GUI for field segmentation", 10 | long_description=long_description, 11 | long_description_content_type="text/markdown", 12 | url="https://github.com/Poissonfish/GRID", 13 | python_requires=">=3.6", 14 | classifiers=[ 15 | "Programming Language :: Python :: 3.6", 16 | "Programming Language :: Python :: 3.7", 17 | "Programming Language :: Python :: 3.8", 18 | "Programming Language :: Python :: 3.9", 19 | "Programming Language :: Python :: 3.10", 20 | "Programming Language :: Python :: 3.11", 21 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", 22 | "Operating System :: MacOS :: MacOS X", 23 | "Operating System :: Microsoft :: Windows :: Windows 10", 24 | ], 25 | author="James Chen", 26 | author_email="niche@vt.edu", 27 | license="GPLv3", 28 | packages=["grid", "grid.gui"], 29 | include_package_data=True, 30 | install_requires=[ 31 | "numpy", 32 | "pandas>=0.19.2", 33 | # data processing 34 | "h5py", 35 | "pyshp", 36 | # math, models 37 | "scikit-learn", 38 | "scipy", 39 | "matplotlib", 40 | # image processing 41 | "image", 42 | "opencv-python", 43 | "rasterio", 44 | # GUI 45 | "PyQt6", 46 | "qdarkstyle", 47 | # misc 48 | "tqdm", 49 | ], 50 | entry_points={"console_scripts": ["GRID=grid.__main__:main"]}, 51 | ) 52 | --------------------------------------------------------------------------------