From 96cf3b0c23e4f71da0afb545f6bc46ba64c2f04e Mon Sep 17 00:00:00 2001 From: Tbs_Fchnr Date: Wed, 25 Nov 2020 00:28:45 +0100 Subject: [PATCH] Final results generated in notebooks. Small changes to code. --- TF00-initial-exploration.ipynb | 49 +-- TF02-histogram-exploration.ipynb | 65 +--- ...histogram-exploration-sliding-window.ipynb | 221 +++++++++++++ ...al-filters-results-generation-foetus.ipynb | 298 ++++++++++++++++++ ...al-filters-results-generation-nzjers.ipynb | 189 +++++++++++ ...histogram-filters-results-generation.ipynb | 212 +++++++++++++ TF06-edge-detection.ipynb | 217 +++++++++++++ edge_detector.py | 0 filters.py | 118 +++---- handler.py | 69 ++-- third_party_filters.py | 9 +- 11 files changed, 1273 insertions(+), 174 deletions(-) create mode 100644 TF03-histogram-exploration-sliding-window.ipynb create mode 100644 TF04-spatial-filters-results-generation-foetus.ipynb create mode 100644 TF04-spatial-filters-results-generation-nzjers.ipynb create mode 100644 TF05-histogram-filters-results-generation.ipynb create mode 100644 TF06-edge-detection.ipynb delete mode 100644 edge_detector.py diff --git a/TF00-initial-exploration.ipynb b/TF00-initial-exploration.ipynb index ac24fa2..2813dea 100644 --- a/TF00-initial-exploration.ipynb +++ b/TF00-initial-exploration.ipynb @@ -62,26 +62,10 @@ "metadata": {}, "outputs": [], "source": [ - "meanie = filters.Mean(maskSize=5)\n", - "print(meanie)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "imgFiltered = meanie.convolve(img)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "handler.plotFigs([img, imgFiltered])" + "for maskSize in [3,5,7,9,11,21,51]:\n", + " meanie = filters.Mean(maskSize=maskSize)\n", + " imgFiltered = meanie.convolve(img)\n", + " handler.plotFigs([img, imgFiltered], meanie)" ] }, { @@ -338,25 +322,12 @@ "metadata": {}, "outputs": [], "source": [ - "trimmedMean = filters.TrimmedMean(maskSize=5, trimStart=4, trimEnd=4)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "imgFiltered = trimmedMean.convolve(img)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "handler.plotFigs([img, imgFiltered])" + "for maskSize in [3,5,7,9,11,21,51]:\n", + " for trim in [maskSize, maskSize*2, maskSize*3]:\n", + " trimmedMean = filters.TrimmedMean(maskSize=5, trimStart=4, trimEnd=4)\n", + " imgFiltered = trimmedMean.convolve(img)\n", + " filteredTitle = trimmedMean.name + \"_maskSize\" + str(trimmedMean.maskSize) + \"_trim\" + str(trim)\n", + " handler.plotFigs([img, imgFiltered], trimmedMean, filteredTitle)" ] }, { diff --git a/TF02-histogram-exploration.ipynb b/TF02-histogram-exploration.ipynb index fdc9ca6..f9b7959 100644 --- a/TF02-histogram-exploration.ipynb +++ b/TF02-histogram-exploration.ipynb @@ -65,7 +65,7 @@ "metadata": {}, "outputs": [], "source": [ - "filtr = filters.Equalise()" + "eq = filters.Equalise()" ] }, { @@ -74,7 +74,7 @@ "metadata": {}, "outputs": [], "source": [ - "imgNew = filtr.filter(img)" + "imgNew = eq.filter(img)" ] }, { @@ -83,7 +83,7 @@ "metadata": {}, "outputs": [], "source": [ - "handler.plotFigs([img, imgNew])" + "handler.plotFigs([img, imgNew], eq, filteredTitle=eq.name)" ] }, { @@ -99,7 +99,7 @@ "metadata": {}, "outputs": [], "source": [ - "filtr = filters.AHE(maskSize=126)" + "ahe = filters.AHE(maskSize=21)" ] }, { @@ -108,7 +108,7 @@ "metadata": {}, "outputs": [], "source": [ - "imgNew = filtr.filter(img)" + "imgNew = ahe.filter(img)" ] }, { @@ -117,60 +117,7 @@ "metadata": {}, "outputs": [], "source": [ - "handler.plotFigs([img, imgNew])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Save Results" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pathlib import Path\n", - "import os.path\n", - "from PIL import Image" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "currentDir = Path().absolute()\n", - "root = str(currentDir) + '\\\\..\\outputs\\\\hist_Adaptive_Equalise\\\\'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "if not os.path.exists(root):\n", - " os.makedirs(root)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Create pillow image object from filtered image array\n", - "img_PIL = Image.fromarray(imgNew, 'L')\n", - "\n", - "# Save filtered image from pillow image object\n", - "filePath = root+'filtered_foetus_maskSize_126.png'\n", - "img_PIL.save(filePath, 'PNG')\n", - "print(\"Saved filtered image to... \\n{}\\n\\n\".format(filePath))" + "handler.plotFigs([img, imgNew], ahe)" ] }, { diff --git a/TF03-histogram-exploration-sliding-window.ipynb b/TF03-histogram-exploration-sliding-window.ipynb new file mode 100644 index 0000000..15e069b --- /dev/null +++ b/TF03-histogram-exploration-sliding-window.ipynb @@ -0,0 +1,221 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Histogram Exploration" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import filters\n", + "import handler" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np # for debugging purposes\n", + "import matplotlib.pyplot as plt\n", + "from sklearn.preprocessing import normalize\n", + "from math import ceil" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load Image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "img, _ = handler.getImageAsArray(handler.FOETUS_PATH_ORIGINAL)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Create and Apply SWAHE Filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "swahe = filters.SWAHE(maskSize = 127)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "imgNew = swahe.filter(img)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "handler.plotFigs([img, imgNew], swahe)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "img.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Save Results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "import os.path\n", + "from PIL import Image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "currentDir = Path().absolute()\n", + "root = str(currentDir) + '\\\\..\\outputs\\\\hist_Adaptive_Equalise\\\\'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "if not os.path.exists(root):\n", + " os.makedirs(root)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create pillow image object from filtered image array\n", + "img_PIL = Image.fromarray(imgNew[0], 'L')\n", + "\n", + "# Save filtered image from pillow image object\n", + "filePath = root+'filtered_foetus_maskSize_52.png'\n", + "img_PIL.save(filePath, 'PNG')\n", + "print(\"Saved filtered image to... \\n{}\\n\\n\".format(filePath))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 3rd Part Check" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use CV2 library built-in equalise and CLAHe functions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import third_party_filters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "third_party_filters.equalise(handler.FOETUS_PATH_ORIGINAL)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "third_party_filters.CLAHE(handler.FOETUS_PATH_ORIGINAL)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.1" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/TF04-spatial-filters-results-generation-foetus.ipynb b/TF04-spatial-filters-results-generation-foetus.ipynb new file mode 100644 index 0000000..e7a9fff --- /dev/null +++ b/TF04-spatial-filters-results-generation-foetus.ipynb @@ -0,0 +1,298 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Initial Script" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import handler\n", + "import filters\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Load Image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "img, _ = handler.getImageAsArray(handler.FOETUS_PATH_ORIGINAL)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Mean" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for maskSize in [3,5,7,9,11,21,51]:\n", + " meanie = filters.Mean(maskSize=maskSize)\n", + " imgFiltered = meanie.convolve(img)\n", + " handler.plotFigs([img, imgFiltered], meanie)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Gaussian" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for sig in [0.5,1,2,3,4,5,8]:\n", + " gaus = filters.Gaussian(sig=sig)\n", + " imgFiltered = gaus.convolve(img)\n", + " filteredTitle = gaus.name + \"_maskSize\" + str(gaus.maskSize) + \"_sig\" + str(sig)\n", + " handler.plotFigs([img, imgFiltered], gaus, filteredTitle)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "imgFiltered = gaus.convolve(img)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "handler.plotFigs([img, imgFiltered])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Median" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for maskSize in [3,5,7,9,11,21,51]:\n", + " med = filters.Median(maskSize=maskSize)\n", + " imgFiltered = med.convolve(img)\n", + " handler.plotFigs([img, imgFiltered], med)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## High Pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "highP = filters.Sharpening(maskSize=21)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "imgFiltered = highP.convolve(img)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "handler.plotFigs([img, imgFiltered], highP)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Low Pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "lowP = filters.LowPass(maskSize=7)\n", + "print(lowP)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "imgFiltered = lowP.convolve(img)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "handler.plotFigs([img, imgFiltered], lowP)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "handler.saveAll(imgFiltered, lowP)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Adaptive Weighted Median" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for maskSize in [3,5,7,11,21,51]:\n", + " for constant in [10, 50,150]:\n", + " for centralWeight in [50, 100]:\n", + " awMed = filters.AdaptiveWeightedMedian(maskSize=maskSize, constant=constant, centralWeight=centralWeight)\n", + " imgFiltered = awMed.convolve(img)\n", + " print(\"maskSize = {}\\nconstant = {}\\ncentral weight = {}\\n\".format(maskSize, constant, centralWeight))\n", + " handler.plotFigs([img, imgFiltered], awMed)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Trimmed Mean" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for maskSize in [3,5,7,9,11,21,51]:\n", + " for trim in [maskSize, maskSize*2, maskSize*3]:\n", + " if trim > maskSize**2/2+1:\n", + " continue\n", + " else:\n", + " trimmedMean = filters.TrimmedMean(maskSize=maskSize, trimStart=trim, trimEnd=trim)\n", + " imgFiltered = trimmedMean.convolve(img)\n", + " filteredTitle = trimmedMean.name + \"_maskSize\" + str(trimmedMean.maskSize) + \"_trim\" + str(trim)\n", + " handler.plotFigs([img, imgFiltered], trimmedMean, filteredTitle)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "trimmedMean = filters.TrimmedMean(maskSize=9, trimStart=1, trimEnd=1)\n", + "imgFiltered = trimmedMean.convolve(img)\n", + "filteredTitle = trimmedMean.name + \"_maskSize\" + str(trimmedMean.maskSize) + \"_trim\" + str(1)\n", + "handler.plotFigs([img, imgFiltered], trimmedMean, filteredTitle)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.1" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/TF04-spatial-filters-results-generation-nzjers.ipynb b/TF04-spatial-filters-results-generation-nzjers.ipynb new file mode 100644 index 0000000..f018b7a --- /dev/null +++ b/TF04-spatial-filters-results-generation-nzjers.ipynb @@ -0,0 +1,189 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Initial Script" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import handler\n", + "import filters\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Load Image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "img, _ = handler.getImageAsArray(handler.NZJERS_PATH_ORIGINAL)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Mean" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for maskSize in [3,5,7,9,11,21,51]:\n", + " meanie = filters.Mean(maskSize=maskSize)\n", + " imgFiltered = meanie.convolve(img)\n", + " handler.plotFigs([img, imgFiltered], meanie)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Gaussian" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for sig in [0.5,1,2,3,4,5,8]:\n", + " gaus = filters.Gaussian(sig=sig)\n", + " imgFiltered = gaus.convolve(img)\n", + " filteredTitle = gaus.name + \"_maskSize\" + str(gaus.maskSize) + \"_sig\" + str(sig)\n", + " handler.plotFigs([img, imgFiltered], gaus, filteredTitle)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Median" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for maskSize in [3,5,7,9,11,21,51]:\n", + " med = filters.Median(maskSize=maskSize)\n", + " imgFiltered = med.convolve(img)\n", + " handler.plotFigs([img, imgFiltered], med)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## High Pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for maskSize in [3,5,7,9,11,21,51]:\n", + " sharp = filters.Sharpening(maskSize=maskSize)\n", + " imgFiltered = sharp.convolve(img)\n", + " handler.plotFigs([img, imgFiltered], sharp)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Adaptive Weighted Median" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for maskSize in [3,5,7,11,21,51]:\n", + " for constant in [10, 50,150]:\n", + " for centralWeight in [50, 100]:\n", + " awMed = filters.AdaptiveWeightedMedian(maskSize=maskSize, constant=constant, centralWeight=centralWeight)\n", + " imgFiltered = awMed.convolve(img)\n", + " print(\"maskSize = {}\\nconstant = {}\\ncentral weight = {}\\n\".format(maskSize, constant, centralWeight))\n", + " handler.plotFigs([img, imgFiltered], awMed)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Trimmed Mean" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for maskSize in [3,5,7,9,11,21,51]:\n", + " for trim in [maskSize, maskSize*2, maskSize*3]:\n", + " if trim > maskSize**2/2+1:\n", + " continue\n", + " else:\n", + " trimmedMean = filters.TrimmedMean(maskSize=maskSize, trimStart=trim, trimEnd=trim)\n", + " imgFiltered = trimmedMean.convolve(img)\n", + " filteredTitle = trimmedMean.name + \"_maskSize\" + str(trimmedMean.maskSize) + \"_trim\" + str(trim)\n", + " handler.plotFigs([img, imgFiltered], trimmedMean, filteredTitle)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.1" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/TF05-histogram-filters-results-generation.ipynb b/TF05-histogram-filters-results-generation.ipynb new file mode 100644 index 0000000..ef84a78 --- /dev/null +++ b/TF05-histogram-filters-results-generation.ipynb @@ -0,0 +1,212 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Histogram Results Generation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import filters\n", + "import handler" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np # for debugging purposes\n", + "import matplotlib.pyplot as plt\n", + "from sklearn.preprocessing import normalize\n", + "from math import ceil" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load Image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "img, _ = handler.getImageAsArray(handler.NZJERS_PATH_ORIGINAL)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Adaptive Histogram Equalise Filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ahe = filters.AHE(maskSize=53)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "imgNew = ahe.filter(img)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "handler.plotFigs([img, imgNew], ahe)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### SWAHE Filter - FOETUS" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "swahe = filters.SWAHE(maskSize = 201)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "imgNew = swahe.filter(img)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "handler.plotFigs([img, imgNew], swahe)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### SWAHE Filter - NZJERS" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for maskSize in [9, 21, 51, 127]:\n", + " swahe = filters.SWAHE(maskSize=maskSize)\n", + " imgNew = swahe.filter(img)\n", + " handler.plotFigs([img, imgNew], swahe)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 3rd Part Check" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use CV2 library built-in equalise and CLAHe functions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import third_party_filters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "third_party_filters.equalise(handler.FOETUS_PATH_ORIGINAL)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "third_party_filters.CLAHE(handler.FOETUS_PATH_ORIGINAL)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.1" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/TF06-edge-detection.ipynb b/TF06-edge-detection.ipynb new file mode 100644 index 0000000..d896bae --- /dev/null +++ b/TF06-edge-detection.ipynb @@ -0,0 +1,217 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Edge Detection" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import filters\n", + "import handler\n", + "import third_party_filters" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load Image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "FOETUS, _ = handler.getImageAsArray(handler.FOETUS_PATH_ORIGINAL)\n", + "NZJERS, _ = handler.getImageAsArray(handler.NZJERS_PATH_ORIGINAL)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NZJERS Trimmed Mean" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "maskSize: 5; trimEnds: 10" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tMed = filters.TrimmedMean(maskSize=5, trimStart=10, trimEnd=10)\n", + "imgMedFiltered = tMed.convolve(NZJERS)\n", + "edge = third_party_filters.edgeDetect(imgMedFiltered)\n", + "handler.plotFigs([NZJERS, imgMedFiltered, edge], tMed)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NZJERS Gaussian" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "sig: 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gaus = filters.Gaussian(sig=1)\n", + "imgGausFiltered = gaus.convolve(NZJERS)\n", + "edge = third_party_filters.edgeDetect(imgGausFiltered)\n", + "handler.plotFigs([NZJERS, imgGausFiltered, edge], gaus)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Foetus Adaptive Weighted Median" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "maskSize: 11; \n", + "constant: 10; \n", + "centralWeight: 50; " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "awmF = filters.AdaptiveWeightedMedian(maskSize=11, constant=10, centralWeight=50)\n", + "foetAWMFiltered = awmF.convolve(FOETUS)\n", + "edge = third_party_filters.edgeDetect(foetAWMFiltered)\n", + "handler.plotFigs([FOETUS, foetAWMFiltered, edge], awmF)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NZJERS Adaptive Weighted Median" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "maskSize: 5; \n", + "constant: 50; \n", + "centralWeight: 50; " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "awmN = filters.AdaptiveWeightedMedian(maskSize=5, constant=50, centralWeight=50)\n", + "nzjAWMFiltered = awmN.convolve(NZJERS)\n", + "edge = third_party_filters.edgeDetect(nzjAWMFiltered)\n", + "handler.plotFigs([NZJERS, nzjAWMFiltered, edge], awmN)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Foetus SWAHE" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "swahe = filters.SWAHE(maskSize=201)\n", + "imgSWAHEFiltered = swahe.filter(FOETUS)\n", + "edge = third_party_filters.edgeDetect(imgSWAHEFiltered)\n", + "handler.plotFigs([FOETUS, imgSWAHEFiltered, edge], swahe)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "highPF = filters.Sharpening(maskSize=27)\n", + "highPN = filters.Sharpening(maskSize=11)\n", + "foetHighPFiltered = highPF.convolve(FOETUS)\n", + "nzjersHighPFiltered = highPN.convolve(NZJERS)\n", + "edgeF = third_party_filters.edgeDetect(foetHighPFiltered)\n", + "edgeN = third_party_filters.edgeDetect(nzjersHighPFiltered)\n", + "handler.plotFigs([FOETUS, foetHighPFiltered, edgeF], highPF)\n", + "handler.plotFigs([NZJERS, nzjersHighPFiltered, edgeN], highPN)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.1" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/edge_detector.py b/edge_detector.py deleted file mode 100644 index e69de29..0000000 diff --git a/filters.py b/filters.py index d6f0f40..c69ced5 100644 --- a/filters.py +++ b/filters.py @@ -35,7 +35,7 @@ def padImage(img, maskSize): # Insert image pixel values into padded array imgPadded[pad:-pad, pad:-pad] = img - print("Padding of {} pixels created.".format(pad)) + logging.info("Padding of {} pixels created.".format(pad)) return imgPadded @@ -93,7 +93,7 @@ def assertTypes(maskSize, kernel): assert isinstance(kernel, np.ndarray) # kernel should be n-dimensional numpy array @abstractmethod - def computePixel(self, sub): + def compute(self, sub): pass def convolve(self, img, padding=True): @@ -120,7 +120,7 @@ def convolve(self, img, padding=True): for row in range(img.shape[0]): # Create sub matrix of mask size surrounding pixel under consideration sub = imgPadded[row: row+self.maskSize, col: col+self.maskSize] - output[row, col] = self.computePixel(sub) + output[row, col] = self.compute(sub) return output @@ -291,31 +291,6 @@ def plotHistograms(histogram, histogramNew, cs, csNew): plt.legend() plt.show() - @staticmethod - def interpolate(subBin, LU, RU, LB, RB, subX, subY): - """ - - :param subBin: - :param LU: - :param RU: - :param LB: - :param RB: - :param subX: - :param subY: - :return: - """ - subImage = np.zeros(subBin.shape) - num = subX * subY - for i in range(subX): - inverseI = subX - i - for j in range(subY): - inverseJ = subY - j - val = subBin[i, j].astype(int) - subImage[i, j] = np.floor( - (inverseI * (inverseJ * LU[val] + j * RU[val]) + i * (inverseJ * LB[val] + j * RB[val])) / float( - num)) - return subImage - @abstractmethod def compute(self, img): pass @@ -323,22 +298,22 @@ def compute(self, img): class Median(SpatialFilter): def __init__(self, maskSize): - # arbitrary kernel weights assigned - kernel = np.zeros((maskSize,maskSize)) - middle = int((maskSize-1)/2) - kernel[middle, middle] = 1 - - super().__init__(maskSize, kernel, name='median', linearity='non-linear') + # arbitrary kernel weights assigned since kernel is not used + super().__init__(maskSize, np.zeros((maskSize,maskSize)), name='median', linearity='non-linear') - def computePixel(self, sub): + def compute(self, sub): return statistics.median(sub.flatten()) class AdaptiveWeightedMedian(SpatialFilter): def __init__(self, maskSize, constant, centralWeight): - # Create kernel with weights representing distance from centre using equivalent of pythagoras + # Create 1D array of linearly distributed values with given start/ stop values and a step size of maskSize ax = np.linspace(-(maskSize - 1) / 2., (maskSize - 1) / 2., maskSize) + + # Create coordinate grid using 1D linspace array xx, yy = np.meshgrid(ax, ax) + + # Finally, create kernel of weight corresponding to distance from centre using pythagoras theorem kernel = np.sqrt(np.square(xx) + np.square(yy)) # set max weight, used for centre of kernel, and constant used in formula @@ -347,8 +322,8 @@ def __init__(self, maskSize, constant, centralWeight): super().__init__(maskSize, kernel, name='adaptive-weighted-median', linearity='non-linear') - def computePixel(self, sub): - # calculate the standard deviation and mean of sub matrix + def compute(self, sub): + # Calculate the standard deviation and mean of sub matrix std = np.std(sub) mean = np.mean(sub) @@ -386,10 +361,13 @@ def __init__(self, maskSize): try: assert kernel.sum() == 1 except AssertionError: - raise Exception("Sum of kernel weights for mean filter should equal 1. They equal {}!".format(self.kernel.sum())) + if abs(1 - kernel.sum()) < 0.01: + pass + else: + raise Exception("Sum of kernel weights for mean filter should equal 1. They equal {}!".format(kernel.sum())) super().__init__(maskSize, kernel, name='mean', linearity='linear') - def computePixel(self, sub): + def compute(self, sub): # element-wise multiplication of the kernel and image pixel under consideration return (self.kernel * sub).sum() @@ -398,14 +376,21 @@ class TrimmedMean(SpatialFilter): Can be used to discard a number of outliers from the higher and lower ends of the retrieved sub matrix of pixel values. """ def __init__(self, maskSize, trimStart=1, trimEnd=1): - kernel = np.ones((maskSize,maskSize)) + kernel = np.ones((maskSize,maskSize))/(maskSize**2) + try: + assert kernel.sum() == 1 + except AssertionError: + if abs(1 - kernel.sum()) < 0.01: + pass + else: + raise Exception("Sum of kernel weights for mean filter should equal 1. They equal {}!".format(kernel.sum())) # Assign trim parameters as attributes specific to this class for use in computation self.trimStart = trimStart self.trimEnd = trimEnd super().__init__(maskSize, kernel, name='trimmed-mean', linearity='linear') - def computePixel(self, sub): + def compute(self, sub): trimmedSub = list(sub.flatten()) return np.mean(trimmedSub[self.trimStart:-self.trimStart]) @@ -421,14 +406,18 @@ def __init__(self, sig): pass # Create kernel with weights representing gaussian distribution with input standard deviation + # Create 1D array of linearly distributed values with given start/ stop values and a step size of maskSize ax = np.linspace(-(maskSize - 1) / 2., (maskSize - 1) / 2., maskSize) + + # Create coordinate grid using 1D linspace array xx, yy = np.meshgrid(ax, ax) + # Finally, create kernel using gaussian distribution formula kernel = np.exp(-0.5 * (np.square(xx) + np.square(yy)) / np.square(sig)) super().__init__(maskSize, kernel, name='gaussian', linearity='linear') - def computePixel(self, sub): + def compute(self, sub): """ Element-wise multiplication of the kernel and image pixel under consideration, accounting for normalisation to mitigate DC distortion effects. @@ -443,19 +432,25 @@ class Sharpening(SpatialFilter): """ def __init__(self, maskSize): - # TODO: Make ratio of intensity reduction vs. increase configurable for both high and low pass - kernel = np.full((maskSize, maskSize), -1/(maskSize**2)) + # Create kernel of negative one over the square of mask size + kernel = np.full((maskSize, maskSize), -1) + + # Set centre pixel to positive fraction such that kernel weights sum to zero middle = int((maskSize-1)/2) - kernel[middle, middle] = 1 - 1/(maskSize**2) + kernel[middle, middle] = maskSize**2 - 1 + + # Divide all elements by the number of elements in the window + kernel = np.divide(kernel, maskSize**2) + super().__init__(maskSize, kernel, name='high-pass', linearity='linear') - def computePixel(self, sub): + def compute(self, sub): try: - assert -0.01 < self.kernel.sum() < 0.01 + assert -0.01 < np.sum(self.kernel) < 0.01 except AssertionError: - raise Exception("Sum of high pass filter weights should be effectively zero.") + raise Exception("Sum of high pass filter weights should be effectively 0.") - return (self.kernel * sub).sum() + return np.sum(np.multiply(self.kernel, sub)) class LowPass(SpatialFilter): def __init__(self, maskSize, middleWeight=1/2, otherWeights=1/8): @@ -468,7 +463,7 @@ def __init__(self, maskSize, middleWeight=1/2, otherWeights=1/8): super().__init__(maskSize, kernel, name='low-pass', linearity='non-linear') - def computePixel(self, sub): + def compute(self, sub): return (self.kernel * sub).sum()/ self.kernel.sum() class TruncateCoefficients(FourierFilter): @@ -498,8 +493,7 @@ class Equalise(HistogramFilter): This filter normalises the brightness whilst increasing the contrast of the image at the same time. """ def __init__(self): - kernel = np.ones((1,1)) - super().__init__(kernel, name='histogram-equalise') + super().__init__(3, name='histogram-equalise') def compute(self, img): histogram, cs = self.getHistogramWithCS(img) @@ -534,8 +528,8 @@ def compute(self, img, padding=True): imgFiltered = np.zeros_like(img) # Loop over every pixel of padded image - for col in tqdm(range(img.shape[1])): - for row in range(img.shape[0]): + for row in tqdm(range(img.shape[0])): + for col in range(img.shape[1]): # Create sub matrix of mask size surrounding pixel under consideration sub = imgPadded[row: row+self.maskSize, col: col+self.maskSize] @@ -558,12 +552,19 @@ def __init__(self, maskSize=32): super().__init__(maskSize, name='sliding-window-adaptive-histogram-equalise') def updateHistogramAndSub(self, histogram, sub, nextCol): + + # Pair pixels in the corresponding rows of the trailing and next columns for pixelSub, pixelAdd in zip(sub[:, 0], nextCol): + # Subtract 1 from the histogram at the occurrence of each pixel intensity in the trailing column histogram[pixelSub] -= 1 + + # Add one for each pixel intensity occurrence in the next kernel window column histogram[pixelAdd] += 1 + # Drop the trailing column of the sub matrix sub = np.delete(sub, 0, axis=1) + # Return the histogram and sub matrix with next column appended return histogram, np.append(sub, nextCol.reshape((self.maskSize, 1)), axis=1) def compute(self, img, padding=True): @@ -606,10 +607,13 @@ def compute(self, img, padding=True): # Get next column of sub array in image nextCol = imgPadded[row: row+self.maskSize, col+self.maskSize] except IndexError: - if col + self.maskSize == imgPadded.shape[1] + 1: + if col + self.maskSize <= imgPadded.shape[1] + 1: continue else: - raise IndexError("Index error triggered unexpectedly when at column {}, row {}.".format(col, row)) + raise IndexError("Index error triggered unexpectedly when at column {}, row {}.\n" + "mask size = {}\n" + "col+self.maskSize = {}\n" + "imgPadded.shape[1] = {}\n".format(col, row, self.maskSize, col+self.maskSize, imgPadded.shape[1])) # Create sub matrix of mask size surrounding pixel under consideration histogram, sub = self.updateHistogramAndSub(histogram, sub, nextCol) diff --git a/handler.py b/handler.py index 82a4536..6b77906 100644 --- a/handler.py +++ b/handler.py @@ -5,17 +5,19 @@ Digital Image Processing 2020, Assignment 1/1, 20% """ +# Import packages and modules used in code import numpy as np from PIL import Image import logging -from IPython.display import display import filters import matplotlib.pyplot as plt from pathlib import Path import os.path +# Initialise logging used to track info and warning messages logging.basicConfig() +# Global variables used for ease of access to test images FOETUS_PATH_ORIGINAL = ".\\images\\foetus.png" NZJERS_PATH_ORIGINAL = ".\\images\\NZjers1.png" @@ -27,12 +29,14 @@ def getImageAsArray(path, convertToGrey=True): :return: both numpy array of image pixel values and original Pillow image data for optional use (likely will discard) """ try: - # Use Pillow to load image data + # Use library Pillow to open image data imgData = Image.open(path) except FileNotFoundError: raise Exception("No file found at that file path. Check it's there and try again. If error persists, check for special characters in file path.") + # Convert to grey scale by default if convertToGrey: + # Convert image to grey scale imgData.convert("L") else: pass @@ -40,10 +44,14 @@ def getImageAsArray(path, convertToGrey=True): # Convert to 2D numpy array and return return np.asarray(imgData), imgData -def plotFigs(images): +def plotFigs(images, imageFilter=None, filteredTitle=None, edgeTitle=None): """ - Simple function to display image(s) in notebook. Intended for use to see original vs filtered images. - + Function provides uniform plotting of figures to show results of filtering alongisde original image. Later, image + edges can be displayed in additional third column, automatically determined by the number of images passed in. + :param images: list of images to display - determines number of columns of sub plot + :param imageFilter: filter object used to get filter name used in most image titles + :param filteredTitle: filtered image title override, used to display more than default information in title + :param edgeTitle: edge title override :return: None """ # Check types and correct common error if necessary @@ -56,32 +64,55 @@ def plotFigs(images): else: raise Exception("Make sure you pass in either a single image as np ndarray or list of images as np ndarray.") - # set up side-by-side image display + # Set dimensions of side-by-side image display fig = plt.figure() fig.set_figheight(15) fig.set_figwidth(15) - ax1 = fig.add_subplot(1,2,1) + # Create axis for first figure, set title and show grey-scale original image on plot + ax1 = fig.add_subplot(1,len(images),1) ax1.title.set_text('original') plt.imshow(images[0], cmap='gray') - # display the new image - ax2 = fig.add_subplot(1,2,2) - ax2.title.set_text('filtered') + # Create axis for second figure, set title and show grey-scale filtered image on plot + ax2 = fig.add_subplot(1,len(images),2) + if filteredTitle: + title = filteredTitle + elif imageFilter: + title = imageFilter.name + "_maskSize" + str(imageFilter.maskSize) + else: + title = 'filtered' + ax2.title.set_text(title) plt.imshow(images[1], cmap='gray') + # Create axis for third figure if more than 2 images available, set title and show grey-scale edge detections on plot + if len(images) > 2: + # display the new image + ax3 = fig.add_subplot(1, len(images), 3) + if edgeTitle: + title = edgeTitle + else: + title = 'edge image' + ax3.title.set_text(title) + plt.imshow(images[2], cmap='gray') + + # Show all plots in Jupyter display plt.show(block=True) -def saveAll(img, filtr, saveFilter=True): +def saveAll(img, imageFilter, saveFilter=True): """ - Function to save all figures relevant to report. Currently filtered image and plot of kernel. - :return: + Function used to save all filter results to disk, including kernel plot, filter object state and images. + :param img: filtered image + :param imageFilter: filter object + :param saveFilter: boolean used to configure if filter object attributes saved to text file + :return: None """ - assert isinstance(filtr, filters.SpatialFilter) + # Locate current directory, used for joining with relative paths currentDir = Path().absolute() - root = str(currentDir) + '\\..\outputs\{}\maskSize_{}\\'.format(filtr.name, filtr.maskSize) + root = str(currentDir) + '\\..\outputs\{}\maskSize_{}\\'.format(imageFilter.name, imageFilter.maskSize) + # Create root path if not present if not os.path.exists(root): os.makedirs(root) @@ -92,19 +123,21 @@ def saveAll(img, filtr, saveFilter=True): img_PIL.save(root+'filtered_image.png', 'PNG') print("Saved filtered image to... \n{}\n\n".format(root+'filtered_image.png')) - # TODO: Make saved image of plot larger. Currently will be tiny if mask size is eg 9x9. # Save figure of kernel plot to image - plt.imsave(root+'kernel_plot.png', filtr.kernel) + plt.imsave(root +'kernel_plot.png', imageFilter.kernel) print("Saved filtered image to... \n{}\n\n".format(root+'kernel.png')) if saveFilter: # Save filter attributes (including kernel as array.tolist()) to text file for traceability + # Open text file with write permissions with open(root+'filter.txt', 'w') as f: - for k, v in filtr.__dict__.items(): + # Retrieve attributes from list of filter object dictionary items as key-value pairs + for k, v in imageFilter.__dict__.items(): if isinstance(v, np.ndarray): v = v.tolist() else: pass + # Write attribute field and values to text file f.write(''.join("filter.{} = {}\n".format(k, v))) print("Saved filter object attributes to... \n{}\n\n".format(root + 'filter.txt')) else: diff --git a/third_party_filters.py b/third_party_filters.py index 81c2124..1b5ea52 100644 --- a/third_party_filters.py +++ b/third_party_filters.py @@ -1,5 +1,7 @@ import cv2.cv2 as cv import handler +import numpy as np +import matplotlib as plt def equalise(path): """ @@ -21,4 +23,9 @@ def CLAHE(path): # create a CLAHE object (Arguments are optional). clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)) cl1 = clahe.apply(img) - handler.plotFigs([img, cl1]) \ No newline at end of file + handler.plotFigs([img, cl1]) + +def edgeDetect(img, minVal=100, maxVal=200): + + return cv.Canny(img, minVal, maxVal) +