├── .github ├── FUNDING.yml └── workflows │ ├── codeql-analysis.yml │ └── pytest.yml ├── .gitignore ├── CITATION.cff ├── LICENSE ├── MANIFEST.in ├── README.md ├── clustimage ├── __init__.py ├── clustimage.py ├── examples.py ├── exif.py └── tests │ ├── __init__.py │ └── test_clustimage.py ├── codecov.yml ├── docs ├── .nojekyll ├── HOWTO.txt ├── Makefile ├── _config.yml ├── figs │ ├── 101_cluster.png │ ├── 101_cluster40.png │ ├── 101_dendrogram.png │ ├── 101_explainedvar.png │ ├── 101_optimalclusters.png │ ├── 101_silhouette_plot.png │ ├── 101_tsne.png │ ├── 101_tsne_mean.png │ ├── 101_tsne_no_mean.png │ ├── best_digits.png │ ├── clothing.png │ ├── dbindex.png │ ├── dbindex_eq1.png │ ├── dbindex_eq2.jpg │ ├── dbindex_tsne.png │ ├── dbscan.png │ ├── dendrogram_five.png │ ├── dendrogram_optimized.png │ ├── dendrogram_three.png │ ├── digits_cluster1.png │ ├── digits_cluster4.png │ ├── digits_cluster5.png │ ├── digits_clusters.png │ ├── digits_dendrogram.png │ ├── digits_explained_var.png │ ├── digits_fig1.png │ ├── digits_fig21_tsne.png │ ├── digits_fig22_tsne.png │ ├── digits_fig2_tsne.png │ ├── digits_pca.png │ ├── digits_pca_dbindex.png │ ├── digits_unique.png │ ├── digits_unique1.png │ ├── digits_unique2.png │ ├── faces1.png │ ├── faces_cluster0.png │ ├── faces_cluster3.png │ ├── faces_scatter.png │ ├── faces_scatter_no_img.png │ ├── faces_set_max_clust.png │ ├── faces_sil_vs_nrclusters.png │ ├── faces_unique.png │ ├── find_digit.png │ ├── find_flowers.png │ ├── find_flowers_scatter.png │ ├── find_in_pca.png │ ├── find_proba.png │ ├── find_results.png │ ├── flower_example1.png │ ├── flower_original.png │ ├── flower_predict_example.png │ ├── flowers_cluster3.png │ ├── flowers_eigenimages.png │ ├── flowers_plot1.png │ ├── flowers_plot2.png │ ├── flowers_predict_1.png │ ├── flowers_predict_2.png │ ├── flowers_predict_scatter.png │ ├── flowers_predict_scatter_all.png │ ├── flowers_scatter.png │ ├── flowers_scatter_imgs.png │ ├── flowers_scatter_imgs_mean.png │ ├── flowers_sil_vs_nrclusters.png │ ├── flowers_silhouette.png │ ├── flowers_unique.png │ ├── flowers_unique_mean.png │ ├── hog_example.png │ ├── hog_example2.png │ ├── hog_example88.png │ ├── performance_approaches.png │ ├── scatter_five.png │ ├── scatter_mnist_all.png │ ├── scatter_optimized.png │ ├── scatter_predict.png │ ├── scatter_three.png │ ├── schematic_overview.png │ ├── schematic_overview.pptx │ ├── silhouette_dbscan.png │ ├── silhouette_tsne.png │ ├── tsne_dbscan.png │ ├── unique_101.png │ └── unique_mean_101.png ├── index.html ├── make.bat ├── pages │ ├── doctrees │ │ ├── Abstract.doctree │ │ ├── Cluster Evaluation.doctree │ │ ├── Coding quality.doctree │ │ ├── Documentation.doctree │ │ ├── Examples.doctree │ │ ├── Feature Extraction.doctree │ │ ├── Find.doctree │ │ ├── Installation.doctree │ │ ├── Performance.doctree │ │ ├── Save and Load.doctree │ │ ├── clustimage.clustimage.doctree │ │ ├── core_functions.doctree │ │ ├── environment.pickle │ │ ├── index.doctree │ │ └── sponsor.doctree │ └── html │ │ ├── .buildinfo │ │ ├── Abstract.html │ │ ├── Cluster Evaluation.html │ │ ├── Coding quality.html │ │ ├── Documentation.html │ │ ├── Examples.html │ │ ├── Feature Extraction.html │ │ ├── Find.html │ │ ├── Installation.html │ │ ├── Performance.html │ │ ├── Save and Load.html │ │ ├── _images │ │ ├── 101_cluster.png │ │ ├── 101_cluster40.png │ │ ├── 101_explainedvar.png │ │ ├── 101_optimalclusters.png │ │ ├── 101_silhouette_plot.png │ │ ├── 101_tsne.png │ │ ├── 101_tsne_no_mean.png │ │ ├── best_digits.png │ │ ├── dbindex.png │ │ ├── dbindex_eq1.png │ │ ├── dbindex_eq2.jpg │ │ ├── dbindex_tsne.png │ │ ├── dbscan.png │ │ ├── dendrogram_five.png │ │ ├── dendrogram_optimized.png │ │ ├── dendrogram_three.png │ │ ├── digits_cluster1.png │ │ ├── digits_cluster5.png │ │ ├── digits_clusters.png │ │ ├── digits_dendrogram.png │ │ ├── digits_explained_var.png │ │ ├── digits_fig1.png │ │ ├── digits_fig21_tsne.png │ │ ├── digits_fig22_tsne.png │ │ ├── digits_fig2_tsne.png │ │ ├── digits_pca_dbindex.png │ │ ├── digits_unique.png │ │ ├── digits_unique1.png │ │ ├── digits_unique2.png │ │ ├── faces1.png │ │ ├── faces_cluster0.png │ │ ├── faces_cluster3.png │ │ ├── faces_scatter.png │ │ ├── faces_scatter_no_img.png │ │ ├── faces_set_max_clust.png │ │ ├── faces_sil_vs_nrclusters.png │ │ ├── faces_unique.png │ │ ├── find_digit.png │ │ ├── find_flowers.png │ │ ├── find_flowers_scatter.png │ │ ├── find_in_pca.png │ │ ├── find_proba.png │ │ ├── find_results.png │ │ ├── flower_example1.png │ │ ├── flower_original.png │ │ ├── flowers_cluster3.png │ │ ├── flowers_plot1.png │ │ ├── flowers_plot2.png │ │ ├── flowers_predict_1.png │ │ ├── flowers_predict_2.png │ │ ├── flowers_predict_scatter.png │ │ ├── flowers_predict_scatter_all.png │ │ ├── flowers_scatter.png │ │ ├── flowers_scatter_imgs.png │ │ ├── flowers_scatter_imgs_mean.png │ │ ├── flowers_sil_vs_nrclusters.png │ │ ├── flowers_silhouette.png │ │ ├── flowers_unique.png │ │ ├── flowers_unique_mean.png │ │ ├── hog_example.png │ │ ├── performance_approaches.png │ │ ├── scatter_five.png │ │ ├── scatter_mnist_all.png │ │ ├── scatter_optimized.png │ │ ├── scatter_three.png │ │ ├── schematic_overview.png │ │ ├── silhouette_dbscan.png │ │ ├── silhouette_tsne.png │ │ ├── tsne_dbscan.png │ │ └── unique_mean_101.png │ │ ├── _sources │ │ ├── Abstract.rst.txt │ │ ├── Cluster Evaluation.rst.txt │ │ ├── Coding quality.rst.txt │ │ ├── Documentation.rst.txt │ │ ├── Examples.rst.txt │ │ ├── Feature Extraction.rst.txt │ │ ├── Find.rst.txt │ │ ├── Installation.rst.txt │ │ ├── Performance.rst.txt │ │ ├── Save and Load.rst.txt │ │ ├── clustimage.clustimage.rst.txt │ │ ├── core_functions.rst.txt │ │ ├── index.rst.txt │ │ └── sponsor.rst.txt │ │ ├── _static │ │ ├── _sphinx_javascript_frameworks_compat.js │ │ ├── basic.css │ │ ├── css │ │ │ ├── badge_only.css │ │ │ ├── carbon_ads_hor.css │ │ │ ├── carbon_ads_ver.css │ │ │ ├── custom.css │ │ │ ├── custom_rectangle.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 │ │ ├── doctools.js │ │ ├── documentation_options.js │ │ ├── file.png │ │ ├── fonts │ │ │ ├── Lato │ │ │ │ ├── lato-bold.eot │ │ │ │ ├── lato-bold.ttf │ │ │ │ ├── lato-bold.woff │ │ │ │ ├── lato-bold.woff2 │ │ │ │ ├── lato-bolditalic.eot │ │ │ │ ├── lato-bolditalic.ttf │ │ │ │ ├── lato-bolditalic.woff │ │ │ │ ├── lato-bolditalic.woff2 │ │ │ │ ├── lato-italic.eot │ │ │ │ ├── lato-italic.ttf │ │ │ │ ├── lato-italic.woff │ │ │ │ ├── lato-italic.woff2 │ │ │ │ ├── lato-regular.eot │ │ │ │ ├── lato-regular.ttf │ │ │ │ ├── lato-regular.woff │ │ │ │ └── lato-regular.woff2 │ │ │ └── RobotoSlab │ │ │ │ ├── roboto-slab-v7-bold.eot │ │ │ │ ├── roboto-slab-v7-bold.ttf │ │ │ │ ├── roboto-slab-v7-bold.woff │ │ │ │ ├── roboto-slab-v7-bold.woff2 │ │ │ │ ├── roboto-slab-v7-regular.eot │ │ │ │ ├── roboto-slab-v7-regular.ttf │ │ │ │ ├── roboto-slab-v7-regular.woff │ │ │ │ └── roboto-slab-v7-regular.woff2 │ │ ├── jquery.js │ │ ├── js │ │ │ ├── badge_only.js │ │ │ ├── theme.js │ │ │ └── versions.js │ │ ├── language_data.js │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ └── sphinx_highlight.js │ │ ├── clustimage.clustimage.html │ │ ├── core_functions.html │ │ ├── genindex.html │ │ ├── index.html │ │ ├── objects.inv │ │ ├── py-modindex.html │ │ ├── search.html │ │ ├── searchindex.js │ │ └── sponsor.html └── source │ ├── Abstract.rst │ ├── Cluster Evaluation.rst │ ├── Coding quality.rst │ ├── Documentation.rst │ ├── Examples.rst │ ├── Feature Extraction.rst │ ├── Find.rst │ ├── Installation.rst │ ├── Performance.rst │ ├── Save and Load.rst │ ├── _static │ └── css │ │ ├── carbon_ads_hor.css │ │ ├── carbon_ads_ver.css │ │ ├── custom.css │ │ └── custom_rectangle.css │ ├── _templates │ ├── carbon_ads.html │ └── layout.html │ ├── add_bottom.add │ ├── add_carbon.add │ ├── add_top.add │ ├── clustimage.clustimage.rst │ ├── conf.py │ ├── core_functions.rst │ ├── helper.py │ ├── index.rst │ ├── requirements.txt │ └── sponsor.rst ├── notebooks ├── Handson_CNN_visualisatie_met_tsne.ipynb ├── Handson_embeddings_mixed_datasets.ipynb ├── Handson_embeddings_sensor.ipynb ├── Handson_quantile_norm.ipynb ├── Session_1_Clustering.ipynb ├── Session_2_Clustering_algorithms.ipynb ├── Session_3_Cluster_validation.ipynb ├── Session_4_Clusteval.ipynb ├── Session_5_Verkeersborden_clusteren.ipynb ├── Session_6_Categorical_clusteren.ipynb ├── Untitled.ipynb └── clustimage.ipynb ├── pyproject.toml ├── requirements-dev.txt ├── requirements.txt ├── requirements_additional.txt └── setup.py.deprecated /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [erdogant] 4 | buy_me_a_coffee: erdogant 5 | ko_fi: erdogant 6 | custom: [https://erdogant.github.io/clustimage/pages/html/Documentation.html] 7 | patreon: # Replace with a single Patreon username 8 | open_collective: # Replace with a single Open Collective username 9 | tidelift: # 10 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 11 | liberapay: # Replace with a single Liberapay username 12 | issuehunt: # Replace with a single IssueHunt username 13 | otechie: # Replace with a single Otechie username 14 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 15 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | name: "CodeQL" 7 | 8 | on: 9 | push: 10 | branches: [master] 11 | pull_request: 12 | # The branches below must be a subset of the branches above 13 | branches: [master] 14 | schedule: 15 | - cron: '0 17 * * 0' 16 | 17 | jobs: 18 | analyze: 19 | name: Analyze 20 | runs-on: ubuntu-latest 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | # Override automatic language detection by changing the below list 26 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 27 | language: ['python'] 28 | # Learn more... 29 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 30 | 31 | steps: 32 | - name: Checkout repository 33 | uses: actions/checkout@v2 34 | with: 35 | # We must fetch at least the immediate parents so that if this is 36 | # a pull request then we can checkout the head. 37 | fetch-depth: 2 38 | 39 | # If this run was triggered by a pull request event, then checkout 40 | # the head of the pull request instead of the merge commit. 41 | - run: git checkout HEAD^2 42 | if: ${{ github.event_name == 'pull_request' }} 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.github/workflows/pytest.yml: -------------------------------------------------------------------------------- 1 | name: Pytest for clustimage 2 | on: [push] 3 | 4 | jobs: 5 | test: 6 | name: Pytest (${{ matrix.python-version }}, ${{ matrix.os }}) 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | os: ["ubuntu-latest"] 12 | python-version: ["3.12"] 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Set up Python ${{ matrix.python-version }} 16 | uses: actions/setup-python@v4 17 | with: 18 | python-version: ${{ matrix.python-version }} 19 | cache: 'pip' 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install --upgrade pip 23 | pip install -r requirements_additional.txt 24 | pip install -r requirements.txt 25 | pip install pytest 26 | - name: Test with pytest 27 | run: | 28 | pytest 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | *.zip 9 | clustimage/data/ 10 | .pypirc 11 | app/ 12 | 13 | # Distribution / packaging 14 | .pylint.* 15 | .Python 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | pip-wheel-metadata/ 29 | share/python-wheels/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | MANIFEST 34 | 35 | # PyInstaller 36 | # Usually these files are written by a python script from a template 37 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 38 | *.manifest 39 | *.spec 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .nox/ 49 | .coverage 50 | .coverage.* 51 | .cache 52 | nosetests.xml 53 | coverage.xml 54 | *.cover 55 | *.py,cover 56 | .hypothesis/ 57 | .pytest_cache/ 58 | 59 | # Translations 60 | *.mo 61 | *.pot 62 | 63 | # Django stuff: 64 | *.log 65 | local_settings.py 66 | db.sqlite3 67 | db.sqlite3-journal 68 | 69 | # Flask stuff: 70 | instance/ 71 | .webassets-cache 72 | 73 | # Scrapy stuff: 74 | .scrapy 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | .python-version 91 | 92 | # pipenv 93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 96 | # install all needed dependencies. 97 | #Pipfile.lock 98 | 99 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 100 | __pypackages__/ 101 | 102 | # Celery stuff 103 | celerybeat-schedule 104 | celerybeat.pid 105 | 106 | # SageMath parsed files 107 | *.sage.py 108 | 109 | # Environments 110 | .env 111 | .venv 112 | env/ 113 | venv/ 114 | ENV/ 115 | env.bak/ 116 | venv.bak/ 117 | 118 | # Spyder project settings 119 | .spyderproject 120 | .spyproject 121 | 122 | # Rope project settings 123 | .ropeproject 124 | 125 | # mkdocs documentation 126 | /site 127 | 128 | # mypy 129 | .mypy_cache/ 130 | .dmypy.json 131 | dmypy.json 132 | 133 | # Pyre type checker 134 | .pyre/ 135 | .pylint.* -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | # YAML 1.2 2 | --- 3 | authors: 4 | - 5 | family-names: Taskesen 6 | given-names: Erdogan 7 | orcid: "https://orcid.org/0000-0002-3430-9618" 8 | cff-version: "1.1.0" 9 | date-released: 2021-11-08 10 | keywords: 11 | - "python" 12 | - "clustering" 13 | - "images" 14 | - "hog" 15 | - "pca" 16 | - "cluster evaluation" 17 | - "unsupervised" 18 | license: "MIT" 19 | message: "If you use this software, please cite it using these metadata." 20 | repository-code: "https://erdogant.github.io/clustimage" 21 | title: "Python package clustimage is for unsupervised clustering of images." 22 | version: "1.0.0" 23 | ... 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Erdogan Taskesen 4 | clustimage - Python package 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include clustimage/data/*.zip 2 | include clustimage/data/*.txt -------------------------------------------------------------------------------- /clustimage/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from datazets import get as import_example 4 | 5 | from clustimage.clustimage import Clustimage 6 | 7 | from clustimage.clustimage import ( 8 | import_example, 9 | url2disk, 10 | imscale, 11 | imresize, 12 | listdir, 13 | set_logger, 14 | move_files, 15 | ) 16 | 17 | __author__ = 'Erdogan Tasksen' 18 | __email__ = 'erdogant@gmail.com' 19 | __version__ = '1.6.22' 20 | 21 | # Setup root logger 22 | _logger = logging.getLogger('clustimage') 23 | _log_handler = logging.StreamHandler() 24 | _fmt = '[{asctime}] [{name}] [{levelname}] {msg}' 25 | _formatter = logging.Formatter(fmt=_fmt, style='{', datefmt='%d-%m-%Y %H:%M:%S') 26 | _log_handler.setFormatter(_formatter) 27 | _log_handler.setLevel(logging.DEBUG) 28 | _logger.addHandler(_log_handler) 29 | _logger.propagate = False 30 | 31 | # module level doc-string 32 | __doc__ = """ 33 | clustimage 34 | ===================================================================== 35 | 36 | Python package clustimage is to detect natural groups or clusters of images. 37 | 38 | The aim of ``clustimage`` is to detect natural groups or clusters of images. It works using a multi-step proces of carefully pre-processing the images, extracting the features, and evaluating the optimal number of clusters across the feature space. 39 | The optimal number of clusters are determined using well known methods suchs as *silhouette, dbindex, and derivatives* in combination with clustering methods, such as *agglomerative, kmeans, dbscan and hdbscan*. 40 | With ``clustimage`` we aim to determine the most robust clustering by efficiently searching across the parameter and evaluation the clusters. 41 | Besides clustering of images, the ``clustimage`` model can also be used to find the most similar images for a new unseen sample. 42 | 43 | Example 44 | ------- 45 | >>> from clustimage import Clustimage 46 | >>> 47 | >>> # Init with default settings 48 | >>> cl = Clustimage(method='pca') 49 | >>> 50 | >>> # load example dataset 51 | >>> X, y = cl.import_example(data='mnist') 52 | >>> 53 | >>> # Cluster digits 54 | >>> results = cl.fit_transform(X) 55 | >>> 56 | >>> # Cluster evaluation 57 | >>> cl.clusteval.plot() 58 | >>> cl.clusteval.scatter(cl.results['xycoord']) 59 | >>> cl.pca.plot() 60 | >>> 61 | >>> # Unique 62 | >>> cl.plot_unique(img_mean=False) 63 | >>> cl.results_unique.keys() 64 | >>> 65 | >>> # Scatter 66 | >>> cl.scatter(img_mean=False, zoom=3) 67 | >>> 68 | >>> # Plot clustered images 69 | >>> cl.plot(labels=8) 70 | >>> 71 | >>> # Plot dendrogram 72 | >>> cl.dendrogram() 73 | >>> 74 | >>> # Find images 75 | >>> results_find = cl.find(X[0,:], k=None, alpha=0.05) 76 | >>> cl.plot_find() 77 | >>> cl.scatter() 78 | >>> 79 | 80 | References 81 | ---------- 82 | https://github.com/erdogant/clustimage 83 | 84 | """ 85 | -------------------------------------------------------------------------------- /clustimage/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/clustimage/tests/__init__.py -------------------------------------------------------------------------------- /clustimage/tests/test_clustimage.py: -------------------------------------------------------------------------------- 1 | from clustimage import Clustimage 2 | import itertools as it 3 | import numpy as np 4 | import unittest 5 | 6 | class TestCLUSTIMAGE(unittest.TestCase): 7 | 8 | def test_import_data(self): 9 | cl = Clustimage() 10 | # Check initialization results 11 | assert cl.results=={'img': None, 'feat': None, 'xycoord': None, 'pathnames': None, 'labels': None, 'url': None} 12 | # Import flowers example 13 | X = cl.import_example(data='flowers') 14 | 15 | # Check numpy array imports 16 | assert cl.import_data(np.array(X)) 17 | assert cl.import_data(X[0]) 18 | assert cl.import_data([X[0]]) 19 | 20 | # Check output 21 | cl = Clustimage(dim=(128, 128), grayscale=False) 22 | _ = cl.import_data(X) 23 | assert np.all(np.isin([*cl.results.keys()], ['img', 'feat', 'xycoord', 'pathnames', 'labels', 'filenames', 'url'])) 24 | assert cl.results['img'].shape==(len(X), 49152) 25 | # Check grayscale parameter with imports 26 | cl = Clustimage(dim=(128, 128), grayscale=True) 27 | _ = cl.import_data(X) 28 | assert cl.results['img'].shape==(len(X), 65536) 29 | 30 | # Import mnist example 31 | X, y = cl.import_example(data='mnist') 32 | cl = Clustimage() 33 | _ = cl.import_data(X) 34 | assert np.all(np.isin([*cl.results.keys()], ['img', 'feat', 'xycoord', 'pathnames', 'labels', 'filenames', 'url'])) 35 | assert cl.results['img'].shape==(1797, 64) 36 | assert len(cl.results['pathnames'])==X.shape[0] 37 | assert len(cl.results['filenames'])==X.shape[0] 38 | 39 | def test_extract_feat(self): 40 | cl = Clustimage(method='pca') 41 | # Import flowers example 42 | X = cl.import_example(data='flowers') 43 | X = cl.import_data(X) 44 | _ = cl.extract_feat(X) 45 | assert cl.results['feat'].shape==(X['img'].shape[0], 153) 46 | 47 | # Init with settings such as PCA 48 | cl = Clustimage(method='hog', verbose=50) 49 | # load example with flowers 50 | pathnames = cl.import_example(data='flowers') 51 | # Cluster flowers 52 | assert cl.fit_transform(pathnames) 53 | # Read the unseen image. Note that the find functionality also performs exactly the same preprocessing steps as for the clustering. 54 | assert cl.find(pathnames[0:2], k=0, alpha=0.05) 55 | 56 | def test_embedding(self): 57 | cl = Clustimage(method='pca') 58 | # Import flowers example 59 | X = cl.import_example(data='flowers') 60 | X = cl.import_data(X) 61 | Xfeat = cl.extract_feat(X) 62 | out = cl.embedding(Xfeat) 63 | assert out.shape==(214,2) 64 | assert cl.results['xycoord'].shape==(X['img'].shape[0], 2) 65 | 66 | def test_cluster(self): 67 | cl = Clustimage() 68 | X = cl.import_example(data='flowers') 69 | results = cl.fit_transform(X) 70 | assert np.all(np.isin([*cl.results.keys()], ['img', 'feat', 'xycoord', 'pathnames', 'filenames', 'labels', 'url'])) 71 | assert len(cl.cluster())==len(X) 72 | 73 | # Parameters combinations to check 74 | param_grid = { 75 | 'cluster_space':['high','low'], 76 | 'cluster':['agglomerative'], 77 | 'evaluate' : ['silhouette', 'dbindex'], 78 | 'min_clust' : [2, 4, 6], 79 | 'max_clust' : [10, 20, 30], 80 | } 81 | # Make the combinatinos 82 | allNames = param_grid.keys() 83 | combinations = list(it.product(*(param_grid[Name] for Name in allNames))) 84 | for combination in combinations: 85 | labx = cl.cluster(cluster_space=combination[0], cluster=combination[1], evaluate=combination[2], metric='euclidean', linkage='ward', min_clust=combination[3], max_clust=combination[4]) 86 | assert len(labx)==len(X) 87 | 88 | def save_and_load(self): 89 | methods = ['pca', 'hog', 'pca-hog'] 90 | for method in methods: 91 | cl = Clustimage(method=method) 92 | # Init 93 | pathnames, y = cl.import_example(data='mnist') 94 | # Cluster flowers 95 | cl.fit_transform(pathnames) 96 | cl.save(overwrite=True) 97 | cl.load() 98 | assert cl.find(pathnames[0:5], k=10, alpha=0.05) 99 | 100 | def test_find(self): 101 | cl = Clustimage(method='pca', grayscale=False) 102 | # load example with flowers 103 | path_to_imgs = cl.import_example(data='flowers') 104 | # Extract features (raw images are not stored and handled per-image to save memory) 105 | results = cl.fit_transform(path_to_imgs, min_clust=10) 106 | # Check nr. of features 107 | featshape = cl.results['feat'].shape 108 | # Predict 109 | results_find = cl.find(path_to_imgs[0:5], k=None, alpha=0.05) 110 | assert cl.results['feat'].shape==featshape 111 | 112 | def test_predict(self): 113 | # Init 114 | cl = Clustimage(method='pca', grayscale=True, params_pca={'n_components':14}) 115 | # Load example with faces 116 | X = cl.import_example(data='flowers') 117 | # Cluster 118 | results = cl.fit_transform(X) 119 | assert np.all(np.isin([*cl.results.keys()], ['img', 'feat', 'xycoord', 'pathnames', 'filenames', 'labels', 'url'])) 120 | 121 | def test_fit_transform(self): 122 | # Example data 123 | cl = Clustimage() 124 | Xflowers = cl.import_example(data='flowers') 125 | Xflowers=Xflowers[0:50] 126 | Xdigits, y = cl.import_example(data='mnist') 127 | Xdigits=Xdigits[0:50,:] 128 | Xfaces, y = cl.import_example(data='faces') 129 | Xfaces=Xfaces[0:50,:] 130 | 131 | # Parameters combinations to check 132 | param_grid = { 133 | 'method':['ahash', 'pca', 'hog', None], 134 | 'embedding':['tsne', None], 135 | 'cluster_space' : ['high', 'low'], 136 | 'grayscale' : [True, False], 137 | 'dim' : [(8,8), (128,128), (256,256)], 138 | 'data' : [Xflowers, Xdigits] 139 | } 140 | # Make the combinatinos 141 | allNames = param_grid.keys() 142 | combinations = list(it.product(*(param_grid[Name] for Name in allNames))) 143 | # Iterate over all combinations 144 | for i, combination in enumerate(combinations): 145 | # init 146 | cl = Clustimage(method=combination[0], 147 | embedding=combination[1], 148 | grayscale=combination[3], 149 | dim=combination[4], 150 | verbose='debug', 151 | params_pca={'n_components': 50}, 152 | ) 153 | # Preprocessing and feature extraction 154 | assert cl.fit_transform(combination[5], cluster_space=combination[2]) 155 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | name: workflow for Codecov 2 | on: [push] 3 | jobs: 4 | run: 5 | runs-on: ${{ matrix.os }} 6 | strategy: 7 | matrix: 8 | os: [ubuntu-latest, macos-latest, windows-latest] 9 | env: 10 | OS: ${{ matrix.os }} 11 | PYTHON: '3.8' 12 | steps: 13 | - uses: actions/checkout@master 14 | - name: Setup Python 15 | uses: actions/setup-python@master 16 | with: 17 | python-version: 3.7 18 | - name: Generate coverage report 19 | run: | 20 | pip install pytest 21 | pip install pytest-cov 22 | pytest --cov=./ --cov-report=xml 23 | - name: Upload coverage to Codecov 24 | uses: codecov/codecov-action@v2 25 | with: 26 | token: ${{ secrets.CODECOV_TOKEN }} 27 | directory: ./coverage/reports/ 28 | env_vars: OS,PYTHON 29 | fail_ci_if_error: true 30 | files: ./coverage1.xml,./coverage2.xml 31 | flags: unittests 32 | name: codecov-umbrella 33 | path_to_write_report: ./coverage/codecov_report.txt 34 | verbose: true 35 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/.nojekyll -------------------------------------------------------------------------------- /docs/HOWTO.txt: -------------------------------------------------------------------------------- 1 | Steps 1-4 are already done in the template! Start at step 5 :) 2 | 3 | 1. Create a folder docs 4 | 2. By default, Jekyll does not build any files or directories with underscore. Include an empty .nojekyll file in the docs folder to turn off Jekyll. 5 | 3. In the docs folder, create an index.html file and redirect to ./html/index.html for example like this: 6 | 4. Change the Sphinx build directory to docs in your Makefile for example as follows: BUILDDIR = pages 7 | 8 | 9 | TODO for every project seperately: 10 | 11 | 5. Go to your /docs/ 12 | 6. Run 13 | 7. Run 14 | 8. Repeat steps 6-7 until you are satisfied. 15 | 16 | 9. add, commit and push the repo. 17 | 10. Go to your github settings: https://github.com/erdogant/bnlearn/settings 18 | a. Go to section: [GitHub Pages] 19 | b. Go to set [Source] 20 | c. Set to [master branch/docs folder] 21 | 11. visit https://erdogant.github.io/ 22 | 23 | To create pdf run: 24 | 12. sphinx-build -b pdf source build 25 | -------------------------------------------------------------------------------- /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 = pages 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /docs/figs/101_cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/101_cluster.png -------------------------------------------------------------------------------- /docs/figs/101_cluster40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/101_cluster40.png -------------------------------------------------------------------------------- /docs/figs/101_dendrogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/101_dendrogram.png -------------------------------------------------------------------------------- /docs/figs/101_explainedvar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/101_explainedvar.png -------------------------------------------------------------------------------- /docs/figs/101_optimalclusters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/101_optimalclusters.png -------------------------------------------------------------------------------- /docs/figs/101_silhouette_plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/101_silhouette_plot.png -------------------------------------------------------------------------------- /docs/figs/101_tsne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/101_tsne.png -------------------------------------------------------------------------------- /docs/figs/101_tsne_mean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/101_tsne_mean.png -------------------------------------------------------------------------------- /docs/figs/101_tsne_no_mean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/101_tsne_no_mean.png -------------------------------------------------------------------------------- /docs/figs/best_digits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/best_digits.png -------------------------------------------------------------------------------- /docs/figs/clothing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/clothing.png -------------------------------------------------------------------------------- /docs/figs/dbindex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/dbindex.png -------------------------------------------------------------------------------- /docs/figs/dbindex_eq1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/dbindex_eq1.png -------------------------------------------------------------------------------- /docs/figs/dbindex_eq2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/dbindex_eq2.jpg -------------------------------------------------------------------------------- /docs/figs/dbindex_tsne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/dbindex_tsne.png -------------------------------------------------------------------------------- /docs/figs/dbscan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/dbscan.png -------------------------------------------------------------------------------- /docs/figs/dendrogram_five.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/dendrogram_five.png -------------------------------------------------------------------------------- /docs/figs/dendrogram_optimized.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/dendrogram_optimized.png -------------------------------------------------------------------------------- /docs/figs/dendrogram_three.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/dendrogram_three.png -------------------------------------------------------------------------------- /docs/figs/digits_cluster1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/digits_cluster1.png -------------------------------------------------------------------------------- /docs/figs/digits_cluster4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/digits_cluster4.png -------------------------------------------------------------------------------- /docs/figs/digits_cluster5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/digits_cluster5.png -------------------------------------------------------------------------------- /docs/figs/digits_clusters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/digits_clusters.png -------------------------------------------------------------------------------- /docs/figs/digits_dendrogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/digits_dendrogram.png -------------------------------------------------------------------------------- /docs/figs/digits_explained_var.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/digits_explained_var.png -------------------------------------------------------------------------------- /docs/figs/digits_fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/digits_fig1.png -------------------------------------------------------------------------------- /docs/figs/digits_fig21_tsne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/digits_fig21_tsne.png -------------------------------------------------------------------------------- /docs/figs/digits_fig22_tsne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/digits_fig22_tsne.png -------------------------------------------------------------------------------- /docs/figs/digits_fig2_tsne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/digits_fig2_tsne.png -------------------------------------------------------------------------------- /docs/figs/digits_pca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/digits_pca.png -------------------------------------------------------------------------------- /docs/figs/digits_pca_dbindex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/digits_pca_dbindex.png -------------------------------------------------------------------------------- /docs/figs/digits_unique.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/digits_unique.png -------------------------------------------------------------------------------- /docs/figs/digits_unique1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/digits_unique1.png -------------------------------------------------------------------------------- /docs/figs/digits_unique2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/digits_unique2.png -------------------------------------------------------------------------------- /docs/figs/faces1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/faces1.png -------------------------------------------------------------------------------- /docs/figs/faces_cluster0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/faces_cluster0.png -------------------------------------------------------------------------------- /docs/figs/faces_cluster3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/faces_cluster3.png -------------------------------------------------------------------------------- /docs/figs/faces_scatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/faces_scatter.png -------------------------------------------------------------------------------- /docs/figs/faces_scatter_no_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/faces_scatter_no_img.png -------------------------------------------------------------------------------- /docs/figs/faces_set_max_clust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/faces_set_max_clust.png -------------------------------------------------------------------------------- /docs/figs/faces_sil_vs_nrclusters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/faces_sil_vs_nrclusters.png -------------------------------------------------------------------------------- /docs/figs/faces_unique.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/faces_unique.png -------------------------------------------------------------------------------- /docs/figs/find_digit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/find_digit.png -------------------------------------------------------------------------------- /docs/figs/find_flowers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/find_flowers.png -------------------------------------------------------------------------------- /docs/figs/find_flowers_scatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/find_flowers_scatter.png -------------------------------------------------------------------------------- /docs/figs/find_in_pca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/find_in_pca.png -------------------------------------------------------------------------------- /docs/figs/find_proba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/find_proba.png -------------------------------------------------------------------------------- /docs/figs/find_results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/find_results.png -------------------------------------------------------------------------------- /docs/figs/flower_example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flower_example1.png -------------------------------------------------------------------------------- /docs/figs/flower_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flower_original.png -------------------------------------------------------------------------------- /docs/figs/flower_predict_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flower_predict_example.png -------------------------------------------------------------------------------- /docs/figs/flowers_cluster3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flowers_cluster3.png -------------------------------------------------------------------------------- /docs/figs/flowers_eigenimages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flowers_eigenimages.png -------------------------------------------------------------------------------- /docs/figs/flowers_plot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flowers_plot1.png -------------------------------------------------------------------------------- /docs/figs/flowers_plot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flowers_plot2.png -------------------------------------------------------------------------------- /docs/figs/flowers_predict_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flowers_predict_1.png -------------------------------------------------------------------------------- /docs/figs/flowers_predict_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flowers_predict_2.png -------------------------------------------------------------------------------- /docs/figs/flowers_predict_scatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flowers_predict_scatter.png -------------------------------------------------------------------------------- /docs/figs/flowers_predict_scatter_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flowers_predict_scatter_all.png -------------------------------------------------------------------------------- /docs/figs/flowers_scatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flowers_scatter.png -------------------------------------------------------------------------------- /docs/figs/flowers_scatter_imgs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flowers_scatter_imgs.png -------------------------------------------------------------------------------- /docs/figs/flowers_scatter_imgs_mean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flowers_scatter_imgs_mean.png -------------------------------------------------------------------------------- /docs/figs/flowers_sil_vs_nrclusters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flowers_sil_vs_nrclusters.png -------------------------------------------------------------------------------- /docs/figs/flowers_silhouette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flowers_silhouette.png -------------------------------------------------------------------------------- /docs/figs/flowers_unique.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flowers_unique.png -------------------------------------------------------------------------------- /docs/figs/flowers_unique_mean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/flowers_unique_mean.png -------------------------------------------------------------------------------- /docs/figs/hog_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/hog_example.png -------------------------------------------------------------------------------- /docs/figs/hog_example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/hog_example2.png -------------------------------------------------------------------------------- /docs/figs/hog_example88.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/hog_example88.png -------------------------------------------------------------------------------- /docs/figs/performance_approaches.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/performance_approaches.png -------------------------------------------------------------------------------- /docs/figs/scatter_five.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/scatter_five.png -------------------------------------------------------------------------------- /docs/figs/scatter_mnist_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/scatter_mnist_all.png -------------------------------------------------------------------------------- /docs/figs/scatter_optimized.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/scatter_optimized.png -------------------------------------------------------------------------------- /docs/figs/scatter_predict.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/scatter_predict.png -------------------------------------------------------------------------------- /docs/figs/scatter_three.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/scatter_three.png -------------------------------------------------------------------------------- /docs/figs/schematic_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/schematic_overview.png -------------------------------------------------------------------------------- /docs/figs/schematic_overview.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/schematic_overview.pptx -------------------------------------------------------------------------------- /docs/figs/silhouette_dbscan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/silhouette_dbscan.png -------------------------------------------------------------------------------- /docs/figs/silhouette_tsne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/silhouette_tsne.png -------------------------------------------------------------------------------- /docs/figs/tsne_dbscan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/tsne_dbscan.png -------------------------------------------------------------------------------- /docs/figs/unique_101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/unique_101.png -------------------------------------------------------------------------------- /docs/figs/unique_mean_101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/figs/unique_mean_101.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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=pages 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/pages/doctrees/Abstract.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/doctrees/Abstract.doctree -------------------------------------------------------------------------------- /docs/pages/doctrees/Cluster Evaluation.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/doctrees/Cluster Evaluation.doctree -------------------------------------------------------------------------------- /docs/pages/doctrees/Coding quality.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/doctrees/Coding quality.doctree -------------------------------------------------------------------------------- /docs/pages/doctrees/Documentation.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/doctrees/Documentation.doctree -------------------------------------------------------------------------------- /docs/pages/doctrees/Examples.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/doctrees/Examples.doctree -------------------------------------------------------------------------------- /docs/pages/doctrees/Feature Extraction.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/doctrees/Feature Extraction.doctree -------------------------------------------------------------------------------- /docs/pages/doctrees/Find.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/doctrees/Find.doctree -------------------------------------------------------------------------------- /docs/pages/doctrees/Installation.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/doctrees/Installation.doctree -------------------------------------------------------------------------------- /docs/pages/doctrees/Performance.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/doctrees/Performance.doctree -------------------------------------------------------------------------------- /docs/pages/doctrees/Save and Load.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/doctrees/Save and Load.doctree -------------------------------------------------------------------------------- /docs/pages/doctrees/clustimage.clustimage.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/doctrees/clustimage.clustimage.doctree -------------------------------------------------------------------------------- /docs/pages/doctrees/core_functions.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/doctrees/core_functions.doctree -------------------------------------------------------------------------------- /docs/pages/doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/doctrees/environment.pickle -------------------------------------------------------------------------------- /docs/pages/doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/doctrees/index.doctree -------------------------------------------------------------------------------- /docs/pages/doctrees/sponsor.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/doctrees/sponsor.doctree -------------------------------------------------------------------------------- /docs/pages/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file records the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 45f041195daabdc7191f26f7a71a26d6 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/pages/html/_images/101_cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/101_cluster.png -------------------------------------------------------------------------------- /docs/pages/html/_images/101_cluster40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/101_cluster40.png -------------------------------------------------------------------------------- /docs/pages/html/_images/101_explainedvar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/101_explainedvar.png -------------------------------------------------------------------------------- /docs/pages/html/_images/101_optimalclusters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/101_optimalclusters.png -------------------------------------------------------------------------------- /docs/pages/html/_images/101_silhouette_plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/101_silhouette_plot.png -------------------------------------------------------------------------------- /docs/pages/html/_images/101_tsne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/101_tsne.png -------------------------------------------------------------------------------- /docs/pages/html/_images/101_tsne_no_mean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/101_tsne_no_mean.png -------------------------------------------------------------------------------- /docs/pages/html/_images/best_digits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/best_digits.png -------------------------------------------------------------------------------- /docs/pages/html/_images/dbindex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/dbindex.png -------------------------------------------------------------------------------- /docs/pages/html/_images/dbindex_eq1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/dbindex_eq1.png -------------------------------------------------------------------------------- /docs/pages/html/_images/dbindex_eq2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/dbindex_eq2.jpg -------------------------------------------------------------------------------- /docs/pages/html/_images/dbindex_tsne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/dbindex_tsne.png -------------------------------------------------------------------------------- /docs/pages/html/_images/dbscan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/dbscan.png -------------------------------------------------------------------------------- /docs/pages/html/_images/dendrogram_five.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/dendrogram_five.png -------------------------------------------------------------------------------- /docs/pages/html/_images/dendrogram_optimized.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/dendrogram_optimized.png -------------------------------------------------------------------------------- /docs/pages/html/_images/dendrogram_three.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/dendrogram_three.png -------------------------------------------------------------------------------- /docs/pages/html/_images/digits_cluster1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/digits_cluster1.png -------------------------------------------------------------------------------- /docs/pages/html/_images/digits_cluster5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/digits_cluster5.png -------------------------------------------------------------------------------- /docs/pages/html/_images/digits_clusters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/digits_clusters.png -------------------------------------------------------------------------------- /docs/pages/html/_images/digits_dendrogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/digits_dendrogram.png -------------------------------------------------------------------------------- /docs/pages/html/_images/digits_explained_var.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/digits_explained_var.png -------------------------------------------------------------------------------- /docs/pages/html/_images/digits_fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/digits_fig1.png -------------------------------------------------------------------------------- /docs/pages/html/_images/digits_fig21_tsne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/digits_fig21_tsne.png -------------------------------------------------------------------------------- /docs/pages/html/_images/digits_fig22_tsne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/digits_fig22_tsne.png -------------------------------------------------------------------------------- /docs/pages/html/_images/digits_fig2_tsne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/digits_fig2_tsne.png -------------------------------------------------------------------------------- /docs/pages/html/_images/digits_pca_dbindex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/digits_pca_dbindex.png -------------------------------------------------------------------------------- /docs/pages/html/_images/digits_unique.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/digits_unique.png -------------------------------------------------------------------------------- /docs/pages/html/_images/digits_unique1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/digits_unique1.png -------------------------------------------------------------------------------- /docs/pages/html/_images/digits_unique2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/digits_unique2.png -------------------------------------------------------------------------------- /docs/pages/html/_images/faces1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/faces1.png -------------------------------------------------------------------------------- /docs/pages/html/_images/faces_cluster0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/faces_cluster0.png -------------------------------------------------------------------------------- /docs/pages/html/_images/faces_cluster3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/faces_cluster3.png -------------------------------------------------------------------------------- /docs/pages/html/_images/faces_scatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/faces_scatter.png -------------------------------------------------------------------------------- /docs/pages/html/_images/faces_scatter_no_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/faces_scatter_no_img.png -------------------------------------------------------------------------------- /docs/pages/html/_images/faces_set_max_clust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/faces_set_max_clust.png -------------------------------------------------------------------------------- /docs/pages/html/_images/faces_sil_vs_nrclusters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/faces_sil_vs_nrclusters.png -------------------------------------------------------------------------------- /docs/pages/html/_images/faces_unique.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/faces_unique.png -------------------------------------------------------------------------------- /docs/pages/html/_images/find_digit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/find_digit.png -------------------------------------------------------------------------------- /docs/pages/html/_images/find_flowers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/find_flowers.png -------------------------------------------------------------------------------- /docs/pages/html/_images/find_flowers_scatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/find_flowers_scatter.png -------------------------------------------------------------------------------- /docs/pages/html/_images/find_in_pca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/find_in_pca.png -------------------------------------------------------------------------------- /docs/pages/html/_images/find_proba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/find_proba.png -------------------------------------------------------------------------------- /docs/pages/html/_images/find_results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/find_results.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flower_example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flower_example1.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flower_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flower_original.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flowers_cluster3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flowers_cluster3.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flowers_plot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flowers_plot1.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flowers_plot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flowers_plot2.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flowers_predict_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flowers_predict_1.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flowers_predict_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flowers_predict_2.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flowers_predict_scatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flowers_predict_scatter.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flowers_predict_scatter_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flowers_predict_scatter_all.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flowers_scatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flowers_scatter.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flowers_scatter_imgs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flowers_scatter_imgs.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flowers_scatter_imgs_mean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flowers_scatter_imgs_mean.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flowers_sil_vs_nrclusters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flowers_sil_vs_nrclusters.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flowers_silhouette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flowers_silhouette.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flowers_unique.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flowers_unique.png -------------------------------------------------------------------------------- /docs/pages/html/_images/flowers_unique_mean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/flowers_unique_mean.png -------------------------------------------------------------------------------- /docs/pages/html/_images/hog_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/hog_example.png -------------------------------------------------------------------------------- /docs/pages/html/_images/performance_approaches.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/performance_approaches.png -------------------------------------------------------------------------------- /docs/pages/html/_images/scatter_five.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/scatter_five.png -------------------------------------------------------------------------------- /docs/pages/html/_images/scatter_mnist_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/scatter_mnist_all.png -------------------------------------------------------------------------------- /docs/pages/html/_images/scatter_optimized.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/scatter_optimized.png -------------------------------------------------------------------------------- /docs/pages/html/_images/scatter_three.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/scatter_three.png -------------------------------------------------------------------------------- /docs/pages/html/_images/schematic_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/schematic_overview.png -------------------------------------------------------------------------------- /docs/pages/html/_images/silhouette_dbscan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/silhouette_dbscan.png -------------------------------------------------------------------------------- /docs/pages/html/_images/silhouette_tsne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/silhouette_tsne.png -------------------------------------------------------------------------------- /docs/pages/html/_images/tsne_dbscan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/tsne_dbscan.png -------------------------------------------------------------------------------- /docs/pages/html/_images/unique_mean_101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_images/unique_mean_101.png -------------------------------------------------------------------------------- /docs/pages/html/_sources/Abstract.rst.txt: -------------------------------------------------------------------------------- 1 | 2 | Abstract 3 | '''''''' 4 | 5 | **Background** 6 | 7 | Image recognition is a computer vision task for identifying and verifying objects/persons on a photograph. 8 | We can seperate the image recognition task into the two broad tasks, namely the supervised and unsupervised task. 9 | In case of the supervised task, we have to classify an image into a fixed number of learned categories. 10 | In case of the unsupervised task, we do not depend on the fact that training data is required but we can interpret the input data and find natural groups or clusters. 11 | 12 | **Aim** 13 | 14 | The aim of ``clustimage`` is to detect natural groups or clusters of images. It works using a multi-step proces of carefully pre-processing the images, extracting the features, and evaluating the optimal number of clusters across the feature space. 15 | The optimal number of clusters are determined using well known methods suchs as *silhouette, dbindex, and derivatives* in combination with clustering methods, such as *agglomerative, kmeans, dbscan and hdbscan*. 16 | With ``clustimage`` we aim to determine the most robust clustering by efficiently searching across the parameter and evaluation the clusters. 17 | Besides clustering of images, the ``clustimage`` model can also be used to find the most similar images for a new unseen sample. 18 | 19 | **Results** 20 | 21 | ``clustimage`` is Python package for unsupervised clustering of images after carefully pre-processing the images, extracting the features, and evaluating the optimal number of clustering in the high-dimensional feature space. 22 | ``clustimage`` does not depend on pre-trained neural networks that require many package-dependencies but is build on core statistics, such as Principal component analysis (PCA), and/or Histogram of Oriented Gradients (HOG). 23 | The input for the model can be a NxM array for which each rows is a flattened rgb/gray image, or it can be a target directory or the full path to a list of images. 24 | More advantages of ``clustimage`` is: 25 | 26 | * It can group any set of images on similarity. 27 | * It can determine the unique() images. 28 | * it can find similar images given an input image. 29 | * It is build on core statistics, such as PCA, HOG for feature extraction, and therefore it does not has a dependency block. 30 | * It does not require a learning proces. 31 | * It works out of the box. 32 | * It provides explainable results. 33 | * It provided many plots to improve understanding of the feature-space and sample-sample relationships. 34 | 35 | 36 | Schematic overview 37 | ''''''''''''''''''' 38 | 39 | The schematic overview of our approach is as following: 40 | 41 | .. _schematic_overview: 42 | 43 | .. figure:: ../figs/schematic_overview.png 44 | 45 | 46 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/pages/html/_sources/Cluster Evaluation.rst.txt: -------------------------------------------------------------------------------- 1 | Gridsearch 2 | ############## 3 | 4 | For the detection of the optimal number of clusters, the python library ``clusteval`` is utilized to evaluate the **goodness** of clusters. 5 | The clustering approaches can be set to *agglomerative*, *kmeans*, *dbscan* and *hdbscan*, for which the ``clusteval`` library then searches across the space of clusters and method-parameters to determine the optimal number of clusters given the input dataset. 6 | 7 | **Cluster evaluation can be performed based on:** 8 | 9 | * Silhouette scores 10 | * DBindex 11 | * Derivative method 12 | 13 | Lets load the **digits** dataset and see how the different methods detects the optimal number of clusters. 14 | 15 | .. code:: python 16 | 17 | from clustimage import Clustimage 18 | # init 19 | cl = Clustimage() 20 | # Example data 21 | X, y = cl.import_example(data='mnist') 22 | 23 | 24 | Silhouette 25 | ############## 26 | 27 | The silhouette value is a measure of how similar a sample is to its own cluster (cohesion) compared to other clusters (separation). 28 | The scores ranges between [−1, 1], where a high value indicates that the object is well matched to its own cluster and poorly matched to neighboring clusters. 29 | The silhouette approach is thus a sample-wise approach, which means that for each sample, a silhouette score is computed and if most samples have a high value, then the clustering configuration is appropriate. 30 | If many points have a low or negative value, then the clustering configuration may have too many or too few clusters. 31 | The silhouette can be calculated with any distance metric, such as the Euclidean distance or the Manhattan distance. 32 | 33 | .. code:: python 34 | 35 | # Feature extraction and cluster evaluation 36 | results = cl.fit_transform(X, evaluate='silhouette', cluster='agglomerative') 37 | # Cluster using different (evaluation) method. 38 | # labels = cl.cluster(evaluate='silhouette', cluster='agglomerative', cluster_space='lower') 39 | # Plot the cluster evaluation 40 | cl.clusteval.plot() 41 | # Scatter 42 | cl.scatter(zoom=3) 43 | # Dendrogram 44 | cl.dendrogram() 45 | 46 | 47 | .. |figCE0| image:: ../figs/digits_clusters.png 48 | .. |figCE6| image:: ../figs/silhouette_tsne.png 49 | 50 | .. table:: Left: The number of clusters vs silhouette scores. Right: tSNE plot coloured on the cluster-labels. 51 | :align: center 52 | 53 | +----------+----------+ 54 | | |figCE0| | |figCE6| | 55 | +----------+----------+ 56 | 57 | 58 | The silhouette plot shows that the *#clusters* value of 11 is the best pick for the given data as the majority of clusters are above the average silhouette scores and does not widely fluctuate in the size of the silhouette plots. 59 | Note that the cluster evaluation is peformed on the high-dimensional feature space, in this case the top 50PCs. For illustration purposes, the 2D-space is shown and coloured on the detected cluster-labels. 60 | 61 | 62 | .. |figCE1| image:: ../figs/digits_fig1.png 63 | 64 | .. table:: Left: the sample-wise silhouette scores. Rright: First 2 PCs coloured on the cluster-labels. 65 | :align: center 66 | 67 | +----------+ 68 | | |figCE1| | 69 | +----------+ 70 | 71 | 72 | 73 | DBindex 74 | ############## 75 | 76 | The Davies–Bouldin index (DBI), is a metric for evaluating clustering algorithms. 77 | This is an internal evaluation scheme, which means that the clustering result is evaluated based on the data clustered itself (internal information). 78 | The lower the value, the tighter the clusters and the seperation between clusters. 79 | 80 | .. |figCE2| image:: ../figs/dbindex_eq1.png 81 | .. |figCE3| image:: ../figs/dbindex_eq2.jpg 82 | 83 | .. table:: The DB index for k number of clusters is defined as : 84 | :align: center 85 | 86 | +----------+----------+ 87 | | |figCE2| | |figCE3| | 88 | +----------+----------+ 89 | 90 | 91 | .. code:: python 92 | 93 | # After the fit transform you can directly try some other approaches for clustering without doing the preprocessing steps. 94 | # Cluster differently using directly the extracted features. 95 | results = cl.cluster(cluster='agglomerative', method='dbindex') 96 | 97 | # Scatter 98 | cl.scatter() 99 | # Dendrogram with cluster evalution 100 | cl.dendrogram() 101 | 102 | 103 | .. |figCE4| image:: ../figs/dbindex.png 104 | .. |figCE5| image:: ../figs/dbindex_tsne.png 105 | 106 | .. table:: Left: the DBindex cluster evaluation. Right: The tSNE plot coloured based on the cluster-labels. 107 | :align: center 108 | 109 | +----------+----------+ 110 | | |figCE4| | |figCE5| | 111 | +----------+----------+ 112 | 113 | The DBindex scores lowers gradually and stops at 24 clusters. This limit is the maximum search space by default. The search space can be altered using ``min_clust`` and ``max_clust`` in the function function :func:`clustimage.clustimage.Clustimage.fit_transform`. 114 | Note that the cluster evaluation is peformed on the high-dimensional feature space, in this case the top 50PCs. For illustration purposes, the 2D-space is coloured on the detected cluster-labels. 115 | 116 | 117 | DBSCAN 118 | ############## 119 | 120 | Density-Based Spatial Clustering of Applications with Noise is an clustering approach that finds core samples of high density and expands clusters from them. 121 | This works especially good when having samples which contains clusters of similar density. This approach is recommended in combination with a low-dimensional space, such as the coordinates from tSNE. 122 | 123 | .. code:: python 124 | 125 | # Feature extraction and cluster evaluation on the low-dimensional space 126 | results = cl.cluster(evaluate='silhouette', cluster='dbscan', cluster_space='low') 127 | # Scatter 128 | cl.scatter() 129 | # Dendrogram with cluster evalution 130 | cl.dendrogram() 131 | 132 | 133 | .. |figCE7| image:: ../figs/dbscan.png 134 | .. |figCE8| image:: ../figs/tsne_dbscan.png 135 | 136 | .. table:: Left: the DBindex cluster evaluation. Right: The tSNE plot coloured based on the cluster-labels. 137 | :align: center 138 | 139 | +----------+----------+ 140 | | |figCE7| | |figCE8| | 141 | +----------+----------+ 142 | 143 | 144 | .. |figCE9| image:: ../figs/silhouette_dbscan.png 145 | 146 | .. table:: Left: the DBindex cluster evaluation. Right: The tSNE plot coloured based on the cluster-labels. 147 | :align: center 148 | 149 | +----------+ 150 | | |figCE9| | 151 | +----------+ 152 | 153 | 154 | 155 | 156 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/pages/html/_sources/Coding quality.rst.txt: -------------------------------------------------------------------------------- 1 | 2 | Coding quality 3 | ''''''''''''''''''''' 4 | 5 | I value software quality. Higher quality software has fewer defects, better security, and better performance, which leads to happier users who can work more effectively. 6 | Code reviews are an effective method for improving software quality. McConnell (2004) suggests that unit testing finds approximately 25% of defects, function testing 35%, integration testing 45%, and code review 55-60%. 7 | While this means that none of these methods are good enough on their own and that they should be combined, clearly code review is an essential tool here. 8 | 9 | This library is therefore developed with several techniques, such as coding styling, low complexity, docstrings, reviews, and unit tests. 10 | Such conventions are helpfull to improve the quality, make the code cleaner and more understandable but alos to trace future bugs, and spot syntax errors. 11 | 12 | 13 | library 14 | -------- 15 | 16 | The file structure of the generated package looks like: 17 | 18 | 19 | .. code-block:: bash 20 | 21 | path/to/clustimage/ 22 | ├── .editorconfig 23 | ├── .gitignore 24 | ├── .pre-commit-config.yml 25 | ├── .prospector.yml 26 | ├── CHANGELOG.rst 27 | ├── docs 28 | │   ├── conf.py 29 | │   ├── index.rst 30 | │   └── ... 31 | ├── LICENSE 32 | ├── MANIFEST.in 33 | ├── NOTICE 34 | ├── clustimage 35 | │   ├── __init__.py 36 | │   ├── __version__.py 37 | │   └── clustimage.py 38 | ├── README.md 39 | ├── requirements.txt 40 | ├── setup.cfg 41 | ├── setup.py 42 | └── tests 43 | ├── __init__.py 44 | └── test_clustimage.py 45 | 46 | 47 | Style 48 | ------- 49 | 50 | This library is compliant with the PEP-8 standards. 51 | PEP stands for Python Enhancement Proposal and sets a baseline for the readability of Python code. 52 | Each public function contains a docstring that is based on numpy standards. 53 | 54 | 55 | Complexity 56 | ------------ 57 | 58 | This library has been developed by using measures that help decreasing technical debt. 59 | Version 0.1.0 of the ``clustimage`` library scored, according the code analyzer: **VALUE**, for which values > 0 are good and 10 is a maximum score. 60 | Developing software with low(er) technical dept may take extra development time, but has many advantages: 61 | 62 | * Higher quality code 63 | * easier maintanable 64 | * Less prone to bugs and errors 65 | * Higher security 66 | 67 | 68 | Unit tests 69 | ------------ 70 | 71 | The use of unit tests is essential to garantee a consistent output of developed functions. 72 | The following tests are secured using :func:`tests.test_clustimage`: 73 | 74 | * The input are checked. 75 | * The output values are checked and whether they are encoded properly. 76 | * The check of whether parameters are handled correctly. 77 | 78 | 79 | .. code-block:: bash 80 | 81 | pytest tests\test_clustimage.py 82 | 83 | ====================================== test session starts ====================================== 84 | platform win32 -- Python 3.6.10, pytest-5.4.0, py-1.8.1, pluggy-0.13.1 85 | collected 3 items 86 | 87 | tests\test_clustimage.py ... [100%] 88 | 89 | ======================================= warnings summary ======================================== 90 | tests/test_clustimage.py::test_plot 91 | 92 | =========================== 3 passed, 1 warning in 1254.97s (0:20:54) =========================== 93 | 94 | 95 | 96 | 97 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/pages/html/_sources/Documentation.rst.txt: -------------------------------------------------------------------------------- 1 | Sponsor 2 | ############ 3 | 4 | .. include:: sponsor.rst 5 | 6 | 7 | Blog 8 | ####### 9 | 10 | .. note:: 11 | For usage and more details, read the Medium Blog: `A step-by-step guide for clustering images `_ 12 | 13 | Github 14 | ####### 15 | 16 | .. note:: 17 | Source code of clustimage can be found at `Github `_ 18 | 19 | 20 | Colab Notebook 21 | ################# 22 | 23 | .. note:: 24 | Experiment with clustering images using the `Colab notebook`_. 25 | 26 | .. _Colab notebook: https://colab.research.google.com/github/erdogant/clustimage/blob/master/notebooks/clustimage.ipynb 27 | 28 | 29 | Citing 30 | ######### 31 | 32 | .. note:: 33 | Bibtex can be found at the right side at the `github page `_. 34 | 35 | 36 | 37 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/pages/html/_sources/Installation.rst.txt: -------------------------------------------------------------------------------- 1 | Installation 2 | ################ 3 | 4 | Create environment 5 | ********************** 6 | 7 | If desired, install ``clustimage`` from an isolated Python environment using conda: 8 | 9 | .. code-block:: python 10 | 11 | conda create -n env_clustimage python=3.8 12 | conda activate env_clustimage 13 | 14 | 15 | Pypi 16 | ********************** 17 | 18 | .. code-block:: console 19 | 20 | # Install from Pypi: 21 | pip install clustimage 22 | 23 | # Force update to latest version 24 | pip install -U clustimage 25 | 26 | 27 | Github source 28 | ************************************ 29 | 30 | .. code-block:: console 31 | 32 | # Install directly from github 33 | pip install git+https://github.com/erdogant/clustimage 34 | 35 | 36 | Uninstalling 37 | ################ 38 | 39 | Remove environment 40 | ********************** 41 | 42 | .. code-block:: console 43 | 44 | # List all the active environments. clustimage should be listed. 45 | conda env list 46 | 47 | # Remove the clustimage environment 48 | conda env remove --name clustimage 49 | 50 | # List all the active environments. clustimage should be absent. 51 | conda env list 52 | 53 | 54 | Remove installation 55 | ********************** 56 | 57 | Note that the removal of the environment will also remove the ``clustimage`` installation. 58 | 59 | .. code-block:: console 60 | 61 | # Install from Pypi: 62 | pip uninstall clustimage 63 | 64 | 65 | 66 | Quickstart 67 | ********************** 68 | 69 | A quick example how to learn a model on a given dataset. 70 | 71 | .. code:: python 72 | 73 | # Import library 74 | from clustimage import Clustimage 75 | 76 | # init with default parameters 77 | cl = Clustimage() 78 | 79 | # load example with flowers 80 | path_to_imgs = cl.import_example(data='flowers') 81 | 82 | # Run the model to find the optimal clusters 83 | results = cl.fit_transform(path_to_imgs, min_clust=10) 84 | 85 | # Cluster evaluation plot 86 | cl.clustimage.plot() 87 | 88 | # Unique images 89 | cl.results_unique.keys() 90 | cl.plot_unique(img_mean=False) 91 | 92 | # Scatter 93 | cl.scatter(dotsize=50, img_mean=False) 94 | 95 | # Plot clustered images 96 | cl.plot(labels=0) 97 | 98 | # Plot dendrogram 99 | cl.dendrogram() 100 | 101 | # Predict 102 | results_find = cl.find(path_to_imgs[0:5], k=None, alpha=0.05) 103 | cl.plot_find() 104 | cl.scatter() 105 | 106 | 107 | 108 | 109 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/pages/html/_sources/Performance.rst.txt: -------------------------------------------------------------------------------- 1 | 2 | Performance 3 | '''''''''''''''''''''' 4 | To measure the performance of various methods implementend in ``clustimage``, we can use the **digits** dataset to determine the match between clustered sampels and the true label. 5 | It can be seen that multiple different parameters still result in similar good performance based on the results below. 6 | 7 | The following peace of code clusters the **digit** images, compares the detected cluster labels with the true label, and finally computes the accuracy. 8 | 9 | .. code:: python 10 | 11 | import matplotlib.pyplot as plt 12 | from sklearn.datasets import load_digits 13 | from clustimage import Clustimage 14 | import classeval as clf 15 | import itertools as it 16 | from sklearn.metrics import accuracy_score 17 | 18 | # Load example data 19 | digits = load_digits(n_class=10) 20 | X, y_true = digits.data, digits.target 21 | 22 | param_grid = { 23 | 'method':['pca', 'hog', None], 24 | 'evaluate' : ['silhouette', 'dbindex', 'derivative'], 25 | 'cluster_space' : ['low', 'high'], 26 | } 27 | 28 | scores = [] 29 | labels = [] 30 | allNames = param_grid.keys() 31 | combinations = list(it.product(*(param_grid[Name] for Name in allNames))) 32 | 33 | # Iterate over all combinations 34 | for combination in combinations: 35 | # Initialize 36 | cl = Clustimage(method=combination[0]) 37 | 38 | # Preprocessing, feature extraction and cluster evaluation 39 | results = cl.fit_transform(X, cluster_space=combination[2], evaluate=combination[1]) 40 | 41 | # Compute confmat 42 | cm = clf.confmatrix.eval(y_true, results['labels'], normalize=False) 43 | 44 | # Transform numbers to make it comparible 45 | y_pred = results['labels'] 46 | cm_argmax = cm['confmat'].argmax(axis=0) 47 | y_pred_ = np.array([cm_argmax[i] for i in y_pred]) 48 | 49 | # Compute again confmat 50 | cm = clf.confmatrix.eval(y_true, y_pred_, normalize=False) 51 | fig,ax = clf.confmatrix.plot(cm) 52 | ax.set_title('Feature extraction: [%s]\nCluster evaluation with [%s] in [%s] dimension' %(combination[0], combination[1], combination[2]), fontsize=16) 53 | plt.pause(0.1) 54 | 55 | # Store scores and labels 56 | scores.append(accuracy_score(y_true,y_pred_)) 57 | labels.append(str(combination[0]) + ' - ' + combination[1] + ' - ' + combination[2]) 58 | 59 | # Make plot 60 | import numpy as np 61 | scores=np.array(scores) 62 | labels=np.array(labels) 63 | isort=np.argsort(scores) 64 | plt.figure(figsize=(12,6)) 65 | plt.plot(np.arange(0,len(scores)), scores[isort]) 66 | plt.xticks(np.arange(0,len(scores)), labels[isort], rotation='vertical') 67 | plt.margins(0.2) 68 | plt.title("Comparison of various approaches.", fontsize=14) 69 | plt.grid(True) 70 | 71 | 72 | .. |figP1| image:: ../figs/performance_approaches.png 73 | 74 | .. table:: Comparison of the performance for the digits dataset using various methods. 75 | :align: center 76 | 77 | +----------+ 78 | | |figP1| | 79 | +----------+ 80 | 81 | 82 | .. |figP2| image:: ../figs/best_digits.png 83 | .. |figP3| image:: ../figs/digits_pca_dbindex.png 84 | 85 | .. table:: Results of the best two approaches. 86 | :align: center 87 | 88 | +----------+----------+ 89 | | |figP2| | |figP3| | 90 | +----------+----------+ 91 | 92 | 93 | 94 | 95 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/pages/html/_sources/Save and Load.rst.txt: -------------------------------------------------------------------------------- 1 | Saving 2 | ########## 3 | 4 | Saving and loading models can be used to restore previous results. 5 | In order to accomplish this, we created two functions: function :func:`clustimage.clustimage.Clustimage.save`. and function :func:`clustimage.clustimage.Clustimage.load`. 6 | Below we illustrate how to save and load models. 7 | 8 | Saving the model with the results: 9 | 10 | .. code:: python 11 | 12 | from clustimage import Clustimage 13 | 14 | # Initialize 15 | cl = Clustimage(method='hog') 16 | # Load example data 17 | 18 | X, y = cl.import_example(data='mnist') 19 | # Preprocessing, feature extraction and cluster evaluation 20 | results = cl.fit_transform(X) 21 | 22 | # Load model 23 | cl.save('clustimage_model', overwrite=True) 24 | 25 | 26 | Loading 27 | ########## 28 | 29 | Loading a learned model can be done using the function :func:`clustimage.load`: 30 | 31 | .. code:: python 32 | 33 | from clustimage import Clustimage 34 | 35 | # Load model 36 | cl.load('clustimage_model') 37 | 38 | 39 | 40 | 41 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/pages/html/_sources/clustimage.clustimage.rst.txt: -------------------------------------------------------------------------------- 1 | API References 2 | ------------------------------------------------ 3 | 4 | 5 | .. automodule:: clustimage.clustimage 6 | :members: 7 | :undoc-members: 8 | 9 | 10 | 11 | 12 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/pages/html/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | clustimage's documentation! 2 | =========================== 3 | 4 | |python| |pypi| |docs| |stars| |LOC| |downloads_month| |downloads_total| |license| |forks| |open issues| |project status| |medium| |colab| |DOI| |repo-size| |donate| 5 | 6 | .. tip:: 7 | `Medium Blog: A step-by-step guide for clustering images `_ 8 | 9 | 10 | .. |fig1| image:: ../figs/schematic_overview.png 11 | 12 | .. table:: 13 | :align: center 14 | 15 | +----------+ 16 | | |fig1| | 17 | +----------+ 18 | 19 | ----------------------------------- 20 | 21 | The aim of ``clustimage`` is to detect natural groups or clusters of images. 22 | 23 | Many computer vision tasks rely on (deep) neural networks, and aim to predict "what's on the image". However, not all tasks require supervised approaches or neural networks. With an unsupervised approach, we can aim to determine natural groups or clusters of images without being constrained to a fixed number of (learned) categories. In this blog, I will summarize the concepts of unsupervised clustering, followed by a hands-on tutorial on how to pre-process images, extract features (PCA, HOG), and group images with high similarity taking into account the goodness of the clustering. I will demonstrate the clustering of the MNIST dataset, the 101 objects dataset, the flower dataset, and finally the clustering of faces using the Olivetti dataset. All results are derived using the Python library clustimage. 24 | 25 | ``clustimage`` is a generic approach for unsupervised images clustering and overcomes the following challenges: 26 | 27 | * 1. Robustly groups similar images. 28 | * 2. Returns the unique images. 29 | * 3. Many plots for deeper exploration. 30 | * 4. Finds higly similar images for a given input image. 31 | 32 | 33 | ----------------------------------- 34 | 35 | .. note:: 36 | **Your ❤️ is important to keep maintaining this package.** You can `support `_ in various ways, have a look at the `sponser page `_. 37 | Report bugs, issues and feature extensions at `github `_ page. 38 | 39 | .. code-block:: console 40 | 41 | pip install clustimage 42 | 43 | ----------------------------------- 44 | 45 | 46 | 47 | Contents 48 | ======== 49 | 50 | .. toctree:: 51 | :maxdepth: 1 52 | :caption: Background 53 | 54 | Abstract 55 | 56 | 57 | .. toctree:: 58 | :maxdepth: 1 59 | :caption: Installation 60 | 61 | Installation 62 | 63 | 64 | .. toctree:: 65 | :maxdepth: 1 66 | :caption: Core functionalities 67 | 68 | core_functions 69 | 70 | .. toctree:: 71 | :maxdepth: 1 72 | :caption: Feature Extraction 73 | 74 | Feature Extraction 75 | 76 | 77 | .. toctree:: 78 | :maxdepth: 1 79 | :caption: Cluster Evaluation 80 | 81 | Cluster Evaluation 82 | 83 | .. toctree:: 84 | :maxdepth: 1 85 | :caption: Find/Predict 86 | 87 | Find 88 | 89 | 90 | .. toctree:: 91 | :maxdepth: 1 92 | :caption: Performance 93 | 94 | Performance 95 | 96 | .. toctree:: 97 | :maxdepth: 1 98 | :caption: Save and Load 99 | 100 | Save and Load 101 | 102 | 103 | .. toctree:: 104 | :maxdepth: 1 105 | :caption: Examples 106 | 107 | Examples 108 | 109 | 110 | .. toctree:: 111 | :maxdepth: 1 112 | :caption: Documentation 113 | 114 | Documentation 115 | Coding quality 116 | clustimage.clustimage 117 | 118 | 119 | 120 | 121 | 122 | Indices and tables 123 | ================== 124 | 125 | * :ref:`genindex` 126 | * :ref:`modindex` 127 | * :ref:`search` 128 | 129 | 130 | 131 | .. |repo-size| image:: https://img.shields.io/github/repo-size/erdogant/clustimage 132 | :alt: repo-size 133 | :target: https://img.shields.io/github/repo-size/erdogant/clustimage 134 | 135 | .. |stars| image:: https://img.shields.io/github/stars/erdogant/clustimage 136 | :alt: Stars 137 | :target: https://img.shields.io/github/stars/erdogant/clustimage 138 | 139 | .. |python| image:: https://img.shields.io/pypi/pyversions/clustimage.svg 140 | :alt: |Python 141 | :target: https://erdogant.github.io/clustimage/ 142 | 143 | .. |pypi| image:: https://img.shields.io/pypi/v/clustimage.svg 144 | :alt: |Python Version 145 | :target: https://pypi.org/project/clustimage/ 146 | 147 | .. |docs| image:: https://img.shields.io/badge/Sphinx-Docs-blue.svg 148 | :alt: Sphinx documentation 149 | :target: https://erdogant.github.io/clustimage/ 150 | 151 | .. |LOC| image:: https://sloc.xyz/github/erdogant/clustimage/?category=code 152 | :alt: lines of code 153 | :target: https://github.com/erdogant/clustimage 154 | 155 | .. |downloads_month| image:: https://static.pepy.tech/personalized-badge/clustimage?period=month&units=international_system&left_color=grey&right_color=brightgreen&left_text=PyPI%20downloads/month 156 | :alt: Downloads per month 157 | :target: https://pepy.tech/project/clustimage 158 | 159 | .. |downloads_total| image:: https://static.pepy.tech/personalized-badge/clustimage?period=total&units=international_system&left_color=grey&right_color=brightgreen&left_text=Downloads 160 | :alt: Downloads in total 161 | :target: https://pepy.tech/project/clustimage 162 | 163 | .. |license| image:: https://img.shields.io/badge/license-MIT-green.svg 164 | :alt: License 165 | :target: https://github.com/erdogant/clustimage/blob/master/LICENSE 166 | 167 | .. |forks| image:: https://img.shields.io/github/forks/erdogant/clustimage.svg 168 | :alt: Github Forks 169 | :target: https://github.com/erdogant/clustimage/network 170 | 171 | .. |open issues| image:: https://img.shields.io/github/issues/erdogant/clustimage.svg 172 | :alt: Open Issues 173 | :target: https://github.com/erdogant/clustimage/issues 174 | 175 | .. |project status| image:: http://www.repostatus.org/badges/latest/active.svg 176 | :alt: Project Status 177 | :target: http://www.repostatus.org/#active 178 | 179 | .. |medium| image:: https://img.shields.io/badge/Medium-Blog-green.svg 180 | :alt: Medium Blog 181 | :target: https://erdogant.github.io/clustimage/pages/html/Documentation.html#medium-blog 182 | 183 | .. |donate| image:: https://img.shields.io/badge/Support%20this%20project-grey.svg?logo=github%20sponsors 184 | :alt: donate 185 | :target: https://erdogant.github.io/clustimage/pages/html/Documentation.html# 186 | 187 | .. |colab| image:: https://colab.research.google.com/assets/colab-badge.svg 188 | :alt: Colab example 189 | :target: https://erdogant.github.io/clustimage/pages/html/Documentation.html#colab-notebook 190 | 191 | .. |DOI| image:: https://zenodo.org/badge/423822054.svg 192 | :alt: Cite 193 | :target: https://zenodo.org/badge/latestdoi/423822054 194 | 195 | 196 | .. include:: add_bottom.add 197 | -------------------------------------------------------------------------------- /docs/pages/html/_sources/sponsor.rst.txt: -------------------------------------------------------------------------------- 1 | .. tip:: 2 | You can support this project in various ways ❤️ 3 | 4 | * **Star** this repo at the github page. 5 | * Become a `Sponsor `_! 6 | * `Buy `_ me a coffee! I ❤️ coffee :) 7 | * Follow me on Medium! Go to my `medium profile `_ and press *follow*. 8 | * Subscribe to me on Substack! Go to my `profile `_ where you can *subscribe*. 9 | * You can also buy a digital educational product over `here `_. 10 | * Contribute in the form of feature requests, idea discussions, reporting bugs, opening pull requests. 11 | 12 | Cheers Mate. 13 | 14 | .. raw:: html 15 | 16 | 20 | -------------------------------------------------------------------------------- /docs/pages/html/_static/_sphinx_javascript_frameworks_compat.js: -------------------------------------------------------------------------------- 1 | /* Compatability shim for jQuery and underscores.js. 2 | * 3 | * Copyright Sphinx contributors 4 | * Released under the two clause BSD licence 5 | */ 6 | 7 | /** 8 | * small helper function to urldecode strings 9 | * 10 | * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL 11 | */ 12 | jQuery.urldecode = function(x) { 13 | if (!x) { 14 | return x 15 | } 16 | return decodeURIComponent(x.replace(/\+/g, ' ')); 17 | }; 18 | 19 | /** 20 | * small helper function to urlencode strings 21 | */ 22 | jQuery.urlencode = encodeURIComponent; 23 | 24 | /** 25 | * This function returns the parsed url parameters of the 26 | * current request. Multiple values per key are supported, 27 | * it will always return arrays of strings for the value parts. 28 | */ 29 | jQuery.getQueryParameters = function(s) { 30 | if (typeof s === 'undefined') 31 | s = document.location.search; 32 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 33 | var result = {}; 34 | for (var i = 0; i < parts.length; i++) { 35 | var tmp = parts[i].split('=', 2); 36 | var key = jQuery.urldecode(tmp[0]); 37 | var value = jQuery.urldecode(tmp[1]); 38 | if (key in result) 39 | result[key].push(value); 40 | else 41 | result[key] = [value]; 42 | } 43 | return result; 44 | }; 45 | 46 | /** 47 | * highlight a given string on a jquery object by wrapping it in 48 | * span elements with the given class name. 49 | */ 50 | jQuery.fn.highlightText = function(text, className) { 51 | function highlight(node, addItems) { 52 | if (node.nodeType === 3) { 53 | var val = node.nodeValue; 54 | var pos = val.toLowerCase().indexOf(text); 55 | if (pos >= 0 && 56 | !jQuery(node.parentNode).hasClass(className) && 57 | !jQuery(node.parentNode).hasClass("nohighlight")) { 58 | var span; 59 | var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); 60 | if (isInSVG) { 61 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 62 | } else { 63 | span = document.createElement("span"); 64 | span.className = className; 65 | } 66 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 67 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 68 | document.createTextNode(val.substr(pos + text.length)), 69 | node.nextSibling)); 70 | node.nodeValue = val.substr(0, pos); 71 | if (isInSVG) { 72 | var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); 73 | var bbox = node.parentElement.getBBox(); 74 | rect.x.baseVal.value = bbox.x; 75 | rect.y.baseVal.value = bbox.y; 76 | rect.width.baseVal.value = bbox.width; 77 | rect.height.baseVal.value = bbox.height; 78 | rect.setAttribute('class', className); 79 | addItems.push({ 80 | "parent": node.parentNode, 81 | "target": rect}); 82 | } 83 | } 84 | } 85 | else if (!jQuery(node).is("button, select, textarea")) { 86 | jQuery.each(node.childNodes, function() { 87 | highlight(this, addItems); 88 | }); 89 | } 90 | } 91 | var addItems = []; 92 | var result = this.each(function() { 93 | highlight(this, addItems); 94 | }); 95 | for (var i = 0; i < addItems.length; ++i) { 96 | jQuery(addItems[i].parent).before(addItems[i].target); 97 | } 98 | return result; 99 | }; 100 | 101 | /* 102 | * backward compatibility for jQuery.browser 103 | * This will be supported until firefox bug is fixed. 104 | */ 105 | if (!jQuery.browser) { 106 | jQuery.uaMatch = function(ua) { 107 | ua = ua.toLowerCase(); 108 | 109 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 110 | /(webkit)[ \/]([\w.]+)/.exec(ua) || 111 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 112 | /(msie) ([\w.]+)/.exec(ua) || 113 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 114 | []; 115 | 116 | return { 117 | browser: match[ 1 ] || "", 118 | version: match[ 2 ] || "0" 119 | }; 120 | }; 121 | jQuery.browser = {}; 122 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; 123 | } 124 | -------------------------------------------------------------------------------- /docs/pages/html/_static/css/badge_only.css: -------------------------------------------------------------------------------- 1 | .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-other-versions .rtd-current-item{font-weight:700}.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}}#flyout-search-form{padding:6px} -------------------------------------------------------------------------------- /docs/pages/html/_static/css/carbon_ads_hor.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #000; 3 | } 4 | 5 | #carbonads * { 6 | margin: initial; 7 | padding: initial; 8 | } 9 | 10 | #carbonads { 11 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 12 | Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, Arial, 13 | sans-serif; 14 | } 15 | 16 | #carbonads { 17 | display: flex; 18 | max-width: 330px; 19 | background-color: hsl(0, 0%, 10%); 20 | box-shadow: 0 0 1px hsla(0, 0%, 0%, 0.5); 21 | } 22 | 23 | #carbonads a { 24 | color: #ddd; 25 | text-decoration: none; 26 | } 27 | 28 | #carbonads a:hover { 29 | color: #ddd; 30 | } 31 | 32 | #carbonads span { 33 | position: relative; 34 | display: block; 35 | overflow: hidden; 36 | } 37 | 38 | #carbonads .carbon-wrap { 39 | display: flex; 40 | } 41 | 42 | #carbonads .carbon-img { 43 | display: block; 44 | margin: 0; 45 | line-height: 1; 46 | } 47 | 48 | #carbonads .carbon-img img { 49 | display: block; 50 | } 51 | 52 | #carbonads .carbon-text { 53 | padding: 10px; 54 | text-align: left; 55 | font-size: 13px; 56 | margin-bottom: 16px; 57 | line-height: 1.5; 58 | } 59 | 60 | #carbonads .carbon-poweredby { 61 | display: block; 62 | padding: 6px 8px; 63 | background: #1e2021; 64 | color: #aaa; 65 | text-align: center; 66 | text-transform: uppercase; 67 | letter-spacing: 0.5px; 68 | font-weight: 600; 69 | font-size: 8px; 70 | line-height: 1; 71 | position: absolute; 72 | bottom: 0; 73 | right: 0; 74 | border-top-left-radius: 6px; 75 | } 76 | -------------------------------------------------------------------------------- /docs/pages/html/_static/css/carbon_ads_ver.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #000; 3 | } 4 | 5 | #carbonads { 6 | display: block; 7 | overflow: hidden; 8 | padding: 1em; 9 | max-width: 130px; 10 | background: hsl(0, 0%, 10%); 11 | box-shadow: 0 0 1px hsla(0, 0%, 0%, 0.5); 12 | text-align: center; 13 | font-size: 12px; 14 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 15 | line-height: 1.5; 16 | } 17 | 18 | #carbonads a { 19 | color: #eee; 20 | text-decoration: none; 21 | } 22 | 23 | #carbonads a:hover { 24 | color: inherit; 25 | } 26 | 27 | #carbonads span { 28 | display: block; 29 | overflow: hidden; 30 | } 31 | 32 | .carbon-img { 33 | display: block; 34 | margin: 0 auto 8px; 35 | line-height: 1; 36 | } 37 | 38 | .carbon-text { 39 | display: block; 40 | margin-bottom: 8px; 41 | } 42 | 43 | .carbon-poweredby { 44 | display: block; 45 | padding: 6px 10px; 46 | background: #1e2021; 47 | text-transform: uppercase; 48 | letter-spacing: 0.5px; 49 | font-size: 7px; 50 | line-height: 1; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /docs/pages/html/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #111; 3 | } 4 | 5 | #carbonads * { 6 | margin: initial; 7 | padding: initial; 8 | } 9 | 10 | #carbonads { 11 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 12 | Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, Arial, 13 | sans-serif; 14 | } 15 | 16 | #carbonads { 17 | display: flex; 18 | max-width: 330px; 19 | background-color: hsl(0, 0%, 10%); 20 | box-shadow: 0 0 1px hsla(0, 0%, 0%, 0.5); 21 | } 22 | 23 | #carbonads a { 24 | color: #ddd; 25 | text-decoration: none; 26 | } 27 | 28 | #carbonads a:hover { 29 | color: #ddd; 30 | } 31 | 32 | #carbonads span { 33 | position: relative; 34 | display: block; 35 | overflow: hidden; 36 | } 37 | 38 | #carbonads .carbon-wrap { 39 | display: flex; 40 | } 41 | 42 | #carbonads .carbon-img { 43 | display: block; 44 | margin: 0; 45 | line-height: 1; 46 | } 47 | 48 | #carbonads .carbon-img img { 49 | display: block; 50 | } 51 | 52 | #carbonads .carbon-text { 53 | padding: 10px; 54 | text-align: left; 55 | font-size: 13px; 56 | margin-bottom: 16px; 57 | line-height: 1.5; 58 | } 59 | 60 | #carbonads .carbon-poweredby { 61 | display: block; 62 | padding: 6px 8px; 63 | background: #1e2021; 64 | color: #aaa; 65 | text-align: center; 66 | text-transform: uppercase; 67 | letter-spacing: 0.5px; 68 | font-weight: 600; 69 | font-size: 8px; 70 | line-height: 1; 71 | position: absolute; 72 | bottom: 0; 73 | right: 0; 74 | border-top-left-radius: 6px; 75 | } 76 | -------------------------------------------------------------------------------- /docs/pages/html/_static/css/custom_rectangle.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #111; 3 | } 4 | 5 | #carbonads { 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | position: relative; 10 | 11 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, Helvetica, Arial, sans-serif; 12 | width: 275px; 13 | height: 215px; 14 | text-align: center; 15 | background-color: hsl(0, 0%, 10%); 16 | box-shadow: 0 0 1px hsla(0, 0%, 0%, 0.5); 17 | } 18 | 19 | #carbonads a { 20 | color: #eee; 21 | text-decoration: none; 22 | } 23 | 24 | #carbonads a:hover { 25 | color: #eee; 26 | } 27 | 28 | 29 | #carbonads>span { 30 | display: block; 31 | position: relative; 32 | overflow: hidden; 33 | padding: 15px; 34 | 35 | border-radius: 4px; 36 | background-color: #1e2021; 37 | box-shadow: 38 | 0 0 1px 1px hsla(0, 0%, 0%, .05), 39 | 0 0 2px 2px hsla(0, 0%, 0%, .05), 40 | 0 0 4px 4px hsla(0, 0%, 0%, .05); 41 | } 42 | 43 | 44 | .carbon-img { 45 | display: block; 46 | margin: 0 0 8px; 47 | 48 | line-height: 1; 49 | } 50 | 51 | .carbon-img img { 52 | width: 150px; 53 | max-width: 150px !important; 54 | height: auto; 55 | } 56 | 57 | .carbon-text { 58 | display: block; 59 | margin-bottom: 8px; 60 | padding: 0 10px; 61 | font-size: 13px; 62 | font-weight: 500; 63 | line-height: 1.35; 64 | font-weight: normal; 65 | } 66 | 67 | .carbon-poweredby { 68 | display: block; 69 | position: absolute; 70 | right: 0; 71 | bottom: 0; 72 | left: 0; 73 | padding: 5px 6px; 74 | font-size: 8px; 75 | line-height: 1; 76 | letter-spacing: .5px; 77 | text-transform: uppercase; 78 | color: #aaa; 79 | background-color: hsl(0, 0%, 10%); 80 | } 81 | -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/Roboto-Slab-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/Roboto-Slab-Bold.woff -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/Roboto-Slab-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/Roboto-Slab-Bold.woff2 -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/Roboto-Slab-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/Roboto-Slab-Regular.woff -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/Roboto-Slab-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/Roboto-Slab-Regular.woff2 -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/lato-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/lato-bold-italic.woff -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/lato-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/lato-bold-italic.woff2 -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/lato-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/lato-bold.woff -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/lato-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/lato-bold.woff2 -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/lato-normal-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/lato-normal-italic.woff -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/lato-normal-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/lato-normal-italic.woff2 -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/lato-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/lato-normal.woff -------------------------------------------------------------------------------- /docs/pages/html/_static/css/fonts/lato-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/css/fonts/lato-normal.woff2 -------------------------------------------------------------------------------- /docs/pages/html/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Base JavaScript utilities for all Sphinx HTML documentation. 3 | */ 4 | "use strict"; 5 | 6 | const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ 7 | "TEXTAREA", 8 | "INPUT", 9 | "SELECT", 10 | "BUTTON", 11 | ]); 12 | 13 | const _ready = (callback) => { 14 | if (document.readyState !== "loading") { 15 | callback(); 16 | } else { 17 | document.addEventListener("DOMContentLoaded", callback); 18 | } 19 | }; 20 | 21 | /** 22 | * Small JavaScript module for the documentation. 23 | */ 24 | const Documentation = { 25 | init: () => { 26 | Documentation.initDomainIndexTable(); 27 | Documentation.initOnKeyListeners(); 28 | }, 29 | 30 | /** 31 | * i18n support 32 | */ 33 | TRANSLATIONS: {}, 34 | PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), 35 | LOCALE: "unknown", 36 | 37 | // gettext and ngettext don't access this so that the functions 38 | // can safely bound to a different name (_ = Documentation.gettext) 39 | gettext: (string) => { 40 | const translated = Documentation.TRANSLATIONS[string]; 41 | switch (typeof translated) { 42 | case "undefined": 43 | return string; // no translation 44 | case "string": 45 | return translated; // translation exists 46 | default: 47 | return translated[0]; // (singular, plural) translation tuple exists 48 | } 49 | }, 50 | 51 | ngettext: (singular, plural, n) => { 52 | const translated = Documentation.TRANSLATIONS[singular]; 53 | if (typeof translated !== "undefined") 54 | return translated[Documentation.PLURAL_EXPR(n)]; 55 | return n === 1 ? singular : plural; 56 | }, 57 | 58 | addTranslations: (catalog) => { 59 | Object.assign(Documentation.TRANSLATIONS, catalog.messages); 60 | Documentation.PLURAL_EXPR = new Function( 61 | "n", 62 | `return (${catalog.plural_expr})` 63 | ); 64 | Documentation.LOCALE = catalog.locale; 65 | }, 66 | 67 | /** 68 | * helper function to focus on search bar 69 | */ 70 | focusSearchBar: () => { 71 | document.querySelectorAll("input[name=q]")[0]?.focus(); 72 | }, 73 | 74 | /** 75 | * Initialise the domain index toggle buttons 76 | */ 77 | initDomainIndexTable: () => { 78 | const toggler = (el) => { 79 | const idNumber = el.id.substr(7); 80 | const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); 81 | if (el.src.substr(-9) === "minus.png") { 82 | el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; 83 | toggledRows.forEach((el) => (el.style.display = "none")); 84 | } else { 85 | el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; 86 | toggledRows.forEach((el) => (el.style.display = "")); 87 | } 88 | }; 89 | 90 | const togglerElements = document.querySelectorAll("img.toggler"); 91 | togglerElements.forEach((el) => 92 | el.addEventListener("click", (event) => toggler(event.currentTarget)) 93 | ); 94 | togglerElements.forEach((el) => (el.style.display = "")); 95 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); 96 | }, 97 | 98 | initOnKeyListeners: () => { 99 | // only install a listener if it is really needed 100 | if ( 101 | !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && 102 | !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS 103 | ) 104 | return; 105 | 106 | document.addEventListener("keydown", (event) => { 107 | // bail for input elements 108 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; 109 | // bail with special keys 110 | if (event.altKey || event.ctrlKey || event.metaKey) return; 111 | 112 | if (!event.shiftKey) { 113 | switch (event.key) { 114 | case "ArrowLeft": 115 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 116 | 117 | const prevLink = document.querySelector('link[rel="prev"]'); 118 | if (prevLink && prevLink.href) { 119 | window.location.href = prevLink.href; 120 | event.preventDefault(); 121 | } 122 | break; 123 | case "ArrowRight": 124 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 125 | 126 | const nextLink = document.querySelector('link[rel="next"]'); 127 | if (nextLink && nextLink.href) { 128 | window.location.href = nextLink.href; 129 | event.preventDefault(); 130 | } 131 | break; 132 | } 133 | } 134 | 135 | // some keyboard layouts may need Shift to get / 136 | switch (event.key) { 137 | case "/": 138 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; 139 | Documentation.focusSearchBar(); 140 | event.preventDefault(); 141 | } 142 | }); 143 | }, 144 | }; 145 | 146 | // quick alias for translations 147 | const _ = Documentation.gettext; 148 | 149 | _ready(Documentation.init); 150 | -------------------------------------------------------------------------------- /docs/pages/html/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | const DOCUMENTATION_OPTIONS = { 2 | VERSION: 'clustimage', 3 | LANGUAGE: 'en', 4 | COLLAPSE_INDEX: false, 5 | BUILDER: 'html', 6 | FILE_SUFFIX: '.html', 7 | LINK_SUFFIX: '.html', 8 | HAS_SOURCE: true, 9 | SOURCELINK_SUFFIX: '.txt', 10 | NAVIGATION_WITH_KEYS: false, 11 | SHOW_SEARCH_SUMMARY: true, 12 | ENABLE_SEARCH_SHORTCUTS: true, 13 | }; -------------------------------------------------------------------------------- /docs/pages/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/file.png -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-bold.eot -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-bold.ttf -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-bold.woff -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-bold.woff2 -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-bolditalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-bolditalic.eot -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-bolditalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-bolditalic.ttf -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-bolditalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-bolditalic.woff -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-bolditalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-bolditalic.woff2 -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-italic.eot -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-italic.ttf -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-italic.woff -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-italic.woff2 -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-regular.eot -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-regular.ttf -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-regular.woff -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/Lato/lato-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/Lato/lato-regular.woff2 -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff -------------------------------------------------------------------------------- /docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 -------------------------------------------------------------------------------- /docs/pages/html/_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/pages/html/_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;t a.language.name.localeCompare(b.language.name)); 14 | 15 | const languagesHTML = ` 16 |
17 |
Languages
18 | ${languages 19 | .map( 20 | (translation) => ` 21 |
22 | ${translation.language.code} 23 |
24 | `, 25 | ) 26 | .join("\n")} 27 |
28 | `; 29 | return languagesHTML; 30 | } 31 | 32 | function renderVersions(config) { 33 | if (!config.versions.active.length) { 34 | return ""; 35 | } 36 | const versionsHTML = ` 37 |
38 |
Versions
39 | ${config.versions.active 40 | .map( 41 | (version) => ` 42 |
43 | ${version.slug} 44 |
45 | `, 46 | ) 47 | .join("\n")} 48 |
49 | `; 50 | return versionsHTML; 51 | } 52 | 53 | function renderDownloads(config) { 54 | if (!Object.keys(config.versions.current.downloads).length) { 55 | return ""; 56 | } 57 | const downloadsNameDisplay = { 58 | pdf: "PDF", 59 | epub: "Epub", 60 | htmlzip: "HTML", 61 | }; 62 | 63 | const downloadsHTML = ` 64 |
65 |
Downloads
66 | ${Object.entries(config.versions.current.downloads) 67 | .map( 68 | ([name, url]) => ` 69 |
70 | ${downloadsNameDisplay[name]} 71 |
72 | `, 73 | ) 74 | .join("\n")} 75 |
76 | `; 77 | return downloadsHTML; 78 | } 79 | 80 | document.addEventListener("readthedocs-addons-data-ready", function (event) { 81 | const config = event.detail.data(); 82 | 83 | const flyout = ` 84 |
85 | 86 | Read the Docs 87 | v: ${config.versions.current.slug} 88 | 89 | 90 |
91 |
92 | ${renderLanguages(config)} 93 | ${renderVersions(config)} 94 | ${renderDownloads(config)} 95 |
96 |
On Read the Docs
97 |
98 | Project Home 99 |
100 |
101 | Builds 102 |
103 |
104 | Downloads 105 |
106 |
107 |
108 |
Search
109 |
110 |
111 | 118 |
119 |
120 |
121 |
122 | 123 | Hosted by Read the Docs 124 | 125 |
126 |
127 | `; 128 | 129 | // Inject the generated flyout into the body HTML element. 130 | document.body.insertAdjacentHTML("beforeend", flyout); 131 | 132 | // Trigger the Read the Docs Addons Search modal when clicking on the "Search docs" input from inside the flyout. 133 | document 134 | .querySelector("#flyout-search-form") 135 | .addEventListener("focusin", () => { 136 | const event = new CustomEvent("readthedocs-search-show"); 137 | document.dispatchEvent(event); 138 | }); 139 | }) 140 | } 141 | 142 | if (themeLanguageSelector || themeVersionSelector) { 143 | function onSelectorSwitch(event) { 144 | const option = event.target.selectedIndex; 145 | const item = event.target.options[option]; 146 | window.location.href = item.dataset.url; 147 | } 148 | 149 | document.addEventListener("readthedocs-addons-data-ready", function (event) { 150 | const config = event.detail.data(); 151 | 152 | const versionSwitch = document.querySelector( 153 | "div.switch-menus > div.version-switch", 154 | ); 155 | if (themeVersionSelector) { 156 | let versions = config.versions.active; 157 | if (config.versions.current.hidden || config.versions.current.type === "external") { 158 | versions.unshift(config.versions.current); 159 | } 160 | const versionSelect = ` 161 | 174 | `; 175 | 176 | versionSwitch.innerHTML = versionSelect; 177 | versionSwitch.firstElementChild.addEventListener("change", onSelectorSwitch); 178 | } 179 | 180 | const languageSwitch = document.querySelector( 181 | "div.switch-menus > div.language-switch", 182 | ); 183 | 184 | if (themeLanguageSelector) { 185 | if (config.projects.translations.length) { 186 | // Add the current language to the options on the selector 187 | let languages = config.projects.translations.concat( 188 | config.projects.current, 189 | ); 190 | languages = languages.sort((a, b) => 191 | a.language.name.localeCompare(b.language.name), 192 | ); 193 | 194 | const languageSelect = ` 195 | 208 | `; 209 | 210 | languageSwitch.innerHTML = languageSelect; 211 | languageSwitch.firstElementChild.addEventListener("change", onSelectorSwitch); 212 | } 213 | else { 214 | languageSwitch.remove(); 215 | } 216 | } 217 | }); 218 | } 219 | 220 | document.addEventListener("readthedocs-addons-data-ready", function (event) { 221 | // Trigger the Read the Docs Addons Search modal when clicking on "Search docs" input from the topnav. 222 | document 223 | .querySelector("[role='search'] input") 224 | .addEventListener("focusin", () => { 225 | const event = new CustomEvent("readthedocs-search-show"); 226 | document.dispatchEvent(event); 227 | }); 228 | }); -------------------------------------------------------------------------------- /docs/pages/html/_static/language_data.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This script contains the language-specific data used by searchtools.js, 3 | * namely the list of stopwords, stemmer, scorer and splitter. 4 | */ 5 | 6 | var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; 7 | 8 | 9 | /* Non-minified version is copied as a separate JS file, if available */ 10 | 11 | /** 12 | * Porter Stemmer 13 | */ 14 | var Stemmer = function() { 15 | 16 | var step2list = { 17 | ational: 'ate', 18 | tional: 'tion', 19 | enci: 'ence', 20 | anci: 'ance', 21 | izer: 'ize', 22 | bli: 'ble', 23 | alli: 'al', 24 | entli: 'ent', 25 | eli: 'e', 26 | ousli: 'ous', 27 | ization: 'ize', 28 | ation: 'ate', 29 | ator: 'ate', 30 | alism: 'al', 31 | iveness: 'ive', 32 | fulness: 'ful', 33 | ousness: 'ous', 34 | aliti: 'al', 35 | iviti: 'ive', 36 | biliti: 'ble', 37 | logi: 'log' 38 | }; 39 | 40 | var step3list = { 41 | icate: 'ic', 42 | ative: '', 43 | alize: 'al', 44 | iciti: 'ic', 45 | ical: 'ic', 46 | ful: '', 47 | ness: '' 48 | }; 49 | 50 | var c = "[^aeiou]"; // consonant 51 | var v = "[aeiouy]"; // vowel 52 | var C = c + "[^aeiouy]*"; // consonant sequence 53 | var V = v + "[aeiou]*"; // vowel sequence 54 | 55 | var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 56 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 57 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 58 | var s_v = "^(" + C + ")?" + v; // vowel in stem 59 | 60 | this.stemWord = function (w) { 61 | var stem; 62 | var suffix; 63 | var firstch; 64 | var origword = w; 65 | 66 | if (w.length < 3) 67 | return w; 68 | 69 | var re; 70 | var re2; 71 | var re3; 72 | var re4; 73 | 74 | firstch = w.substr(0,1); 75 | if (firstch == "y") 76 | w = firstch.toUpperCase() + w.substr(1); 77 | 78 | // Step 1a 79 | re = /^(.+?)(ss|i)es$/; 80 | re2 = /^(.+?)([^s])s$/; 81 | 82 | if (re.test(w)) 83 | w = w.replace(re,"$1$2"); 84 | else if (re2.test(w)) 85 | w = w.replace(re2,"$1$2"); 86 | 87 | // Step 1b 88 | re = /^(.+?)eed$/; 89 | re2 = /^(.+?)(ed|ing)$/; 90 | if (re.test(w)) { 91 | var fp = re.exec(w); 92 | re = new RegExp(mgr0); 93 | if (re.test(fp[1])) { 94 | re = /.$/; 95 | w = w.replace(re,""); 96 | } 97 | } 98 | else if (re2.test(w)) { 99 | var fp = re2.exec(w); 100 | stem = fp[1]; 101 | re2 = new RegExp(s_v); 102 | if (re2.test(stem)) { 103 | w = stem; 104 | re2 = /(at|bl|iz)$/; 105 | re3 = new RegExp("([^aeiouylsz])\\1$"); 106 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 107 | if (re2.test(w)) 108 | w = w + "e"; 109 | else if (re3.test(w)) { 110 | re = /.$/; 111 | w = w.replace(re,""); 112 | } 113 | else if (re4.test(w)) 114 | w = w + "e"; 115 | } 116 | } 117 | 118 | // Step 1c 119 | re = /^(.+?)y$/; 120 | if (re.test(w)) { 121 | var fp = re.exec(w); 122 | stem = fp[1]; 123 | re = new RegExp(s_v); 124 | if (re.test(stem)) 125 | w = stem + "i"; 126 | } 127 | 128 | // Step 2 129 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; 130 | if (re.test(w)) { 131 | var fp = re.exec(w); 132 | stem = fp[1]; 133 | suffix = fp[2]; 134 | re = new RegExp(mgr0); 135 | if (re.test(stem)) 136 | w = stem + step2list[suffix]; 137 | } 138 | 139 | // Step 3 140 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; 141 | if (re.test(w)) { 142 | var fp = re.exec(w); 143 | stem = fp[1]; 144 | suffix = fp[2]; 145 | re = new RegExp(mgr0); 146 | if (re.test(stem)) 147 | w = stem + step3list[suffix]; 148 | } 149 | 150 | // Step 4 151 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; 152 | re2 = /^(.+?)(s|t)(ion)$/; 153 | if (re.test(w)) { 154 | var fp = re.exec(w); 155 | stem = fp[1]; 156 | re = new RegExp(mgr1); 157 | if (re.test(stem)) 158 | w = stem; 159 | } 160 | else if (re2.test(w)) { 161 | var fp = re2.exec(w); 162 | stem = fp[1] + fp[2]; 163 | re2 = new RegExp(mgr1); 164 | if (re2.test(stem)) 165 | w = stem; 166 | } 167 | 168 | // Step 5 169 | re = /^(.+?)e$/; 170 | if (re.test(w)) { 171 | var fp = re.exec(w); 172 | stem = fp[1]; 173 | re = new RegExp(mgr1); 174 | re2 = new RegExp(meq1); 175 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 176 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) 177 | w = stem; 178 | } 179 | re = /ll$/; 180 | re2 = new RegExp(mgr1); 181 | if (re.test(w) && re2.test(w)) { 182 | re = /.$/; 183 | w = w.replace(re,""); 184 | } 185 | 186 | // and turn initial Y back to y 187 | if (firstch == "y") 188 | w = firstch.toLowerCase() + w.substr(1); 189 | return w; 190 | } 191 | } 192 | 193 | -------------------------------------------------------------------------------- /docs/pages/html/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/minus.png -------------------------------------------------------------------------------- /docs/pages/html/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/_static/plus.png -------------------------------------------------------------------------------- /docs/pages/html/_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: #eeffcc; } 8 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 9 | .highlight .err { border: 1px solid #F00 } /* Error */ 10 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 11 | .highlight .o { color: #666 } /* Operator */ 12 | .highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */ 13 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 14 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 15 | .highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */ 16 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 17 | .highlight .cs { color: #408090; background-color: #FFF0F0 } /* Comment.Special */ 18 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 19 | .highlight .ge { font-style: italic } /* Generic.Emph */ 20 | .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ 21 | .highlight .gr { color: #F00 } /* Generic.Error */ 22 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 23 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 24 | .highlight .go { color: #333 } /* Generic.Output */ 25 | .highlight .gp { color: #C65D09; font-weight: bold } /* Generic.Prompt */ 26 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 27 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 28 | .highlight .gt { color: #04D } /* Generic.Traceback */ 29 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 30 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 31 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 32 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 33 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 34 | .highlight .kt { color: #902000 } /* Keyword.Type */ 35 | .highlight .m { color: #208050 } /* Literal.Number */ 36 | .highlight .s { color: #4070A0 } /* Literal.String */ 37 | .highlight .na { color: #4070A0 } /* Name.Attribute */ 38 | .highlight .nb { color: #007020 } /* Name.Builtin */ 39 | .highlight .nc { color: #0E84B5; font-weight: bold } /* Name.Class */ 40 | .highlight .no { color: #60ADD5 } /* Name.Constant */ 41 | .highlight .nd { color: #555; font-weight: bold } /* Name.Decorator */ 42 | .highlight .ni { color: #D55537; font-weight: bold } /* Name.Entity */ 43 | .highlight .ne { color: #007020 } /* Name.Exception */ 44 | .highlight .nf { color: #06287E } /* Name.Function */ 45 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 46 | .highlight .nn { color: #0E84B5; font-weight: bold } /* Name.Namespace */ 47 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 48 | .highlight .nv { color: #BB60D5 } /* Name.Variable */ 49 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 50 | .highlight .w { color: #BBB } /* Text.Whitespace */ 51 | .highlight .mb { color: #208050 } /* Literal.Number.Bin */ 52 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 53 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 54 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 55 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 56 | .highlight .sa { color: #4070A0 } /* Literal.String.Affix */ 57 | .highlight .sb { color: #4070A0 } /* Literal.String.Backtick */ 58 | .highlight .sc { color: #4070A0 } /* Literal.String.Char */ 59 | .highlight .dl { color: #4070A0 } /* Literal.String.Delimiter */ 60 | .highlight .sd { color: #4070A0; font-style: italic } /* Literal.String.Doc */ 61 | .highlight .s2 { color: #4070A0 } /* Literal.String.Double */ 62 | .highlight .se { color: #4070A0; font-weight: bold } /* Literal.String.Escape */ 63 | .highlight .sh { color: #4070A0 } /* Literal.String.Heredoc */ 64 | .highlight .si { color: #70A0D0; font-style: italic } /* Literal.String.Interpol */ 65 | .highlight .sx { color: #C65D09 } /* Literal.String.Other */ 66 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 67 | .highlight .s1 { color: #4070A0 } /* Literal.String.Single */ 68 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 69 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 70 | .highlight .fm { color: #06287E } /* Name.Function.Magic */ 71 | .highlight .vc { color: #BB60D5 } /* Name.Variable.Class */ 72 | .highlight .vg { color: #BB60D5 } /* Name.Variable.Global */ 73 | .highlight .vi { color: #BB60D5 } /* Name.Variable.Instance */ 74 | .highlight .vm { color: #BB60D5 } /* Name.Variable.Magic */ 75 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/pages/html/_static/sphinx_highlight.js: -------------------------------------------------------------------------------- 1 | /* Highlighting utilities for Sphinx HTML documentation. */ 2 | "use strict"; 3 | 4 | const SPHINX_HIGHLIGHT_ENABLED = true 5 | 6 | /** 7 | * highlight a given string on a node by wrapping it in 8 | * span elements with the given class name. 9 | */ 10 | const _highlight = (node, addItems, text, className) => { 11 | if (node.nodeType === Node.TEXT_NODE) { 12 | const val = node.nodeValue; 13 | const parent = node.parentNode; 14 | const pos = val.toLowerCase().indexOf(text); 15 | if ( 16 | pos >= 0 && 17 | !parent.classList.contains(className) && 18 | !parent.classList.contains("nohighlight") 19 | ) { 20 | let span; 21 | 22 | const closestNode = parent.closest("body, svg, foreignObject"); 23 | const isInSVG = closestNode && closestNode.matches("svg"); 24 | if (isInSVG) { 25 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 26 | } else { 27 | span = document.createElement("span"); 28 | span.classList.add(className); 29 | } 30 | 31 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 32 | const rest = document.createTextNode(val.substr(pos + text.length)); 33 | parent.insertBefore( 34 | span, 35 | parent.insertBefore( 36 | rest, 37 | node.nextSibling 38 | ) 39 | ); 40 | node.nodeValue = val.substr(0, pos); 41 | /* There may be more occurrences of search term in this node. So call this 42 | * function recursively on the remaining fragment. 43 | */ 44 | _highlight(rest, addItems, text, className); 45 | 46 | if (isInSVG) { 47 | const rect = document.createElementNS( 48 | "http://www.w3.org/2000/svg", 49 | "rect" 50 | ); 51 | const bbox = parent.getBBox(); 52 | rect.x.baseVal.value = bbox.x; 53 | rect.y.baseVal.value = bbox.y; 54 | rect.width.baseVal.value = bbox.width; 55 | rect.height.baseVal.value = bbox.height; 56 | rect.setAttribute("class", className); 57 | addItems.push({ parent: parent, target: rect }); 58 | } 59 | } 60 | } else if (node.matches && !node.matches("button, select, textarea")) { 61 | node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); 62 | } 63 | }; 64 | const _highlightText = (thisNode, text, className) => { 65 | let addItems = []; 66 | _highlight(thisNode, addItems, text, className); 67 | addItems.forEach((obj) => 68 | obj.parent.insertAdjacentElement("beforebegin", obj.target) 69 | ); 70 | }; 71 | 72 | /** 73 | * Small JavaScript module for the documentation. 74 | */ 75 | const SphinxHighlight = { 76 | 77 | /** 78 | * highlight the search words provided in localstorage in the text 79 | */ 80 | highlightSearchWords: () => { 81 | if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight 82 | 83 | // get and clear terms from localstorage 84 | const url = new URL(window.location); 85 | const highlight = 86 | localStorage.getItem("sphinx_highlight_terms") 87 | || url.searchParams.get("highlight") 88 | || ""; 89 | localStorage.removeItem("sphinx_highlight_terms") 90 | url.searchParams.delete("highlight"); 91 | window.history.replaceState({}, "", url); 92 | 93 | // get individual terms from highlight string 94 | const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); 95 | if (terms.length === 0) return; // nothing to do 96 | 97 | // There should never be more than one element matching "div.body" 98 | const divBody = document.querySelectorAll("div.body"); 99 | const body = divBody.length ? divBody[0] : document.querySelector("body"); 100 | window.setTimeout(() => { 101 | terms.forEach((term) => _highlightText(body, term, "highlighted")); 102 | }, 10); 103 | 104 | const searchBox = document.getElementById("searchbox"); 105 | if (searchBox === null) return; 106 | searchBox.appendChild( 107 | document 108 | .createRange() 109 | .createContextualFragment( 110 | '" 114 | ) 115 | ); 116 | }, 117 | 118 | /** 119 | * helper function to hide the search marks again 120 | */ 121 | hideSearchWords: () => { 122 | document 123 | .querySelectorAll("#searchbox .highlight-link") 124 | .forEach((el) => el.remove()); 125 | document 126 | .querySelectorAll("span.highlighted") 127 | .forEach((el) => el.classList.remove("highlighted")); 128 | localStorage.removeItem("sphinx_highlight_terms") 129 | }, 130 | 131 | initEscapeListener: () => { 132 | // only install a listener if it is really needed 133 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; 134 | 135 | document.addEventListener("keydown", (event) => { 136 | // bail for input elements 137 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; 138 | // bail with special keys 139 | if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; 140 | if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { 141 | SphinxHighlight.hideSearchWords(); 142 | event.preventDefault(); 143 | } 144 | }); 145 | }, 146 | }; 147 | 148 | _ready(() => { 149 | /* Do not call highlightSearchWords() when we are on the search page. 150 | * It will highlight words from the *previous* search query. 151 | */ 152 | if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); 153 | SphinxHighlight.initEscapeListener(); 154 | }); 155 | -------------------------------------------------------------------------------- /docs/pages/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdogant/clustimage/b67f8f3a3d6da68feddb9c109c945d59b9bc20b2/docs/pages/html/objects.inv -------------------------------------------------------------------------------- /docs/source/Abstract.rst: -------------------------------------------------------------------------------- 1 | 2 | Abstract 3 | '''''''' 4 | 5 | **Background** 6 | 7 | Image recognition is a computer vision task for identifying and verifying objects/persons on a photograph. 8 | We can seperate the image recognition task into the two broad tasks, namely the supervised and unsupervised task. 9 | In case of the supervised task, we have to classify an image into a fixed number of learned categories. 10 | In case of the unsupervised task, we do not depend on the fact that training data is required but we can interpret the input data and find natural groups or clusters. 11 | 12 | **Aim** 13 | 14 | The aim of ``clustimage`` is to detect natural groups or clusters of images. It works using a multi-step proces of carefully pre-processing the images, extracting the features, and evaluating the optimal number of clusters across the feature space. 15 | The optimal number of clusters are determined using well known methods suchs as *silhouette, dbindex, and derivatives* in combination with clustering methods, such as *agglomerative, kmeans, dbscan and hdbscan*. 16 | With ``clustimage`` we aim to determine the most robust clustering by efficiently searching across the parameter and evaluation the clusters. 17 | Besides clustering of images, the ``clustimage`` model can also be used to find the most similar images for a new unseen sample. 18 | 19 | **Results** 20 | 21 | ``clustimage`` is Python package for unsupervised clustering of images after carefully pre-processing the images, extracting the features, and evaluating the optimal number of clustering in the high-dimensional feature space. 22 | ``clustimage`` does not depend on pre-trained neural networks that require many package-dependencies but is build on core statistics, such as Principal component analysis (PCA), and/or Histogram of Oriented Gradients (HOG). 23 | The input for the model can be a NxM array for which each rows is a flattened rgb/gray image, or it can be a target directory or the full path to a list of images. 24 | More advantages of ``clustimage`` is: 25 | 26 | * It can group any set of images on similarity. 27 | * It can determine the unique() images. 28 | * it can find similar images given an input image. 29 | * It is build on core statistics, such as PCA, HOG for feature extraction, and therefore it does not has a dependency block. 30 | * It does not require a learning proces. 31 | * It works out of the box. 32 | * It provides explainable results. 33 | * It provided many plots to improve understanding of the feature-space and sample-sample relationships. 34 | 35 | 36 | Schematic overview 37 | ''''''''''''''''''' 38 | 39 | The schematic overview of our approach is as following: 40 | 41 | .. _schematic_overview: 42 | 43 | .. figure:: ../figs/schematic_overview.png 44 | 45 | 46 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/source/Cluster Evaluation.rst: -------------------------------------------------------------------------------- 1 | Gridsearch 2 | ############## 3 | 4 | For the detection of the optimal number of clusters, the python library ``clusteval`` is utilized to evaluate the **goodness** of clusters. 5 | The clustering approaches can be set to *agglomerative*, *kmeans*, *dbscan* and *hdbscan*, for which the ``clusteval`` library then searches across the space of clusters and method-parameters to determine the optimal number of clusters given the input dataset. 6 | 7 | **Cluster evaluation can be performed based on:** 8 | 9 | * Silhouette scores 10 | * DBindex 11 | * Derivative method 12 | 13 | Lets load the **digits** dataset and see how the different methods detects the optimal number of clusters. 14 | 15 | .. code:: python 16 | 17 | from clustimage import Clustimage 18 | # init 19 | cl = Clustimage() 20 | # Example data 21 | X, y = cl.import_example(data='mnist') 22 | 23 | 24 | Silhouette 25 | ############## 26 | 27 | The silhouette value is a measure of how similar a sample is to its own cluster (cohesion) compared to other clusters (separation). 28 | The scores ranges between [−1, 1], where a high value indicates that the object is well matched to its own cluster and poorly matched to neighboring clusters. 29 | The silhouette approach is thus a sample-wise approach, which means that for each sample, a silhouette score is computed and if most samples have a high value, then the clustering configuration is appropriate. 30 | If many points have a low or negative value, then the clustering configuration may have too many or too few clusters. 31 | The silhouette can be calculated with any distance metric, such as the Euclidean distance or the Manhattan distance. 32 | 33 | .. code:: python 34 | 35 | # Feature extraction and cluster evaluation 36 | results = cl.fit_transform(X, evaluate='silhouette', cluster='agglomerative') 37 | # Cluster using different (evaluation) method. 38 | # labels = cl.cluster(evaluate='silhouette', cluster='agglomerative', cluster_space='lower') 39 | # Plot the cluster evaluation 40 | cl.clusteval.plot() 41 | # Scatter 42 | cl.scatter(zoom=3) 43 | # Dendrogram 44 | cl.dendrogram() 45 | 46 | 47 | .. |figCE0| image:: ../figs/digits_clusters.png 48 | .. |figCE6| image:: ../figs/silhouette_tsne.png 49 | 50 | .. table:: Left: The number of clusters vs silhouette scores. Right: tSNE plot coloured on the cluster-labels. 51 | :align: center 52 | 53 | +----------+----------+ 54 | | |figCE0| | |figCE6| | 55 | +----------+----------+ 56 | 57 | 58 | The silhouette plot shows that the *#clusters* value of 11 is the best pick for the given data as the majority of clusters are above the average silhouette scores and does not widely fluctuate in the size of the silhouette plots. 59 | Note that the cluster evaluation is peformed on the high-dimensional feature space, in this case the top 50PCs. For illustration purposes, the 2D-space is shown and coloured on the detected cluster-labels. 60 | 61 | 62 | .. |figCE1| image:: ../figs/digits_fig1.png 63 | 64 | .. table:: Left: the sample-wise silhouette scores. Rright: First 2 PCs coloured on the cluster-labels. 65 | :align: center 66 | 67 | +----------+ 68 | | |figCE1| | 69 | +----------+ 70 | 71 | 72 | 73 | DBindex 74 | ############## 75 | 76 | The Davies–Bouldin index (DBI), is a metric for evaluating clustering algorithms. 77 | This is an internal evaluation scheme, which means that the clustering result is evaluated based on the data clustered itself (internal information). 78 | The lower the value, the tighter the clusters and the seperation between clusters. 79 | 80 | .. |figCE2| image:: ../figs/dbindex_eq1.png 81 | .. |figCE3| image:: ../figs/dbindex_eq2.jpg 82 | 83 | .. table:: The DB index for k number of clusters is defined as : 84 | :align: center 85 | 86 | +----------+----------+ 87 | | |figCE2| | |figCE3| | 88 | +----------+----------+ 89 | 90 | 91 | .. code:: python 92 | 93 | # After the fit transform you can directly try some other approaches for clustering without doing the preprocessing steps. 94 | # Cluster differently using directly the extracted features. 95 | results = cl.cluster(cluster='agglomerative', method='dbindex') 96 | 97 | # Scatter 98 | cl.scatter() 99 | # Dendrogram with cluster evalution 100 | cl.dendrogram() 101 | 102 | 103 | .. |figCE4| image:: ../figs/dbindex.png 104 | .. |figCE5| image:: ../figs/dbindex_tsne.png 105 | 106 | .. table:: Left: the DBindex cluster evaluation. Right: The tSNE plot coloured based on the cluster-labels. 107 | :align: center 108 | 109 | +----------+----------+ 110 | | |figCE4| | |figCE5| | 111 | +----------+----------+ 112 | 113 | The DBindex scores lowers gradually and stops at 24 clusters. This limit is the maximum search space by default. The search space can be altered using ``min_clust`` and ``max_clust`` in the function function :func:`clustimage.clustimage.Clustimage.fit_transform`. 114 | Note that the cluster evaluation is peformed on the high-dimensional feature space, in this case the top 50PCs. For illustration purposes, the 2D-space is coloured on the detected cluster-labels. 115 | 116 | 117 | DBSCAN 118 | ############## 119 | 120 | Density-Based Spatial Clustering of Applications with Noise is an clustering approach that finds core samples of high density and expands clusters from them. 121 | This works especially good when having samples which contains clusters of similar density. This approach is recommended in combination with a low-dimensional space, such as the coordinates from tSNE. 122 | 123 | .. code:: python 124 | 125 | # Feature extraction and cluster evaluation on the low-dimensional space 126 | results = cl.cluster(evaluate='silhouette', cluster='dbscan', cluster_space='low') 127 | # Scatter 128 | cl.scatter() 129 | # Dendrogram with cluster evalution 130 | cl.dendrogram() 131 | 132 | 133 | .. |figCE7| image:: ../figs/dbscan.png 134 | .. |figCE8| image:: ../figs/tsne_dbscan.png 135 | 136 | .. table:: Left: the DBindex cluster evaluation. Right: The tSNE plot coloured based on the cluster-labels. 137 | :align: center 138 | 139 | +----------+----------+ 140 | | |figCE7| | |figCE8| | 141 | +----------+----------+ 142 | 143 | 144 | .. |figCE9| image:: ../figs/silhouette_dbscan.png 145 | 146 | .. table:: Left: the DBindex cluster evaluation. Right: The tSNE plot coloured based on the cluster-labels. 147 | :align: center 148 | 149 | +----------+ 150 | | |figCE9| | 151 | +----------+ 152 | 153 | 154 | 155 | 156 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/source/Coding quality.rst: -------------------------------------------------------------------------------- 1 | 2 | Coding quality 3 | ''''''''''''''''''''' 4 | 5 | I value software quality. Higher quality software has fewer defects, better security, and better performance, which leads to happier users who can work more effectively. 6 | Code reviews are an effective method for improving software quality. McConnell (2004) suggests that unit testing finds approximately 25% of defects, function testing 35%, integration testing 45%, and code review 55-60%. 7 | While this means that none of these methods are good enough on their own and that they should be combined, clearly code review is an essential tool here. 8 | 9 | This library is therefore developed with several techniques, such as coding styling, low complexity, docstrings, reviews, and unit tests. 10 | Such conventions are helpfull to improve the quality, make the code cleaner and more understandable but alos to trace future bugs, and spot syntax errors. 11 | 12 | 13 | library 14 | -------- 15 | 16 | The file structure of the generated package looks like: 17 | 18 | 19 | .. code-block:: bash 20 | 21 | path/to/clustimage/ 22 | ├── .editorconfig 23 | ├── .gitignore 24 | ├── .pre-commit-config.yml 25 | ├── .prospector.yml 26 | ├── CHANGELOG.rst 27 | ├── docs 28 | │   ├── conf.py 29 | │   ├── index.rst 30 | │   └── ... 31 | ├── LICENSE 32 | ├── MANIFEST.in 33 | ├── NOTICE 34 | ├── clustimage 35 | │   ├── __init__.py 36 | │   ├── __version__.py 37 | │   └── clustimage.py 38 | ├── README.md 39 | ├── requirements.txt 40 | ├── setup.cfg 41 | ├── setup.py 42 | └── tests 43 | ├── __init__.py 44 | └── test_clustimage.py 45 | 46 | 47 | Style 48 | ------- 49 | 50 | This library is compliant with the PEP-8 standards. 51 | PEP stands for Python Enhancement Proposal and sets a baseline for the readability of Python code. 52 | Each public function contains a docstring that is based on numpy standards. 53 | 54 | 55 | Complexity 56 | ------------ 57 | 58 | This library has been developed by using measures that help decreasing technical debt. 59 | Version 0.1.0 of the ``clustimage`` library scored, according the code analyzer: **VALUE**, for which values > 0 are good and 10 is a maximum score. 60 | Developing software with low(er) technical dept may take extra development time, but has many advantages: 61 | 62 | * Higher quality code 63 | * easier maintanable 64 | * Less prone to bugs and errors 65 | * Higher security 66 | 67 | 68 | Unit tests 69 | ------------ 70 | 71 | The use of unit tests is essential to garantee a consistent output of developed functions. 72 | The following tests are secured using :func:`tests.test_clustimage`: 73 | 74 | * The input are checked. 75 | * The output values are checked and whether they are encoded properly. 76 | * The check of whether parameters are handled correctly. 77 | 78 | 79 | .. code-block:: bash 80 | 81 | pytest tests\test_clustimage.py 82 | 83 | ====================================== test session starts ====================================== 84 | platform win32 -- Python 3.6.10, pytest-5.4.0, py-1.8.1, pluggy-0.13.1 85 | collected 3 items 86 | 87 | tests\test_clustimage.py ... [100%] 88 | 89 | ======================================= warnings summary ======================================== 90 | tests/test_clustimage.py::test_plot 91 | 92 | =========================== 3 passed, 1 warning in 1254.97s (0:20:54) =========================== 93 | 94 | 95 | 96 | 97 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/source/Documentation.rst: -------------------------------------------------------------------------------- 1 | Sponsor 2 | ############ 3 | 4 | .. include:: sponsor.rst 5 | 6 | 7 | Blog 8 | ####### 9 | 10 | .. note:: 11 | For usage and more details, read the Medium Blog: `A step-by-step guide for clustering images `_ 12 | 13 | Github 14 | ####### 15 | 16 | .. note:: 17 | Source code of clustimage can be found at `Github `_ 18 | 19 | 20 | Colab Notebook 21 | ################# 22 | 23 | .. note:: 24 | Experiment with clustering images using the `Colab notebook`_. 25 | 26 | .. _Colab notebook: https://colab.research.google.com/github/erdogant/clustimage/blob/master/notebooks/clustimage.ipynb 27 | 28 | 29 | Citing 30 | ######### 31 | 32 | .. note:: 33 | Bibtex can be found at the right side at the `github page `_. 34 | 35 | 36 | 37 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/source/Installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ################ 3 | 4 | Create environment 5 | ********************** 6 | 7 | If desired, install ``clustimage`` from an isolated Python environment using conda: 8 | 9 | .. code-block:: python 10 | 11 | conda create -n env_clustimage python=3.8 12 | conda activate env_clustimage 13 | 14 | 15 | Pypi 16 | ********************** 17 | 18 | .. code-block:: console 19 | 20 | # Install from Pypi: 21 | pip install clustimage 22 | 23 | # Force update to latest version 24 | pip install -U clustimage 25 | 26 | 27 | Github source 28 | ************************************ 29 | 30 | .. code-block:: console 31 | 32 | # Install directly from github 33 | pip install git+https://github.com/erdogant/clustimage 34 | 35 | 36 | Uninstalling 37 | ################ 38 | 39 | Remove environment 40 | ********************** 41 | 42 | .. code-block:: console 43 | 44 | # List all the active environments. clustimage should be listed. 45 | conda env list 46 | 47 | # Remove the clustimage environment 48 | conda env remove --name clustimage 49 | 50 | # List all the active environments. clustimage should be absent. 51 | conda env list 52 | 53 | 54 | Remove installation 55 | ********************** 56 | 57 | Note that the removal of the environment will also remove the ``clustimage`` installation. 58 | 59 | .. code-block:: console 60 | 61 | # Install from Pypi: 62 | pip uninstall clustimage 63 | 64 | 65 | 66 | Quickstart 67 | ********************** 68 | 69 | A quick example how to learn a model on a given dataset. 70 | 71 | .. code:: python 72 | 73 | # Import library 74 | from clustimage import Clustimage 75 | 76 | # init with default parameters 77 | cl = Clustimage() 78 | 79 | # load example with flowers 80 | path_to_imgs = cl.import_example(data='flowers') 81 | 82 | # Run the model to find the optimal clusters 83 | results = cl.fit_transform(path_to_imgs, min_clust=10) 84 | 85 | # Cluster evaluation plot 86 | cl.clustimage.plot() 87 | 88 | # Unique images 89 | cl.results_unique.keys() 90 | cl.plot_unique(img_mean=False) 91 | 92 | # Scatter 93 | cl.scatter(dotsize=50, img_mean=False) 94 | 95 | # Plot clustered images 96 | cl.plot(labels=0) 97 | 98 | # Plot dendrogram 99 | cl.dendrogram() 100 | 101 | # Predict 102 | results_find = cl.find(path_to_imgs[0:5], k=None, alpha=0.05) 103 | cl.plot_find() 104 | cl.scatter() 105 | 106 | 107 | 108 | 109 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/source/Performance.rst: -------------------------------------------------------------------------------- 1 | 2 | Performance 3 | '''''''''''''''''''''' 4 | To measure the performance of various methods implementend in ``clustimage``, we can use the **digits** dataset to determine the match between clustered sampels and the true label. 5 | It can be seen that multiple different parameters still result in similar good performance based on the results below. 6 | 7 | The following peace of code clusters the **digit** images, compares the detected cluster labels with the true label, and finally computes the accuracy. 8 | 9 | .. code:: python 10 | 11 | import matplotlib.pyplot as plt 12 | from sklearn.datasets import load_digits 13 | from clustimage import Clustimage 14 | import classeval as clf 15 | import itertools as it 16 | from sklearn.metrics import accuracy_score 17 | 18 | # Load example data 19 | digits = load_digits(n_class=10) 20 | X, y_true = digits.data, digits.target 21 | 22 | param_grid = { 23 | 'method':['pca', 'hog', None], 24 | 'evaluate' : ['silhouette', 'dbindex', 'derivative'], 25 | 'cluster_space' : ['low', 'high'], 26 | } 27 | 28 | scores = [] 29 | labels = [] 30 | allNames = param_grid.keys() 31 | combinations = list(it.product(*(param_grid[Name] for Name in allNames))) 32 | 33 | # Iterate over all combinations 34 | for combination in combinations: 35 | # Initialize 36 | cl = Clustimage(method=combination[0]) 37 | 38 | # Preprocessing, feature extraction and cluster evaluation 39 | results = cl.fit_transform(X, cluster_space=combination[2], evaluate=combination[1]) 40 | 41 | # Compute confmat 42 | cm = clf.confmatrix.eval(y_true, results['labels'], normalize=False) 43 | 44 | # Transform numbers to make it comparible 45 | y_pred = results['labels'] 46 | cm_argmax = cm['confmat'].argmax(axis=0) 47 | y_pred_ = np.array([cm_argmax[i] for i in y_pred]) 48 | 49 | # Compute again confmat 50 | cm = clf.confmatrix.eval(y_true, y_pred_, normalize=False) 51 | fig,ax = clf.confmatrix.plot(cm) 52 | ax.set_title('Feature extraction: [%s]\nCluster evaluation with [%s] in [%s] dimension' %(combination[0], combination[1], combination[2]), fontsize=16) 53 | plt.pause(0.1) 54 | 55 | # Store scores and labels 56 | scores.append(accuracy_score(y_true,y_pred_)) 57 | labels.append(str(combination[0]) + ' - ' + combination[1] + ' - ' + combination[2]) 58 | 59 | # Make plot 60 | import numpy as np 61 | scores=np.array(scores) 62 | labels=np.array(labels) 63 | isort=np.argsort(scores) 64 | plt.figure(figsize=(12,6)) 65 | plt.plot(np.arange(0,len(scores)), scores[isort]) 66 | plt.xticks(np.arange(0,len(scores)), labels[isort], rotation='vertical') 67 | plt.margins(0.2) 68 | plt.title("Comparison of various approaches.", fontsize=14) 69 | plt.grid(True) 70 | 71 | 72 | .. |figP1| image:: ../figs/performance_approaches.png 73 | 74 | .. table:: Comparison of the performance for the digits dataset using various methods. 75 | :align: center 76 | 77 | +----------+ 78 | | |figP1| | 79 | +----------+ 80 | 81 | 82 | .. |figP2| image:: ../figs/best_digits.png 83 | .. |figP3| image:: ../figs/digits_pca_dbindex.png 84 | 85 | .. table:: Results of the best two approaches. 86 | :align: center 87 | 88 | +----------+----------+ 89 | | |figP2| | |figP3| | 90 | +----------+----------+ 91 | 92 | 93 | 94 | 95 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/source/Save and Load.rst: -------------------------------------------------------------------------------- 1 | Saving 2 | ########## 3 | 4 | Saving and loading models can be used to restore previous results. 5 | In order to accomplish this, we created two functions: function :func:`clustimage.clustimage.Clustimage.save`. and function :func:`clustimage.clustimage.Clustimage.load`. 6 | Below we illustrate how to save and load models. 7 | 8 | Saving the model with the results: 9 | 10 | .. code:: python 11 | 12 | from clustimage import Clustimage 13 | 14 | # Initialize 15 | cl = Clustimage(method='hog') 16 | # Load example data 17 | 18 | X, y = cl.import_example(data='mnist') 19 | # Preprocessing, feature extraction and cluster evaluation 20 | results = cl.fit_transform(X) 21 | 22 | # Load model 23 | cl.save('clustimage_model', overwrite=True) 24 | 25 | 26 | Loading 27 | ########## 28 | 29 | Loading a learned model can be done using the function :func:`clustimage.load`: 30 | 31 | .. code:: python 32 | 33 | from clustimage import Clustimage 34 | 35 | # Load model 36 | cl.load('clustimage_model') 37 | 38 | 39 | 40 | 41 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/source/_static/css/carbon_ads_hor.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #000; 3 | } 4 | 5 | #carbonads * { 6 | margin: initial; 7 | padding: initial; 8 | } 9 | 10 | #carbonads { 11 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 12 | Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, Arial, 13 | sans-serif; 14 | } 15 | 16 | #carbonads { 17 | display: flex; 18 | max-width: 330px; 19 | background-color: hsl(0, 0%, 10%); 20 | box-shadow: 0 0 1px hsla(0, 0%, 0%, 0.5); 21 | } 22 | 23 | #carbonads a { 24 | color: #ddd; 25 | text-decoration: none; 26 | } 27 | 28 | #carbonads a:hover { 29 | color: #ddd; 30 | } 31 | 32 | #carbonads span { 33 | position: relative; 34 | display: block; 35 | overflow: hidden; 36 | } 37 | 38 | #carbonads .carbon-wrap { 39 | display: flex; 40 | } 41 | 42 | #carbonads .carbon-img { 43 | display: block; 44 | margin: 0; 45 | line-height: 1; 46 | } 47 | 48 | #carbonads .carbon-img img { 49 | display: block; 50 | } 51 | 52 | #carbonads .carbon-text { 53 | padding: 10px; 54 | text-align: left; 55 | font-size: 13px; 56 | margin-bottom: 16px; 57 | line-height: 1.5; 58 | } 59 | 60 | #carbonads .carbon-poweredby { 61 | display: block; 62 | padding: 6px 8px; 63 | background: #1e2021; 64 | color: #aaa; 65 | text-align: center; 66 | text-transform: uppercase; 67 | letter-spacing: 0.5px; 68 | font-weight: 600; 69 | font-size: 8px; 70 | line-height: 1; 71 | position: absolute; 72 | bottom: 0; 73 | right: 0; 74 | border-top-left-radius: 6px; 75 | } 76 | -------------------------------------------------------------------------------- /docs/source/_static/css/carbon_ads_ver.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #000; 3 | } 4 | 5 | #carbonads { 6 | display: block; 7 | overflow: hidden; 8 | padding: 1em; 9 | max-width: 130px; 10 | background: hsl(0, 0%, 10%); 11 | box-shadow: 0 0 1px hsla(0, 0%, 0%, 0.5); 12 | text-align: center; 13 | font-size: 12px; 14 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 15 | line-height: 1.5; 16 | } 17 | 18 | #carbonads a { 19 | color: #eee; 20 | text-decoration: none; 21 | } 22 | 23 | #carbonads a:hover { 24 | color: inherit; 25 | } 26 | 27 | #carbonads span { 28 | display: block; 29 | overflow: hidden; 30 | } 31 | 32 | .carbon-img { 33 | display: block; 34 | margin: 0 auto 8px; 35 | line-height: 1; 36 | } 37 | 38 | .carbon-text { 39 | display: block; 40 | margin-bottom: 8px; 41 | } 42 | 43 | .carbon-poweredby { 44 | display: block; 45 | padding: 6px 10px; 46 | background: #1e2021; 47 | text-transform: uppercase; 48 | letter-spacing: 0.5px; 49 | font-size: 7px; 50 | line-height: 1; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /docs/source/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #111; 3 | } 4 | 5 | #carbonads * { 6 | margin: initial; 7 | padding: initial; 8 | } 9 | 10 | #carbonads { 11 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 12 | Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, Arial, 13 | sans-serif; 14 | } 15 | 16 | #carbonads { 17 | display: flex; 18 | max-width: 330px; 19 | background-color: hsl(0, 0%, 10%); 20 | box-shadow: 0 0 1px hsla(0, 0%, 0%, 0.5); 21 | } 22 | 23 | #carbonads a { 24 | color: #ddd; 25 | text-decoration: none; 26 | } 27 | 28 | #carbonads a:hover { 29 | color: #ddd; 30 | } 31 | 32 | #carbonads span { 33 | position: relative; 34 | display: block; 35 | overflow: hidden; 36 | } 37 | 38 | #carbonads .carbon-wrap { 39 | display: flex; 40 | } 41 | 42 | #carbonads .carbon-img { 43 | display: block; 44 | margin: 0; 45 | line-height: 1; 46 | } 47 | 48 | #carbonads .carbon-img img { 49 | display: block; 50 | } 51 | 52 | #carbonads .carbon-text { 53 | padding: 10px; 54 | text-align: left; 55 | font-size: 13px; 56 | margin-bottom: 16px; 57 | line-height: 1.5; 58 | } 59 | 60 | #carbonads .carbon-poweredby { 61 | display: block; 62 | padding: 6px 8px; 63 | background: #1e2021; 64 | color: #aaa; 65 | text-align: center; 66 | text-transform: uppercase; 67 | letter-spacing: 0.5px; 68 | font-weight: 600; 69 | font-size: 8px; 70 | line-height: 1; 71 | position: absolute; 72 | bottom: 0; 73 | right: 0; 74 | border-top-left-radius: 6px; 75 | } 76 | -------------------------------------------------------------------------------- /docs/source/_static/css/custom_rectangle.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #111; 3 | } 4 | 5 | #carbonads { 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | position: relative; 10 | 11 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, Helvetica, Arial, sans-serif; 12 | width: 275px; 13 | height: 215px; 14 | text-align: center; 15 | background-color: hsl(0, 0%, 10%); 16 | box-shadow: 0 0 1px hsla(0, 0%, 0%, 0.5); 17 | } 18 | 19 | #carbonads a { 20 | color: #eee; 21 | text-decoration: none; 22 | } 23 | 24 | #carbonads a:hover { 25 | color: #eee; 26 | } 27 | 28 | 29 | #carbonads>span { 30 | display: block; 31 | position: relative; 32 | overflow: hidden; 33 | padding: 15px; 34 | 35 | border-radius: 4px; 36 | background-color: #1e2021; 37 | box-shadow: 38 | 0 0 1px 1px hsla(0, 0%, 0%, .05), 39 | 0 0 2px 2px hsla(0, 0%, 0%, .05), 40 | 0 0 4px 4px hsla(0, 0%, 0%, .05); 41 | } 42 | 43 | 44 | .carbon-img { 45 | display: block; 46 | margin: 0 0 8px; 47 | 48 | line-height: 1; 49 | } 50 | 51 | .carbon-img img { 52 | width: 150px; 53 | max-width: 150px !important; 54 | height: auto; 55 | } 56 | 57 | .carbon-text { 58 | display: block; 59 | margin-bottom: 8px; 60 | padding: 0 10px; 61 | font-size: 13px; 62 | font-weight: 500; 63 | line-height: 1.35; 64 | font-weight: normal; 65 | } 66 | 67 | .carbon-poweredby { 68 | display: block; 69 | position: absolute; 70 | right: 0; 71 | bottom: 0; 72 | left: 0; 73 | padding: 5px 6px; 74 | font-size: 8px; 75 | line-height: 1; 76 | letter-spacing: .5px; 77 | text-transform: uppercase; 78 | color: #aaa; 79 | background-color: hsl(0, 0%, 10%); 80 | } 81 | -------------------------------------------------------------------------------- /docs/source/_templates/carbon_ads.html: -------------------------------------------------------------------------------- 1 | .. raw:: html 2 | 3 |
4 |
5 | 6 |
7 |
8 | -------------------------------------------------------------------------------- /docs/source/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | 3 | {% block menu %} 4 | {{ super() }} 5 | Index 6 | {% endblock %} 7 | 8 | -------------------------------------------------------------------------------- /docs/source/add_bottom.add: -------------------------------------------------------------------------------- 1 | .. raw:: html 2 | 3 |
4 |
5 | 6 | 7 | 8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /docs/source/add_carbon.add: -------------------------------------------------------------------------------- 1 | .. raw:: html 2 | 3 |
4 |
5 | 6 |
7 |
8 | -------------------------------------------------------------------------------- /docs/source/add_top.add: -------------------------------------------------------------------------------- 1 | .. raw:: html 2 | 3 |
4 |
5 | 6 |
7 | 8 |
9 |
10 | -------------------------------------------------------------------------------- /docs/source/clustimage.clustimage.rst: -------------------------------------------------------------------------------- 1 | API References 2 | ------------------------------------------------ 3 | 4 | 5 | .. automodule:: clustimage.clustimage 6 | :members: 7 | :undoc-members: 8 | 9 | 10 | 11 | 12 | .. include:: add_bottom.add -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | ######################################################################################## 2 | 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file only contains a selection of the most common options. For a full 6 | # list see the documentation: 7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | import os 16 | import sys 17 | sys.path.insert(0, os.path.abspath('../../')) 18 | import clustimage 19 | 20 | currpath = os.path.dirname(__file__) 21 | sys.path.insert(0, os.path.abspath('./')) 22 | from helper import * 23 | 24 | ######################################################################################## 25 | # -- Download rst file ----------------------------------------------------- 26 | download_file('https://erdogant.github.io/docs/rst/sponsor.rst', "sponsor.rst") 27 | download_file('https://erdogant.github.io/docs/rst/add_carbon.add', "add_carbon.add") 28 | download_file('https://erdogant.github.io/docs/rst/add_top.add', "add_top.add") 29 | download_file('https://erdogant.github.io/docs/rst/add_bottom.add', "add_bottom.add") 30 | ######################################################################################## 31 | add_includes_to_rst_files(top=False, bottom=True) 32 | ######################################################################################## 33 | # Import PDF from directory in rst files 34 | # embed_in_rst(currpath, 'pdf', '.pdf', "Additional Information", 'Additional_Information.rst') 35 | ######################################################################################## 36 | # Import notebooks in HTML format 37 | # convert_ipynb_to_html(currpath, 'notebooks', '.ipynb') 38 | # embed_in_rst(currpath, 'notebooks', '.html', "Notebook", 'notebook.rst') 39 | ######################################################################################## 40 | 41 | # -- Project information ----------------------------------------------------- 42 | 43 | project = 'clustimage' 44 | copyright = '2020, Erdogan Taskesen' 45 | author = 'Erdogan Taskesen' 46 | 47 | # The master toctree document. 48 | master_doc = 'index' 49 | 50 | # The full version, including alpha/beta/rc tags 51 | release = 'clustimage' 52 | version = str(clustimage.__version__) 53 | 54 | # -- General configuration --------------------------------------------------- 55 | 56 | # Add any Sphinx extension module names here, as strings. They can be 57 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 58 | # ones. 59 | extensions = [ 60 | "sphinx.ext.autodoc", 61 | "sphinx.ext.napoleon", 62 | "sphinx.ext.intersphinx", 63 | "sphinx.ext.autosectionlabel", 64 | "rst2pdf.pdfbuilder", 65 | ] 66 | 67 | napoleon_google_docstring = False 68 | napoleon_numpy_docstring = True 69 | 70 | # autodoc_mock_imports = ['cv2','keras'] 71 | 72 | 73 | pdf_documents = [('index', u'clustimage', u'clustimage', u'Erdogan Taskesen'),] 74 | 75 | # Add any paths that contain templates here, relative to this directory. 76 | templates_path = ['_templates'] 77 | 78 | # List of patterns, relative to source directory, that match files and 79 | # directories to ignore when looking for source files. 80 | # This pattern also affects html_static_path and html_extra_path. 81 | exclude_patterns = ["_build"] 82 | 83 | 84 | # The name of the Pygments (syntax highlighting) style to use. 85 | pygments_style = "sphinx" 86 | 87 | # -- Options for HTML output ------------------------------------------------- 88 | 89 | # The theme to use for HTML and HTML Help pages. See the documentation for 90 | # a list of builtin themes. 91 | # 92 | # html_theme = 'alabaster' 93 | # html_theme = 'default' 94 | html_theme = 'sphinx_rtd_theme' 95 | 96 | 97 | # Add any paths that contain custom static files (such as style sheets) here, 98 | # relative to this directory. They are copied after the builtin static files, 99 | # so a file named "default.css" will overwrite the builtin "default.css". 100 | html_static_path = ['_static'] 101 | 102 | 103 | # These paths are either relative to html_static_path 104 | # or fully qualified paths (eg. https://...) 105 | html_css_files = ['css/custom.css',] 106 | 107 | # html_sidebars = { '**': ['globaltoc.html', 'relations.html', 'carbon_ads.html', 'sourcelink.html', 'searchbox.html'] } 108 | 109 | 110 | -------------------------------------------------------------------------------- /docs/source/helper.py: -------------------------------------------------------------------------------- 1 | import os 2 | from glob import glob 3 | import numpy as np 4 | 5 | # %% Download rst file 6 | def download_file(url_rst, filename): 7 | try: 8 | from urllib.request import urlretrieve 9 | if os.path.isfile(filename): 10 | os.remove(filename) 11 | print('Download %s..' %(filename)) 12 | urlretrieve(url_rst, filename) 13 | except: 14 | print('Downloading %s failed.' %(url_rst)) 15 | 16 | # %% Include ADD to rst files 17 | def add_includes_to_rst_files(top=True, bottom=True): 18 | skipfiles = ['sponsor.rst'] 19 | for file_path in glob("*.rst"): 20 | if not np.isin(file_path, skipfiles): 21 | with open(file_path, "r+", encoding="utf8") as file: 22 | contents = file.read() 23 | if top and ".. include:: add_top.add" not in contents: 24 | file.seek(0) 25 | file.write(".. include:: add_top.add\n\n" + contents) 26 | print('Top Add included >%s' %(file_path)) 27 | elif (not top) and ".. include:: add_top.add" in contents: 28 | contents = contents.replace(".. include:: add_top.add\n\n", "") 29 | print('Remove Top Add>%s' %(file_path)) 30 | file.seek(0) 31 | file.truncate() 32 | file.write(contents) 33 | 34 | if bottom and ".. include:: add_bottom.add" not in contents: 35 | file.seek(0, 2) 36 | file.write("\n\n.. include:: add_bottom.add") 37 | print('Bottom Add included >%s' %(file_path)) 38 | elif (not bottom) and ".. include:: add_bottom.add" in contents: 39 | contents = contents.replace(".. include:: add_bottom.add\n\n", "") 40 | print('Remove Bottom Add>%s' %(file_path)) 41 | file.seek(0) 42 | file.truncate() 43 | file.write(contents) 44 | 45 | # %% ADD TO REST 46 | def adds_in_rst(filehandle): 47 | # Write carbon adds 48 | filehandle.write("\n\n.. raw:: html\n") 49 | filehandle.write("\n
") 50 | filehandle.write("\n
") 51 | filehandle.write('\n ') 52 | filehandle.write("\n
") 53 | filehandle.write("\n
") 54 | 55 | # %% SCAN DIRECTORY 56 | def scan_directory(currpath, directory, ext): 57 | # Uitlezen op ext 58 | path_to_files = os.path.join(currpath, '_static', directory) 59 | files_in_dir = np.array(os.listdir(path_to_files)) 60 | Iloc = np.array(list(map(lambda x: x[-len(ext):]==ext, files_in_dir))) 61 | return files_in_dir[Iloc] 62 | 63 | # %% EMBED PDF IN RST 64 | def embed_in_rst(currpath, directory, ext, title, file_rst): 65 | 66 | try: 67 | # Uitlezen op extensie 68 | files_in_dir = scan_directory(currpath, directory, ext) 69 | print('---------------------------------------------------------------') 70 | print('[%s] embedding in RST from directory: [%s]' %(ext, directory)) 71 | 72 | # Open file 73 | filehandle = open(file_rst, 'w') 74 | filehandle.write(".. _code_directive:\n\n" + title + "\n#######################\n\n") 75 | 76 | # 3. simple concat op 77 | for fname in files_in_dir: 78 | print('[%s] processed in rst' %(fname)) 79 | title = "**" + fname[:-len(ext)] + "**\n" 80 | if ext=='.pdf': 81 | newstr = ":pdfembed:`src:_static/" + directory + "/" + fname + ", height:600, width:700, align:middle`" 82 | elif ext=='.html': 83 | newstr = ".. raw:: html\n\n" + ' ' 84 | write_to_rst = title + "\n" + newstr + "\n\n\n\n" 85 | # Write to rst 86 | filehandle.write(write_to_rst) 87 | 88 | # ADDs in RST wegschrijven 89 | adds_in_rst(filehandle) 90 | # Close file 91 | filehandle.close() 92 | except: 93 | print('ERROR IN EMBEDDING IT IN RST.') 94 | 95 | # %% CONVERT NOTEBOOKS TO HTML 96 | def convert_ipynb_to_html(currpath, directory, ext): 97 | try: 98 | # Uitlezen op extensie 99 | files_in_dir = scan_directory(currpath, directory, ext) 100 | # 3. simple concat op 101 | for fname in files_in_dir: 102 | path_to_file = os.path.join('_static/', directory, fname) 103 | print('[%s] converting to HTML' %(path_to_file)) 104 | os.system('jupyter nbconvert --to html ' + path_to_file) 105 | except: 106 | print('ERROR IN CONVERTING NOTEBOOK TO HTML.') 107 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | clustimage's documentation! 2 | =========================== 3 | 4 | |python| |pypi| |docs| |stars| |LOC| |downloads_month| |downloads_total| |license| |forks| |open issues| |project status| |medium| |colab| |DOI| |repo-size| |donate| 5 | 6 | .. tip:: 7 | `Medium Blog: A step-by-step guide for clustering images `_ 8 | 9 | 10 | .. |fig1| image:: ../figs/schematic_overview.png 11 | 12 | .. table:: 13 | :align: center 14 | 15 | +----------+ 16 | | |fig1| | 17 | +----------+ 18 | 19 | ----------------------------------- 20 | 21 | The aim of ``clustimage`` is to detect natural groups or clusters of images. 22 | 23 | Many computer vision tasks rely on (deep) neural networks, and aim to predict "what's on the image". However, not all tasks require supervised approaches or neural networks. With an unsupervised approach, we can aim to determine natural groups or clusters of images without being constrained to a fixed number of (learned) categories. In this blog, I will summarize the concepts of unsupervised clustering, followed by a hands-on tutorial on how to pre-process images, extract features (PCA, HOG), and group images with high similarity taking into account the goodness of the clustering. I will demonstrate the clustering of the MNIST dataset, the 101 objects dataset, the flower dataset, and finally the clustering of faces using the Olivetti dataset. All results are derived using the Python library clustimage. 24 | 25 | ``clustimage`` is a generic approach for unsupervised images clustering and overcomes the following challenges: 26 | 27 | * 1. Robustly groups similar images. 28 | * 2. Returns the unique images. 29 | * 3. Many plots for deeper exploration. 30 | * 4. Finds higly similar images for a given input image. 31 | 32 | 33 | ----------------------------------- 34 | 35 | .. note:: 36 | **Your ❤️ is important to keep maintaining this package.** You can `support `_ in various ways, have a look at the `sponser page `_. 37 | Report bugs, issues and feature extensions at `github `_ page. 38 | 39 | .. code-block:: console 40 | 41 | pip install clustimage 42 | 43 | ----------------------------------- 44 | 45 | 46 | 47 | Contents 48 | ======== 49 | 50 | .. toctree:: 51 | :maxdepth: 1 52 | :caption: Background 53 | 54 | Abstract 55 | 56 | 57 | .. toctree:: 58 | :maxdepth: 1 59 | :caption: Installation 60 | 61 | Installation 62 | 63 | 64 | .. toctree:: 65 | :maxdepth: 1 66 | :caption: Core functionalities 67 | 68 | core_functions 69 | 70 | .. toctree:: 71 | :maxdepth: 1 72 | :caption: Feature Extraction 73 | 74 | Feature Extraction 75 | 76 | 77 | .. toctree:: 78 | :maxdepth: 1 79 | :caption: Cluster Evaluation 80 | 81 | Cluster Evaluation 82 | 83 | .. toctree:: 84 | :maxdepth: 1 85 | :caption: Find/Predict 86 | 87 | Find 88 | 89 | 90 | .. toctree:: 91 | :maxdepth: 1 92 | :caption: Performance 93 | 94 | Performance 95 | 96 | .. toctree:: 97 | :maxdepth: 1 98 | :caption: Save and Load 99 | 100 | Save and Load 101 | 102 | 103 | .. toctree:: 104 | :maxdepth: 1 105 | :caption: Examples 106 | 107 | Examples 108 | 109 | 110 | .. toctree:: 111 | :maxdepth: 1 112 | :caption: Documentation 113 | 114 | Documentation 115 | Coding quality 116 | clustimage.clustimage 117 | 118 | 119 | 120 | 121 | 122 | Indices and tables 123 | ================== 124 | 125 | * :ref:`genindex` 126 | * :ref:`modindex` 127 | * :ref:`search` 128 | 129 | 130 | 131 | .. |repo-size| image:: https://img.shields.io/github/repo-size/erdogant/clustimage 132 | :alt: repo-size 133 | :target: https://img.shields.io/github/repo-size/erdogant/clustimage 134 | 135 | .. |stars| image:: https://img.shields.io/github/stars/erdogant/clustimage 136 | :alt: Stars 137 | :target: https://img.shields.io/github/stars/erdogant/clustimage 138 | 139 | .. |python| image:: https://img.shields.io/pypi/pyversions/clustimage.svg 140 | :alt: |Python 141 | :target: https://erdogant.github.io/clustimage/ 142 | 143 | .. |pypi| image:: https://img.shields.io/pypi/v/clustimage.svg 144 | :alt: |Python Version 145 | :target: https://pypi.org/project/clustimage/ 146 | 147 | .. |docs| image:: https://img.shields.io/badge/Sphinx-Docs-blue.svg 148 | :alt: Sphinx documentation 149 | :target: https://erdogant.github.io/clustimage/ 150 | 151 | .. |LOC| image:: https://sloc.xyz/github/erdogant/clustimage/?category=code 152 | :alt: lines of code 153 | :target: https://github.com/erdogant/clustimage 154 | 155 | .. |downloads_month| image:: https://static.pepy.tech/personalized-badge/clustimage?period=month&units=international_system&left_color=grey&right_color=brightgreen&left_text=PyPI%20downloads/month 156 | :alt: Downloads per month 157 | :target: https://pepy.tech/project/clustimage 158 | 159 | .. |downloads_total| image:: https://static.pepy.tech/personalized-badge/clustimage?period=total&units=international_system&left_color=grey&right_color=brightgreen&left_text=Downloads 160 | :alt: Downloads in total 161 | :target: https://pepy.tech/project/clustimage 162 | 163 | .. |license| image:: https://img.shields.io/badge/license-MIT-green.svg 164 | :alt: License 165 | :target: https://github.com/erdogant/clustimage/blob/master/LICENSE 166 | 167 | .. |forks| image:: https://img.shields.io/github/forks/erdogant/clustimage.svg 168 | :alt: Github Forks 169 | :target: https://github.com/erdogant/clustimage/network 170 | 171 | .. |open issues| image:: https://img.shields.io/github/issues/erdogant/clustimage.svg 172 | :alt: Open Issues 173 | :target: https://github.com/erdogant/clustimage/issues 174 | 175 | .. |project status| image:: http://www.repostatus.org/badges/latest/active.svg 176 | :alt: Project Status 177 | :target: http://www.repostatus.org/#active 178 | 179 | .. |medium| image:: https://img.shields.io/badge/Medium-Blog-green.svg 180 | :alt: Medium Blog 181 | :target: https://erdogant.github.io/clustimage/pages/html/Documentation.html#medium-blog 182 | 183 | .. |donate| image:: https://img.shields.io/badge/Support%20this%20project-grey.svg?logo=github%20sponsors 184 | :alt: donate 185 | :target: https://erdogant.github.io/clustimage/pages/html/Documentation.html# 186 | 187 | .. |colab| image:: https://colab.research.google.com/assets/colab-badge.svg 188 | :alt: Colab example 189 | :target: https://erdogant.github.io/clustimage/pages/html/Documentation.html#colab-notebook 190 | 191 | .. |DOI| image:: https://zenodo.org/badge/423822054.svg 192 | :alt: Cite 193 | :target: https://zenodo.org/badge/latestdoi/423822054 194 | 195 | 196 | .. include:: add_bottom.add 197 | -------------------------------------------------------------------------------- /docs/source/requirements.txt: -------------------------------------------------------------------------------- 1 | pip install sphinx_rtd_theme 2 | 3 | -------------------------------------------------------------------------------- /docs/source/sponsor.rst: -------------------------------------------------------------------------------- 1 | .. tip:: 2 | You can support this project in various ways ❤️ 3 | 4 | * **Star** this repo at the github page. 5 | * Become a `Sponsor `_! 6 | * `Buy `_ me a coffee! I ❤️ coffee :) 7 | * Follow me on Medium! Go to my `medium profile `_ and press *follow*. 8 | * Subscribe to me on Substack! Go to my `profile `_ where you can *subscribe*. 9 | * You can also buy a digital educational product over `here `_. 10 | * Contribute in the form of feature requests, idea discussions, reporting bugs, opening pull requests. 11 | 12 | Cheers Mate. 13 | 14 | .. raw:: html 15 | 16 | 20 | -------------------------------------------------------------------------------- /notebooks/Untitled.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [], 3 | "metadata": {}, 4 | "nbformat": 4, 5 | "nbformat_minor": 5 6 | } 7 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "clustimage" 7 | dynamic = ["version"] 8 | authors = [{ name = "Erdogan Taskesen", email = "erdogant@gmail.com" },] 9 | description = "Python package clustimage is for unsupervised clustering of images." 10 | readme = "README.md" 11 | requires-python = ">=3" 12 | license = { file = "LICENSE" } 13 | keywords = ["clustering", "images", "unsupervised learning"] 14 | classifiers = [ 15 | "Programming Language :: Python :: 3", 16 | "License :: OSI Approved :: MIT License", 17 | "Operating System :: OS Independent", 18 | ] 19 | dependencies = [ 20 | "datazets>=1.0.0", 21 | "scatterd>=1.3.6", 22 | "ismember", 23 | "pypickle", 24 | "pca>=2.0.4", 25 | "distfit", 26 | "clusteval>=2.1.5", 27 | "colourmap>=1.1.16", 28 | "numpy", 29 | "pandas", 30 | "tqdm", 31 | "scikit-learn", 32 | "scikit-image", 33 | "matplotlib", 34 | "scipy", 35 | "piexif", 36 | "imagehash", 37 | "pillow-heif", 38 | "geopy", 39 | "folium", 40 | "opencv-python-headless", 41 | 42 | ] 43 | 44 | [project.urls] 45 | Homepage = "https://erdogant.github.io/clustimage" 46 | Download = "https://github.com/erdogant/clustimage/archive/{version}.tar.gz" 47 | 48 | [tool.setuptools] 49 | packages = ["clustimage"] 50 | include-package-data = true 51 | 52 | [tool.setuptools.dynamic] 53 | version = { attr = "clustimage.__version__" } 54 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | sphinx_rtd_theme 3 | rst2pdf 4 | sphinx 5 | pytest 6 | #git+https://github.com/SuperKogito/sphinxcontrib-pdfembed.git 7 | irelease 8 | sphinxcontrib-fulltoc 9 | tabulate 10 | #nbconvert # jupyter nbconvert --to html notebook.ipynb 11 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | datazets>=1.0.0 2 | clusteval>=2.1.5 3 | colourmap>=1.1.16 4 | scatterd>=1.3.6 5 | pca>=2.0.4 6 | distfit 7 | numpy 8 | pandas 9 | tqdm 10 | scikit-learn 11 | matplotlib 12 | scikit-image 13 | scipy 14 | pypickle 15 | imagehash 16 | pypickle 17 | ismember 18 | pillow-heif 19 | piexif 20 | 21 | geopy 22 | folium 23 | opencv-python-headless -------------------------------------------------------------------------------- /requirements_additional.txt: -------------------------------------------------------------------------------- 1 | piexif 2 | geopy 3 | folium 4 | opencv-python-headless 5 | # opencv-python 6 | 7 | -------------------------------------------------------------------------------- /setup.py.deprecated: -------------------------------------------------------------------------------- 1 | import setuptools 2 | import re 3 | 4 | # versioning ------------ 5 | VERSIONFILE="clustimage/__init__.py" 6 | getversion = re.search( r"^__version__ = ['\"]([^'\"]*)['\"]", open(VERSIONFILE, "rt").read(), re.M) 7 | if getversion: 8 | new_version = getversion.group(1) 9 | else: 10 | raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,)) 11 | 12 | # Setup ------------ 13 | with open("README.md", "r", encoding="utf8") as fh: 14 | long_description = fh.read() 15 | setuptools.setup( 16 | install_requires=['datazets>=1.0.0', 17 | 'scatterd>=1.3.6', 18 | 'ismember', 19 | 'pypickle', 20 | 'pca>=2.0.4', 21 | 'distfit', 22 | 'clusteval>=2.1.5', 23 | 'colourmap>=1.1.16', 24 | 'numpy', 25 | 'pandas', 26 | 'tqdm', 27 | 'scikit-learn', 28 | 'scikit-image', 29 | 'matplotlib', 30 | 'scipy', 31 | 'piexif', 32 | 'imagehash', 33 | 'pillow-heif'], 34 | python_requires='>=3', 35 | name='clustimage', 36 | version=new_version, 37 | author="Erdogan Taskesen", 38 | author_email="erdogant@gmail.com", 39 | description="Python package clustimage is for unsupervised clustering of images.", 40 | long_description=long_description, 41 | long_description_content_type="text/markdown", 42 | url="https://erdogant.github.io/clustimage", 43 | download_url = 'https://github.com/erdogant/clustimage/archive/'+new_version+'.tar.gz', 44 | packages=setuptools.find_packages(), # Searches throughout all dirs for files to include 45 | include_package_data=True, # Must be true to include files depicted in MANIFEST.in 46 | license_files=["LICENSE"], 47 | classifiers=[ 48 | "Programming Language :: Python :: 3", 49 | "License :: OSI Approved :: MIT License", 50 | "Operating System :: OS Independent", 51 | ], 52 | ) 53 | --------------------------------------------------------------------------------