From 06ce0345789399aa6591493922cdacc45a482e73 Mon Sep 17 00:00:00 2001 From: Tbs_Fchnr Date: Mon, 26 Oct 2020 16:56:56 +0100 Subject: [PATCH] create abstract base class filter with two filters implemented as child classes. abstract method apply added that will contain computation of pixel value updates. First notebook added. --- TF00-initial-exploration.ipynb | 106 ++++++++++++++++++ edge_detector.py | 0 filters.py | 47 ++++++++ handler.py | 67 +++++++++++ ...-enhancement.py => image-gui-deprecated.py | 6 +- requirements.txt | 60 ++++++++++ techniques.py | 17 --- 7 files changed, 283 insertions(+), 20 deletions(-) create mode 100644 TF00-initial-exploration.ipynb create mode 100644 edge_detector.py create mode 100644 filters.py create mode 100644 handler.py rename image-enhancement.py => image-gui-deprecated.py (97%) delete mode 100644 techniques.py diff --git a/TF00-initial-exploration.ipynb b/TF00-initial-exploration.ipynb new file mode 100644 index 0000000..ac3996f --- /dev/null +++ b/TF00-initial-exploration.ipynb @@ -0,0 +1,106 @@ +{ + "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" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load Image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "img, _ = handler.getImageAsArray(handler.FOETUS_PATH_ORIGINAL, convertToGrey=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "handler.plotFigs(img)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create Filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "medFilter = filters.Median(maskSize=9)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(medFilter)" + ] + }, + { + "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 new file mode 100644 index 0000000..e69de29 diff --git a/filters.py b/filters.py new file mode 100644 index 0000000..a37cc40 --- /dev/null +++ b/filters.py @@ -0,0 +1,47 @@ +""" +Module to define filters and their computation algorithms. Used by handler.py. + +Digital Image Processing 2020, Assignment 1/1, 20% +""" + +from abc import ABC, abstractmethod +from scipy.signal import convolve2d + +class Filter(ABC): + def __init__(self, name, linearity, maskSize): + self.name = name + self.linearity = linearity + self.maskSize = maskSize + + def __str__(self): + """ + Override built-in str function so a description of the filter is shown when you run print(filter), where + filter is an instance of class Filter. + :return: string describing filter instance. + """ + descriptor = "Filter name: {},\nLinearity: {},\nMask size: {}\n".format( + self.name, + self.linearity, + self.maskSize + ) + return descriptor + + @abstractmethod + def apply(self): + pass + +class Median(Filter): + def __init__(self, maskSize): + assert isinstance(maskSize, int) + super().__init__(name='median', linearity='non-linear', maskSize=maskSize) + + def apply(self): + raise NotImplementedError + +class Mean(Filter): + def __init__(self, maskSize): + assert isinstance(maskSize, int) + super().__init__(name='mean', linearity='linear', maskSize=maskSize) + + def apply(self): + raise NotImplementedError \ No newline at end of file diff --git a/handler.py b/handler.py new file mode 100644 index 0000000..d98f4a1 --- /dev/null +++ b/handler.py @@ -0,0 +1,67 @@ +""" +Module used to load image, apply filter, and show images in figures. Use in conjunction with filters and +edge-detector modules to complete assignment. + +Digital Image Processing 2020, Assignment 1/1, 20% +""" + +import numpy as np +from PIL import Image +import logging +from IPython.display import display + +logging.basicConfig() + +FOETUS_PATH_ORIGINAL = ".\\images\\foetus.png" +NZJERS_PATH_ORIGINAL = ".\\images\\NZjers1.png" +FOETUS_PATH_FILTERED = ".\\images\\foetus-filtered.png" +NZJERS_PATH_FILTERED = ".\\images\\NZjers1-filtered.png" + +def getImageAsArray(path, convertToGrey=True): + """ + Function loads image from local file system using Pillow image loader and returns as numpy array. + :param convertToGrey: bool parameter set to convert img to grey scale on loading. Default == True. + :param path: file path to look to for image + :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 + 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.") + + if convertToGrey: + # TODO: Convert to grey scale image + raise NotImplementedError + else: + pass + + # Convert to 2D numpy array and return + return np.asarray(imgData), imgData + +def applyFilter(image, filtr): + # TODO: Check filter present in filters.py module + # TODO: Check parameters are correct + # TODO: Convolve filter with image + # TODO: Return filtered image. + raise NotImplementedError + +def plotFigs(images): + """ + Simple function to display image(s) in notebook. Intended for use to see original vs filtered images. + + :return: None + """ + try: + assert isinstance(images, list) + except AssertionError: + if isinstance(images, np.ndarray): + images = [images] + pass + else: + raise Exception("Make sure you pass in either a single image as np ndarray or list of images as np ndarray.") + + for img in images: + img_PIL = Image.fromarray(img, 'L') + display(img_PIL) \ No newline at end of file diff --git a/image-enhancement.py b/image-gui-deprecated.py similarity index 97% rename from image-enhancement.py rename to image-gui-deprecated.py index 1c0f830..13effa6 100644 --- a/image-enhancement.py +++ b/image-gui-deprecated.py @@ -2,7 +2,7 @@ from PIL import Image, ImageTk import tkinter as tk import logging -import techniques +import filters import inspect logging.basicConfig() @@ -76,8 +76,8 @@ def getParamsFromGUI(): # Create option menu and create variable to store selected option from options selectedDrop = tk.StringVar(master) -selectedDrop.set(inspect.getmembers(techniques, inspect.isfunction)[0]) -option = tk.OptionMenu(frame0, selectedDrop, list(inspect.getmembers(techniques, inspect.isfunction))) +selectedDrop.set(inspect.getmembers(filters, inspect.isfunction)[0]) +option = tk.OptionMenu(frame0, selectedDrop, list(inspect.getmembers(filters, inspect.isfunction))) option.pack(side=tk.TOP) # Create TK image objects from the pixel arrays as GUI attributes, ready to be displayed in the GUI diff --git a/requirements.txt b/requirements.txt index 7f2254c..091827a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,61 @@ +argon2-cffi==20.1.0 +async-generator==1.10 +attrs==20.2.0 +backcall==0.2.0 +bleach==3.2.1 +certifi==2020.6.20 +cffi==1.14.3 +colorama==0.4.4 +cycler==0.10.0 +decorator==4.4.2 +defusedxml==0.6.0 +entrypoints==0.3 +ipykernel==5.3.4 +ipython==7.18.1 +ipython-genutils==0.2.0 +ipywidgets==7.5.1 +jedi==0.17.2 +Jinja2==2.11.2 +jsonschema==3.2.0 +jupyter==1.0.0 +jupyter-client==6.1.7 +jupyter-console==6.2.0 +jupyter-core==4.6.3 +jupyterlab-pygments==0.1.2 +kiwisolver==1.2.0 +MarkupSafe==1.1.1 +matplotlib==3.3.2 +mistune==0.8.4 +nbclient==0.5.1 +nbconvert==6.0.7 +nbformat==5.0.8 +nest-asyncio==1.4.2 +notebook==6.1.4 numpy==1.19.2 +packaging==20.4 +pandocfilters==1.4.3 +parso==0.7.1 +pickleshare==0.7.5 +Pillow==7.2.0 +prometheus-client==0.8.0 +prompt-toolkit==3.0.8 +pycparser==2.20 +Pygments==2.7.2 +pyparsing==2.4.7 +pyrsistent==0.17.3 +python-dateutil==2.8.1 +pywin32==228 +pywinpty==0.5.7 +pyzmq==19.0.2 +qtconsole==4.7.7 +QtPy==1.9.0 +scipy==1.5.2 +Send2Trash==1.5.0 +six==1.15.0 +terminado==0.9.1 +testpath==0.4.4 +tornado==6.0.4 +traitlets==5.0.5 +wcwidth==0.2.5 +webencodings==0.5.1 +widgetsnbextension==3.5.1 diff --git a/techniques.py b/techniques.py deleted file mode 100644 index 7440570..0000000 --- a/techniques.py +++ /dev/null @@ -1,17 +0,0 @@ -from scipy import ndimage - -def meanFilter(image, mask): - """ - takes in 2D array of pixel greymap and mask with weightings. convolves the mask over pixel array and returns - the filtered image as 2D array. - :param image: - :param mask: - :return: - """ - - - - raise NotImplementedError - -def medianFilter(): - raise NotImplementedError \ No newline at end of file