diff --git a/README.md b/README.md deleted file mode 100644 index 4a064f841..000000000 --- a/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Pattern Analysis -Pattern Analysis of various datasets by COMP3710 students at the University of Queensland. - -We create pattern recognition and image processing library for Tensorflow (TF), PyTorch or JAX. - -This library is created and maintained by The University of Queensland [COMP3710](https://my.uq.edu.au/programs-courses/course.html?course_code=comp3710) students. - -The library includes the following implemented in Tensorflow: -* fractals -* recognition problems - -In the recognition folder, you will find many recognition problems solved including: -* OASIS brain segmentation -* Classification -etc. diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/.gitignore b/recognition/ImprovedUNet-ISIC2018-45293915/.gitignore new file mode 100644 index 000000000..58aa9b3a8 --- /dev/null +++ b/recognition/ImprovedUNet-ISIC2018-45293915/.gitignore @@ -0,0 +1,185 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Ignore everything in the specified folders +datasets/training_groundtruth/* +datasets/validation_groundtruth/* +datasets/test_groundtruth/* +datasets/training_input/* +datasets/validation_input/* +datasets/test_input/* +/modelenv/* +output/* +models/* + +# But do not ignore the .gitkeep files +!datasets/training_groundtruth/.gitkeep +!datasets/validation_groundtruth/.gitkeep +!datasets/test_groundtruth/.gitkeep +!datasets/training_input/.gitkeep +!datasets/validation_input/.gitkeep +!datasets/test_input/.gitkeep +!/modelenv/.gitkeep +!output/.gitkeep +!models/.gitkeep + +# .keras files +*.keras diff --git a/LICENSE b/recognition/ImprovedUNet-ISIC2018-45293915/LICENSE similarity index 98% rename from LICENSE rename to recognition/ImprovedUNet-ISIC2018-45293915/LICENSE index 261eeb9e9..29f81d812 100644 --- a/LICENSE +++ b/recognition/ImprovedUNet-ISIC2018-45293915/LICENSE @@ -1,201 +1,201 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/README.md b/recognition/ImprovedUNet-ISIC2018-45293915/README.md new file mode 100644 index 000000000..bacf1f51d --- /dev/null +++ b/recognition/ImprovedUNet-ISIC2018-45293915/README.md @@ -0,0 +1,132 @@ +# Improved U-Net for ISIC 2018 Skin Lesion Segmentation + +This repository contains the code for the Improved U-Net model for the ISIC 2018 Skin Lesion Segmentation dataset (https://challenge.isic-archive.com/data/#2018). The model contained 2594 dermoscopic images of skin lesions and was trained on a single NVIDIA RTX 3080. The model was trained for 5 epochs, with a batch size of 2. The model was trained using the Adam optimiser with a learning rate of 0.0005. The model achieved a dice coefficient of 0.81 on the test set. + +## Architecture Description + +The Improved U-Net is a cutting-edge neural network architecture which can be tailored for biomedical image segmentation tasks. Originally inspired by the U-Net architecture, this version boasts enhancements that further optimise its accuracy and performance. The algorithm effectively addresses the problem of segmenting skin lesions from dermoscopic images, a crucial step in early skin cancer detection. + +## How It Works + +### Upsampling the Feature Maps: + +- The first step in the localisation module is to upsample the feature maps coming from the deeper layers (lower spatial resolution) to a higher spatial resolution. +- Instead of directly using a transposed convolution, the Improved U-Net often employs a simpler upscale mechanism. This could involve just doubling each pixel value or using a simple bilinear or nearest-neighbor interpolation. +- After the upscale, a 2D convolution is applied. This helps in refining the upsampled feature maps and can reduce the number of feature channels (if required). + +### Concatenation with Skip Connection: + +- Feature maps from the corresponding level in the downsampling pathway (or the encoder) are concatenated with the upsampled feature maps. This is the hallmark of the U-Net architecture and is referred to as a skip connection. +- The concatenated feature maps combine the high-resolution spatial details from the encoder with the high-level contextual information from the decoder. + +![Improved UNet Model Architecture](assets/architecture.png) + +Above is an image that showcases the localisation module in the context of processing 3 dimensional data. + +## Dependencies + +- **TensorFlow**: version 2.14.0 +- **Keras**: version 2.14.0 +- **NumPy**: version 1.26.1 +- **TensorFlow Addons**: version 0.22.0 +- **TensorFlow Estimator**: version 2.14.0 +- **Matplotlib**: version 3.8.0 +- **Python**: version 3.10.12 + + +## Reproducibility + +To ensure the reproducibility of results: +- We use fixed random seeds. +- Exact versions of all dependencies are listed. +- Training procedures, including data augmentation strategies and hyperparameters, are documented in detail. + +## Example Inputs and Outputs + +**1**: + +![Visaulisation 1](assets/visualisation1.png) + +**2**: + +![Visaulisation 2](assets/visualisation2.png) + +**3**: + +![Visaulisation 3](assets/visualisation3.png) + +## Evaluation Metrics + +### Accuracy and Loss + +![Accuracy and Loss](assets/accuracy_loss.png) + +### Dice Coefficient + +![Dice Coefficient](assets/dice_coefficient.png) + +## Final Metrics For Test Set + +``` +Test Accuracy: 0.8966387510299683 +Test Loss: 0.20560072362422943 +Test Dice Coefficient: 0.8109999895095825 +``` + + +## Data Pre-processing + +For the image segmentation task, a series of data pre-processing steps were performed to prepare the input images and corresponding ground truth masks for training and validation. + +### Input Images + +- **Color Mode:** The color mode for the input images was set to `image_mode`, which is typically 'rgb' for full-color images. + +- **Resising:** To ensure consistency, all input images were resised to a common dimension of `image_height` pixels in height and `image_width` pixels in width (e.g., 512x512 pixels). + +- **Normalisation:** Normalisation was applied to the input images by rescaling their pixel values to have zero mean and unit variance. This was done using the formula `rescale=1.0 / 255`. + +- **Data Augmentation:** Data augmentation techniques were employed to increase the diversity of the training dataset. Augmentation options included shear transformations with a range of `shear_range`, zooming transformations within `zoom_range`, horizontal flips (`horizontal_flip`), and vertical flips (`vertical_flip`). These augmentations help the model generalize better to different variations of the input data. + +- **Fill Mode:** The `fill_mode` parameter determined how newly created pixels, if any, were filled during data augmentation. Common options include 'nearest,' 'constant,' and 'reflect.' + +### Ground Truth Masks + +- **Color Mode:** The color mode for the ground truth masks was set to `mask_mode`, which is typically 'grayscale' for binary masks. + +- **Resising:** Similar to the input images, the ground truth masks were resised to the same dimensions of `image_height` pixels in height and `image_width` pixels in width (e.g., 512x512 pixels). + +Overall, these pre-processing steps ensured that both input images and ground truth masks were appropriately sized, normalised, and augmented for training and validation of the image segmentation model. + +**References**: +- https://arxiv.org/pdf/1802.10508v1.pdf + +## Dataset Folder Structure + +``` +root_directory +│ +├── datasets +│ ├ +│ ├── training_input +│ ├── training_groundtruth +│ ├── validation_input +│ ├── validation_groundtruth +│ ├── test_input +│ └── test_groundtruth +├── output +├── models +├── assets +│ +├── train.py +├── modules.py +├── dataset.py +├── predict.py +├── utils.py +├── validation.py +└── requirements.txt +``` + +## Data Splits + +The ISIC 2018 data was provided through 6 zip files for 3 types of data: Training, Validation and Testing. The training data contained 2594 images, the validation data contained 100 images and the testing data contained 1000 images. \ No newline at end of file diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/assets/accuracy_loss.png b/recognition/ImprovedUNet-ISIC2018-45293915/assets/accuracy_loss.png new file mode 100644 index 000000000..990271937 Binary files /dev/null and b/recognition/ImprovedUNet-ISIC2018-45293915/assets/accuracy_loss.png differ diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/assets/architecture.png b/recognition/ImprovedUNet-ISIC2018-45293915/assets/architecture.png new file mode 100644 index 000000000..1c5aab0ae Binary files /dev/null and b/recognition/ImprovedUNet-ISIC2018-45293915/assets/architecture.png differ diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/assets/dice_coefficient.png b/recognition/ImprovedUNet-ISIC2018-45293915/assets/dice_coefficient.png new file mode 100644 index 000000000..4da408c33 Binary files /dev/null and b/recognition/ImprovedUNet-ISIC2018-45293915/assets/dice_coefficient.png differ diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/assets/visualisation1.png b/recognition/ImprovedUNet-ISIC2018-45293915/assets/visualisation1.png new file mode 100644 index 000000000..244da4c9b Binary files /dev/null and b/recognition/ImprovedUNet-ISIC2018-45293915/assets/visualisation1.png differ diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/assets/visualisation2.png b/recognition/ImprovedUNet-ISIC2018-45293915/assets/visualisation2.png new file mode 100644 index 000000000..854baa365 Binary files /dev/null and b/recognition/ImprovedUNet-ISIC2018-45293915/assets/visualisation2.png differ diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/assets/visualisation3.png b/recognition/ImprovedUNet-ISIC2018-45293915/assets/visualisation3.png new file mode 100644 index 000000000..7065e28b8 Binary files /dev/null and b/recognition/ImprovedUNet-ISIC2018-45293915/assets/visualisation3.png differ diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/dataset.py b/recognition/ImprovedUNet-ISIC2018-45293915/dataset.py new file mode 100644 index 000000000..f560938fe --- /dev/null +++ b/recognition/ImprovedUNet-ISIC2018-45293915/dataset.py @@ -0,0 +1,74 @@ +import os +import tensorflow as tf +from tensorflow import keras + +class DataLoader: + """ + A utility class for creating data generators for image segmentation tasks. + It prepares and generates batches of input images and corresponding ground truth masks + for training or validation purposes. + + Parameters: + - input_dir: The directory containing input images. + - groundtruth_dir: The directory containing corresponding ground truth masks. + - image_mode: The color mode for input images (e.g., 'rgb'). + - mask_mode: The color mode for ground truth masks (e.g., 'grayscale'). + - image_height: The desired height of the input images. + - image_width: The desired width of the input images. + - batch_size: The batch size for data generation. + - seed: The random seed for data augmentation (default is 45). + - shear_range: The range for shear transformations during data augmentation. + - zoom_range: The range for zooming transformations during data augmentation. + - horizontal_flip: Whether to perform horizontal flips during data augmentation. + - vertical_flip: Whether to perform vertical flips during data augmentation. + - fill_mode: The fill mode for filling in newly created pixels during data augmentation. + + Methods: + - create_data_generators: Creates and returns data generators for input images and masks. + """ + def __init__(self, input_dir, groundtruth_dir, image_mode, mask_mode, image_height, image_width, batch_size, seed=45, shear_range=0.1, zoom_range=0.1, horizontal_flip=True, vertical_flip=True, fill_mode='nearest',): + self.input_dir = input_dir + self.groundtruth_dir = groundtruth_dir + self.image_mode = image_mode + self.mask_mode = mask_mode + self.image_height = image_height + self.image_width = image_width + self.batch_size = batch_size + self.seed = seed + self.shear_range = shear_range + self.zoom_range = zoom_range + self.horizontal_flip = horizontal_flip + self.vertical_flip = vertical_flip + self.fill_mode = fill_mode + + def create_data_generators(self): + data_gen_args = dict( + rescale=1.0 / 255, + shear_range=self.shear_range, + zoom_range=self.zoom_range, + horizontal_flip=self.horizontal_flip, + vertical_flip=self.vertical_flip, + fill_mode=self.fill_mode,) + + input_image_generator = keras.preprocessing.image.ImageDataGenerator(**data_gen_args) + groundtruth_mask_generator = keras.preprocessing.image.ImageDataGenerator(**data_gen_args) + + input_gen = input_image_generator.flow_from_directory( + self.input_dir, + color_mode=self.image_mode, + seed=self.seed, + class_mode=None, + batch_size=self.batch_size, + interpolation="nearest", + target_size=(self.image_height, self.image_width)) + + groundtruth_gen = groundtruth_mask_generator.flow_from_directory( + self.groundtruth_dir, + color_mode=self.mask_mode, + seed=self.seed, + class_mode=None, + batch_size=self.batch_size, + interpolation="nearest", + target_size=(self.image_height, self.image_width)) + + return zip(input_gen, groundtruth_gen) diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/datasets/test_groundtruth/.gitkeep b/recognition/ImprovedUNet-ISIC2018-45293915/datasets/test_groundtruth/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/datasets/test_input/.gitkeep b/recognition/ImprovedUNet-ISIC2018-45293915/datasets/test_input/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/datasets/training_groundtruth/.gitkeep b/recognition/ImprovedUNet-ISIC2018-45293915/datasets/training_groundtruth/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/datasets/training_input/.gitkeep b/recognition/ImprovedUNet-ISIC2018-45293915/datasets/training_input/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/datasets/validation_groundtruth/.gitkeep b/recognition/ImprovedUNet-ISIC2018-45293915/datasets/validation_groundtruth/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/datasets/validation_input/.gitkeep b/recognition/ImprovedUNet-ISIC2018-45293915/datasets/validation_input/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/models/.gitkeep b/recognition/ImprovedUNet-ISIC2018-45293915/models/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/modules.py b/recognition/ImprovedUNet-ISIC2018-45293915/modules.py new file mode 100644 index 000000000..554d3674a --- /dev/null +++ b/recognition/ImprovedUNet-ISIC2018-45293915/modules.py @@ -0,0 +1,163 @@ +import tensorflow as tf +from tensorflow import keras +from tensorflow.keras.regularizers import l2 +import tensorflow_addons as tfa + +# TensorFlow Addons (tfa) is used here because it provides support for Instance Normalization (IN) +# as opposed to Batch Normalization (BN). This choice aligns with the findings from the referenced paper +# (https://arxiv.org/abs/1802.10508v1), where IN is preferred over BN due to the potential destabilization +# of batch normalization caused by small batch sizes in the training process. + +# -------------------------------------------- +# GLOBAL CONSTANTS +# -------------------------------------------- + +LEAKY_RELU_ALPHA = 0.01 # Alpha value for LeakyReLU activation function +DROPOUT = 0.35 # Dropout rate +L2_WEIGHT_DECAY = 0.0005 # L2 weight decay +CONV_PROP = dict( + kernel_regularizer=l2(L2_WEIGHT_DECAY), + bias_regularizer=l2(L2_WEIGHT_DECAY), + padding="same") +IN_PROP = dict( + axis=3, + center=True, + scale=True, + beta_initializer="random_uniform", + gamma_initializer="random_uniform") + + +def improved_unet(width, height, channels): + # Improved UNet Model for ISIC Segmentation + # ------------------------------------------------- + + # Implementation based on the 'improved UNet' architecture from https://arxiv.org/abs/1802.10508v1. + # This 2D implementation is designed for image segmentation tasks, with instance normalization (IN) + # for improved stability, especially with small batch sizes. + + # Architecture Overview: + # + # [Input] -> [Encoder] -> [Decoder] -> [Output] + # + # Key Components: + # - Encoder: Down-sampling path with convolutional layers and context modules. + # - Decoder: Up-sampling path with localization and upsampling modules. + # - Context Module: Enhances feature extraction and learning by adding context. + # - Upsampling Module: Upscales feature maps to recover spatial information. + # - Localisation Module: Fine-tunes feature maps for better segmentation. + # + # Output Layer: + # - Sigmoid activation for binary segmentation. + # + # Detailed Architecture: + # [Input] -> [Conv] -> [IN] -> [LeakyReLU] -> [Context] -> [Add] -> [Conv] -> [IN] -> [LeakyReLU] -> ... + # ... -> [Upsampling] -> [Conv] -> [IN] -> [LeakyReLU] -> [Add] -> [Concatenate] -> [Localization] -> ... + # ... -> [Upsampling] -> [Conv] -> [IN] -> [LeakyReLU] -> [Add] -> [Concatenate] -> [Localization] -> ... + # ... -> [Upsampling] -> [Conv] -> [IN] -> [LeakyReLU] -> [Add] -> [Concatenate] -> [Localization] -> ... + # ... -> [Upsampling] -> [Conv] -> [IN] -> [LeakyReLU] -> [Add] -> [Concatenate] -> [Conv] -> [IN] -> ... + # ... [LeakyReLU] -> [Sigmoid] -> [Upsampling] -> [Sigmoid] -> [Add] -> [Upsampling] -> [Sigmoid] -> [Add] -> [Output] + + input_layer = keras.Input(shape=(width, height, channels)) + + # Encoder + # ------------------------------------------------- + + conv1 = keras.layers.Conv2D(16, (3, 3), input_shape=(width, height, channels), **CONV_PROP)(input_layer) + norm1 = tfa.layers.InstanceNormalization(**IN_PROP)(conv1) + relu1 = keras.layers.LeakyReLU(alpha=LEAKY_RELU_ALPHA)(norm1) + context1 = context_module(relu1, 16) + add1 = keras.layers.Add()([conv1, context1]) + + conv2 = keras.layers.Conv2D(32, (3, 3), strides=2, **CONV_PROP)(add1) + norm2 = tfa.layers.InstanceNormalization(**IN_PROP)(conv2) + relu2 = keras.layers.LeakyReLU(alpha=LEAKY_RELU_ALPHA)(norm2) + context2 = context_module(relu2, 32) + add2 = keras.layers.Add()([relu2, context2]) + + conv3 = keras.layers.Conv2D(64, (3, 3), strides=2, **CONV_PROP)(add2) + norm3 = tfa.layers.InstanceNormalization(**IN_PROP)(conv3) + relu3 = keras.layers.LeakyReLU(alpha=LEAKY_RELU_ALPHA)(norm3) + context3 = context_module(relu3, 64) + add3 = keras.layers.Add()([relu3, context3]) + + conv4 = keras.layers.Conv2D(128, (3, 3), strides=2, **CONV_PROP)(add3) + norm4 = tfa.layers.InstanceNormalization(**IN_PROP)(conv4) + relu4 = keras.layers.LeakyReLU(alpha=LEAKY_RELU_ALPHA)(norm4) + context4 = context_module(relu4, 128) + add4 = keras.layers.Add()([relu4, context4]) + + # Decoder + # ------------------------------------------------- + + conv5 = keras.layers.Conv2D(256, (3, 3), strides=2, **CONV_PROP)(add4) + norm5 = tfa.layers.InstanceNormalization(**IN_PROP)(conv5) + relu5 = keras.layers.LeakyReLU(alpha=LEAKY_RELU_ALPHA)(norm5) + context5 = context_module(relu5, 256) + add5 = keras.layers.Add()([relu5, context5]) + upsample1 = upsampling_module(add5, 128) + + concat1 = keras.layers.Concatenate()([add4, upsample1]) + localization1 = localisation_module(concat1, 128) + upsample2 = upsampling_module(localization1, 64) + + concat2 = keras.layers.Concatenate()([add3, upsample2]) + localization2 = localisation_module(concat2, 64) + upsample3 = upsampling_module(localization2, 32) + + concat3 = keras.layers.Concatenate()([add2, upsample3]) + localization3 = localisation_module(concat3, 32) + upsample4 = upsampling_module(localization3, 16) + + concat4 = keras.layers.Concatenate()([add1, upsample4]) + conv6 = keras.layers.Conv2D(32, (3, 3), **CONV_PROP)(concat4) + norm6 = tfa.layers.InstanceNormalization(**IN_PROP)(conv6) + relu6 = keras.layers.LeakyReLU(alpha=LEAKY_RELU_ALPHA)(norm6) + + seg_layer1 = keras.layers.Activation('sigmoid')(localization2) + upsample5 = upsampling_module(seg_layer1, 32) + seg_layer2 = keras.layers.Activation('sigmoid')(localization3) + sum1 = keras.layers.Add()([upsample5, seg_layer2]) + upsample6 = upsampling_module(sum1, 32) + seg_layer3 = keras.layers.Activation('sigmoid')(relu6) + sum2 = keras.layers.Add()([upsample6, seg_layer3]) + + # Output + # ------------------------------------------------- + + output_layer = keras.layers.Conv2D(1, (1, 1), activation="sigmoid", **CONV_PROP)(sum2) + u_net = keras.Model(inputs=[input_layer], outputs=[output_layer]) + return u_net + + + +# MODULES +# ------------------------------------------------- + +# Context Module based inspired by the Improved UNet paper. +def context_module(input_layer, out_filter): + conv1 = keras.layers.Conv2D(out_filter, (3, 3), **CONV_PROP)(input_layer) + norm1 = tfa.layers.InstanceNormalization(**IN_PROP)(conv1) + relu1 = keras.layers.LeakyReLU(alpha=LEAKY_RELU_ALPHA)(norm1) + dropout1 = keras.layers.Dropout(DROPOUT)(relu1) + conv2 = keras.layers.Conv2D(out_filter, (3, 3), **CONV_PROP)(dropout1) + norm2 = tfa.layers.InstanceNormalization(**IN_PROP)(conv2) + relu2 = keras.layers.LeakyReLU(alpha=LEAKY_RELU_ALPHA)(norm2) + return relu2 + +# Upsampling Module based inspired by the Improved UNet paper. +def upsampling_module(input_layer, out_filter): + upsampled = keras.layers.UpSampling2D(size=(2, 2))(input_layer) + conv = keras.layers.Conv2D(out_filter, (3, 3), **CONV_PROP)(upsampled) + norm = tfa.layers.InstanceNormalization(**IN_PROP)(conv) + relu = keras.layers.LeakyReLU(alpha=LEAKY_RELU_ALPHA)(norm) + return relu + +# Localisation Module inspired by the Improved UNet paper. +def localisation_module(input_layer, out_filter): + conv1 = keras.layers.Conv2D(out_filter, (3, 3), **CONV_PROP)(input_layer) + norm1 = tfa.layers.InstanceNormalization(**IN_PROP)(conv1) + relu1 = keras.layers.LeakyReLU(alpha=LEAKY_RELU_ALPHA)(norm1) + conv2 = keras.layers.Conv2D(out_filter, (1, 1), **CONV_PROP)(relu1) + norm2 = tfa.layers.InstanceNormalization(**IN_PROP)(conv2) + relu2 = keras.layers.LeakyReLU(alpha=LEAKY_RELU_ALPHA)(norm2) + return relu2 \ No newline at end of file diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/output/.gitkeep b/recognition/ImprovedUNet-ISIC2018-45293915/output/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/predict.py b/recognition/ImprovedUNet-ISIC2018-45293915/predict.py new file mode 100644 index 000000000..52c08f5c8 --- /dev/null +++ b/recognition/ImprovedUNet-ISIC2018-45293915/predict.py @@ -0,0 +1,121 @@ +import numpy as np +import matplotlib.pyplot as plt +from tensorflow import keras +import os +import math +import time +from itertools import islice + +from utils import dice_coefficient +from dataset import DataLoader + +BATCH_SIZE = 2 # set the batch_size +STEPS_PER_EPOCH_TEST = math.floor(1000 / BATCH_SIZE) + + +def save_prediction(original, probabilities, truth, dice_coeff, filename): + # Create a subplot for the visualization + figure, axes = plt.subplots(1, 3) + + # Plot and save the input image + axes[0].title.set_text('Input') + axes[0].imshow(original, vmin=0.0, vmax=1.0) + axes[0].set_axis_off() + + # Plot and save the model's output + axes[1].title.set_text('Dice Coeff: ' + str(dice_coeff.numpy())) + axes[1].imshow(probabilities, cmap='gray', vmin=0.0, vmax=1.0) + axes[1].set_axis_off() + + # Plot and save the ground truth + axes[2].title.set_text('Ground Truth') + axes[2].imshow(truth, cmap='gray', vmin=0.0, vmax=1.0) + axes[2].set_axis_off() + + # Save the visualisation to the output folder + plt.axis('off') + plt.savefig(filename, bbox_inches='tight', pad_inches=0.1) + plt.close() + + +# Evaluate and visualise model predictions on the validation set. +def test_and_visualise_predictions(model, test_data, output_dir, timestr, number_of_predictions): + + test_loss, test_accuracy, test_dice = \ + model.evaluate(test_data, steps=STEPS_PER_EPOCH_TEST, verbose=2, use_multiprocessing=False) + print("Test Accuracy: " + str(test_accuracy)) + print("Test Loss: " + str(test_loss)) + print("Test Dice Coefficient: " + str(test_dice) + "\n") + + test_range = np.arange(0, stop=number_of_predictions, step=1) + + for i in test_range: + current = next(islice(test_data, i, None)) + image_input = current[0] # Image tensor + mask_truth = current[1] # Mask tensor + # debug statements to check the types of the tensors and find the division by zero + test_pred = model.predict(image_input, steps=1, use_multiprocessing=False)[0] + truth = mask_truth[0] + original = image_input[0] + probabilities = keras.preprocessing.image.img_to_array(test_pred) + test_dice = dice_coefficient(truth, test_pred, axis=None) + + + # Create a unique filename for each visualization + filename = os.path.join(output_dir, f"test_visualisation_{i}_{timestr}.png") + + # Create a subplot for the visualization + figure, axes = plt.subplots(1, 3) + + # Plot and save the input image + axes[0].title.set_text('Input') + axes[0].imshow(original, vmin=0.0, vmax=1.0) + axes[0].set_axis_off() + + # Plot and save the model's output + axes[1].title.set_text('Output (DSC: ' + str(test_dice.numpy()) + ")") + axes[1].imshow(probabilities, cmap='gray', vmin=0.0, vmax=1.0) + axes[1].set_axis_off() + + # Plot and save the ground truth + axes[2].title.set_text('Ground Truth') + axes[2].imshow(truth, cmap='gray', vmin=0.0, vmax=1.0) + axes[2].set_axis_off() + + # Save the visualization to the output folder + plt.axis('off') + plt.savefig(filename, bbox_inches='tight', pad_inches=0.1) + plt.close() + + print("Visualisations of test output saved in the 'output' folder.") + +if __name__ == "__main__": + # Constants related to preprocessing + test_dir = "datasets/test_input" + test_groundtruth_dir = "datasets/test_groundtruth" + image_mode = "rgb" + mask_mode = "grayscale" + image_height = 512 + image_width = 512 + batch_size = 2 + seed = 45 + shear_range = 0.1 + zoom_range = 0.1 + horizontal_flip = True + vertical_flip = True + fill_mode = 'nearest' + number_of_predictions = 3 + + print("\nPREPROCESSING IMAGES") + # print number of images in each directory + test_data = DataLoader( + test_dir, test_groundtruth_dir, image_mode, mask_mode, image_height, image_width, batch_size, seed, + shear_range, zoom_range, horizontal_flip, vertical_flip, fill_mode) + test_data = test_data.create_data_generators() + + + model_name = "models/my_model.keras" + output_dir = "output" + timestr = time.strftime("%Y%m%d-%H%M%S") + print("\nTESTING MODEL") + test_and_visualise_predictions(model_name, test_data, output_dir, timestr, number_of_predictions) \ No newline at end of file diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/requirements.txt b/recognition/ImprovedUNet-ISIC2018-45293915/requirements.txt new file mode 100644 index 000000000..c531b1f3d --- /dev/null +++ b/recognition/ImprovedUNet-ISIC2018-45293915/requirements.txt @@ -0,0 +1,5 @@ +# requirements.txt +tensorflow[and-cuda] +matplotlib +numpy + diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/train.py b/recognition/ImprovedUNet-ISIC2018-45293915/train.py new file mode 100644 index 000000000..d891766c2 --- /dev/null +++ b/recognition/ImprovedUNet-ISIC2018-45293915/train.py @@ -0,0 +1,114 @@ +import math +import time + +from tensorflow import keras + +import modules as layers +from dataset import DataLoader +from utils import ( + dice_coefficient, + dice_loss, + DiceCoefficientCallback, + plot_accuracy_loss, + save_dice_coefficient_plot +) +from validation import validate_and_visualise_predictions +from predict import test_and_visualise_predictions + +# Constants +EPOCHS = 5 +LEARNING_RATE = 0.0005 +BATCH_SIZE = 2 +IMAGE_HEIGHT = 512 +IMAGE_WIDTH = 512 +CHANNELS = 3 +STEPS_PER_EPOCH_TRAIN = math.floor(2594 / BATCH_SIZE) +STEPS_PER_EPOCH_TEST = math.floor(100 / BATCH_SIZE) + +# String modifier for saving output files based on time +timestr = time.strftime("%Y%m%d-%H%M%S") +output_dir = "output" + + +def train_model_check_accuracy(training_data, validation_data): + model = layers.improved_unet(IMAGE_WIDTH, IMAGE_HEIGHT, CHANNELS) + model.summary() + + dice_coefficient_callback = DiceCoefficientCallback(validation_data, STEPS_PER_EPOCH_TEST) + model.compile(optimizer=keras.optimizers.Adam(LEARNING_RATE), + loss=dice_loss, + metrics=['accuracy', dice_coefficient]) + + track = model.fit( + training_data, + steps_per_epoch=STEPS_PER_EPOCH_TRAIN, + epochs=EPOCHS, + shuffle=True, + verbose=1, + use_multiprocessing=False, + callbacks=[dice_coefficient_callback] + ) + + plot_accuracy_loss(track, output_dir, timestr) + + print("\nEvaluating validation images...") + validation_loss, validation_accuracy, validation_dice = \ + model.evaluate(validation_data, steps=STEPS_PER_EPOCH_TEST, verbose=2, use_multiprocessing=False) + + print(f"Validation Accuracy: {validation_accuracy}") + print(f"Validation Loss: {validation_loss}") + print(f"Validation DSC: {validation_dice}\n") + + return model, track.history['dice_coefficient'] + + +def main(): + preprocessing_params = { + "image_mode": "rgb", + "mask_mode": "grayscale", + "image_height": IMAGE_HEIGHT, + "image_width": IMAGE_WIDTH, + "batch_size": BATCH_SIZE, + "seed": 45, + "shear_range": 0.1, + "zoom_range": 0.1, + "horizontal_flip": True, + "vertical_flip": True, + "fill_mode": 'nearest' + } + + print("\nPREPROCESSING IMAGES") + validation_data = DataLoader( + "datasets/validation_input", + "datasets/validation_groundtruth", + **preprocessing_params + ).create_data_generators() + + train_data = DataLoader( + "datasets/training_input", + "datasets/training_groundtruth", + **preprocessing_params + ).create_data_generators() + + print("\nTRAINING MODEL") + model, dice_history = train_model_check_accuracy(train_data, validation_data) + save_dice_coefficient_plot(dice_history, output_dir, timestr) + + print("\nSAVING MODEL") + keras.saving.save_model(model, f"models/my_model_{timestr}.keras", overwrite=True) + + test_data = DataLoader( + "datasets/test_input", + "datasets/test_groundtruth", + **preprocessing_params + ).create_data_generators() + + number_of_predictions = 3 + test_and_visualise_predictions(model, test_data, output_dir, timestr, number_of_predictions) + + print("COMPLETED") + return 0 + + +if __name__ == "__main__": + main() diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/utils.py b/recognition/ImprovedUNet-ISIC2018-45293915/utils.py new file mode 100644 index 000000000..cf4c25a07 --- /dev/null +++ b/recognition/ImprovedUNet-ISIC2018-45293915/utils.py @@ -0,0 +1,64 @@ +from keras.callbacks import Callback +import tensorflow as tf +import matplotlib.pyplot as plt +import os + +# Metric for how similar two sets (prediction vs truth) are. +# Implementation based off https://en.wikipedia.org/wiki/S%C3%B8rensen%E2%80%93Dice_coefficient +# DSC = (2|X & Y|) / (|X| + |Y|) -> 'soft' dice coefficient. +def dice_coefficient(truth, pred, eps=1e-7, axis=(1, 2, 3)): + numerator = (2.0 * (tf.reduce_sum(pred * truth, axis=axis))) + eps + denominator = tf.reduce_sum(pred, axis=axis) + tf.reduce_sum(truth, axis=axis) + eps + dice = tf.reduce_mean(numerator / denominator) + return dice + + +# Loss function - DSC distance. +def dice_loss(truth, pred): + return 1.0 - dice_coefficient(truth, pred) + + +# Define a callback to calculate Dice coefficient after each epoch +class DiceCoefficientCallback(Callback): + def __init__(self, test_gen, steps_per_epoch_test): + self.test_gen = test_gen + self.steps_per_epoch_test = steps_per_epoch_test + self.dice_coefficients = [] + + def on_epoch_end(self, epoch, logs=None): + test_loss, test_accuracy, test_dice = \ + self.model.evaluate(self.test_gen, steps=self.steps_per_epoch_test, verbose=0, use_multiprocessing=False) + self.dice_coefficients.append(test_dice) + print(f"Epoch {epoch + 1} - Test Dice Coefficient: {test_dice:.4f}") + + +# Plot the accuracy and loss curves of model training. +def plot_accuracy_loss(track, output_dir, timestr): + plt.figure(0) + plt.plot(track.history['accuracy']) + plt.plot(track.history['loss']) + plt.title('Loss & Accuracy Curves') + plt.xlabel('Epoch') + plt.legend(['Accuracy', 'Loss']) + + # Generate a unique filename based on the current date and tim + filename = os.path.join(output_dir, f"accuracy_loss_plot_{timestr}.png") + + # Save the plot to the output folder + plt.savefig(filename, bbox_inches='tight', pad_inches=0.1) + plt.close() + + print(f"Accuracy and loss plot saved as '{filename}'.") + + +# Plot the dice coefficient curve and save it as an image +def save_dice_coefficient_plot(dice_history, output_dir, timestr): + filename = os.path.join(output_dir, f"dice_coefficient_plot_{timestr}.png") + plt.figure(1) + plt.plot(dice_history) + plt.title('Dice Coefficient Curve') + plt.xlabel('Epoch') + plt.ylabel('Dice Coefficient') + plt.savefig(filename) # Save the plot as an image + plt.close() # Close the figure to release resources + print("Dice Coefficient saved as " + filename + ".") \ No newline at end of file diff --git a/recognition/ImprovedUNet-ISIC2018-45293915/validation.py b/recognition/ImprovedUNet-ISIC2018-45293915/validation.py new file mode 100644 index 000000000..b8918b047 --- /dev/null +++ b/recognition/ImprovedUNet-ISIC2018-45293915/validation.py @@ -0,0 +1,65 @@ +import numpy as np +import matplotlib.pyplot as plt +from tensorflow import keras +import os + +from utils import dice_coefficient + + +def save_prediction(original, probabilities, truth, dice_coeff, filename): + # Create a subplot for the visualization + figure, axes = plt.subplots(1, 3) + + # Plot and save the input image + axes[0].title.set_text('Input') + axes[0].imshow(original, vmin=0.0, vmax=1.0) + axes[0].set_axis_off() + + # Plot and save the model's output + axes[1].title.set_text('Dice Coeff: ' + str(dice_coeff.numpy())) + axes[1].imshow(probabilities, cmap='gray', vmin=0.0, vmax=1.0) + axes[1].set_axis_off() + + # Plot and save the ground truth + axes[2].title.set_text('Ground Truth') + axes[2].imshow(truth, cmap='gray', vmin=0.0, vmax=1.0) + axes[2].set_axis_off() + + # Save the visualisation to the output folder + plt.axis('off') + plt.savefig(filename, bbox_inches='tight', pad_inches=0.1) + plt.close() + + +# Evaluate and visualise model predictions on the validation set. +def validate_and_visualise_predictions(model, validation_data, output_dir, timestr, number_of_predictions): + # Initialise variables to calculate statistics + total_dice_coefficient = 0.0 + total_samples = 0 + + for i, (image_input, mask_truth) in enumerate(validation_data): + # Predict using the model + predictions = model.predict(image_input, steps=1, use_multiprocessing=False) + prediction = predictions[0] + truth = mask_truth[0] + original = image_input[0] + probabilities = keras.preprocessing.image.img_to_array(prediction) + dice_coeff = dice_coefficient(truth, prediction, axis=None) + + # Create a unique filename for each visualisation + filename = os.path.join(output_dir, f"visualisation_{i}_{timestr}.png") + + # Save the visualisation + if i < number_of_predictions: + save_prediction(original, probabilities, truth, dice_coeff, filename) + + # Accumulate statistics + total_dice_coefficient += dice_coeff + total_samples += 1 + + + # Calculate and print average Dice coefficient for the entire validation set + average_dice_coefficient = total_dice_coefficient / total_samples + print("Average Dice Coefficient on Validation Set:", average_dice_coefficient) + + print("Visualisations saved in the 'output' folder.") diff --git a/recognition/README.md b/recognition/README.md index 5c646231c..0f840e069 100644 --- a/recognition/README.md +++ b/recognition/README.md @@ -1,10 +1,10 @@ -# Recognition Tasks -Various recognition tasks solved in deep learning frameworks. - -Tasks may include: -* Image Segmentation -* Object detection -* Graph node classification -* Image super resolution -* Disease classification -* Generative modelling with StyleGAN and Stable Diffusion +# Recognition Tasks +Various recognition tasks solved in deep learning frameworks. + +Tasks may include: +* Image Segmentation +* Object detection +* Graph node classification +* Image super resolution +* Disease classification +* Generative modelling with StyleGAN and Stable Diffusion