diff --git a/.gitignore b/.gitignore index ea72868..6e6685e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,6 @@ __pycache__/ multyscale.egg-info/ docs/_build/ -test/odog_matlab.mat +test/odog_MATLAB.mat + +docs/reference/_api/ diff --git a/README.md b/README.md index e667aaf..5cc254f 100644 --- a/README.md +++ b/README.md @@ -37,5 +37,5 @@ import multyscale ``` - `multyscale.filters` contains functions to generate filters -- `multyscale.filterbank` contains classes defining specific sets (banks) of filters +- `multyscale.filterbanks` contains classes defining specific sets (banks) of filters - `multsycale.models` implements some common models from the literature diff --git a/demo/demo_dogbank.py b/demo/demo_dogbank.py deleted file mode 100644 index a0f576d..0000000 --- a/demo/demo_dogbank.py +++ /dev/null @@ -1,47 +0,0 @@ -# %% -# Third party libraries -import matplotlib.pyplot as plt -import numpy as np -from PIL import Image - -# Import local module -from multyscale import filterbank, utils - -# %% Load example stimulus -stimulus = np.asarray(Image.open("example_stimulus.png").convert("L")) - -# %% Parameters of image -shape = stimulus.shape # filtershape in pixels -# visual extent, same convention as pyplot: -visextent = (-16, 16, -16, 16) - -# %% Create image coordinate system: -axish = np.linspace(visextent[0], visextent[1], shape[0]) -axisv = np.linspace(visextent[2], visextent[3], shape[1]) - -(x, y) = np.meshgrid(axish, axisv) - -# %% Filterbank parameters -num_scales = 7 -largest_center_sigma = 3 # in degrees -center_sigmas = utils.octave_intervals(num_scales) * largest_center_sigma -cs_ratio = 2 # center-surround ratio - -# Convert to filterbank parameters -sigmas = [(s, cs_ratio * s) for s in center_sigmas] - -# %% Create filterbank -bank = filterbank.DOGBank(sigmas, x, y) - -# %% Visualise filterbank -for i in range(bank.filters.shape[0]): - plt.subplot(1, bank.filters.shape[0], i + 1) - plt.imshow(bank.filters[i, ...], extent=visextent) - -# %% Apply filterbank -filters_output = bank.apply(stimulus) - -# %% Visualise filter bank output -for i in range(filters_output.shape[0]): - plt.subplot(1, filters_output.shape[0], i + 1) - plt.imshow(filters_output[i, ...], extent=visextent) diff --git a/demo/demo_filterbank.py b/demo/demo_filterbank.py deleted file mode 100644 index 20a1fd0..0000000 --- a/demo/demo_filterbank.py +++ /dev/null @@ -1,60 +0,0 @@ -# %% -# Third party libraries -import matplotlib.pyplot as plt -import numpy as np -from PIL import Image - -# Import local module -from multyscale import filterbank, utils - -# %% Load example stimulus -stimulus = np.asarray(Image.open("example_stimulus.png").convert("L")) - -# %% Parameters of image -shape = stimulus.shape # filtershape in pixels -# visual extent, same convention as pyplot: -visextent = (-16, 16, -16, 16) - -# %% Create image coordinate system: -axish = np.linspace(visextent[0], visextent[1], shape[0]) -axisv = np.linspace(visextent[2], visextent[3], shape[1]) - -(x, y) = np.meshgrid(axish, axisv) - -# %% Filterbank parameters -# Parameters (BM1999) -n_orientations = 6 -num_scales = 7 -largest_center_sigma = 3 # in degrees -center_sigmas = utils.octave_intervals(num_scales) * largest_center_sigma -cs_ratio = 2 # center-surround ratio - -# Convert to filterbank parameters -orientations = np.arange(0, 180, 180 / n_orientations) -sigmas = [((s, s), (s, cs_ratio * s)) for s in center_sigmas] - -# %% Create filterbank -bank = filterbank.ODOGBank(orientations, sigmas, x, y) - -# %% Visualise filterbank -for i in range(bank.filters.shape[0]): - for j in range(bank.filters.shape[1]): - plt.subplot( - bank.filters.shape[0], - bank.filters.shape[1], - i * bank.filters.shape[0] + ((j + i) * 1) + 1, - ) - plt.imshow(bank.filters[i, j, ...], extent=visextent) - -# %% Apply filterbank -filters_output = bank.apply(stimulus) - -# %% Visualise filter bank output -for i in range(filters_output.shape[0]): - for j in range(filters_output.shape[1]): - plt.subplot( - filters_output.shape[0], - filters_output.shape[1], - i * filters_output.shape[0] + ((j + i) * 1) + 1, - ) - plt.imshow(filters_output[i, j, ...], extent=visextent) diff --git a/demo/demo_filters.py b/demo/demo_filters.py deleted file mode 100644 index ac9fab7..0000000 --- a/demo/demo_filters.py +++ /dev/null @@ -1,68 +0,0 @@ -# %% -# Third party libraries -import matplotlib.pyplot as plt -import numpy as np -from PIL import Image - -# Import local module -from multyscale import filters - -# %% Load example stimulus -stimulus = np.asarray(Image.open("example_stimulus.png").convert("L")) - -# %% Parameters of image -shape = stimulus.shape # filtershape in pixels -# visual extent, same convention as pyplot: -visextent = (-16, 16, -16, 16) - -# %% Create image coordinate system: -axish = np.linspace(visextent[0], visextent[1], shape[0]) -axisv = np.linspace(visextent[2], visextent[3], shape[1]) - -(x, y) = np.meshgrid(axish, axisv) - -# %% Circular gaussian -img = filters.gaussian2d(x, y, (2, 2)) - -# Plot -plt.subplot(1, 2, 1) -plt.imshow(img, extent=visextent) - -# Plot horizontal meridian -plt.subplot(1, 2, 2) -plt.plot(x[int(img.shape[0] / 2)], img[int(img.shape[0] / 2), ...]) - -# %% Elliptical gaussian (3:1 axes; rotated) -img = filters.gaussian2d(x, y, (6, 2), orientation=15) - -# Plot -plt.subplot(1, 2, 1) -plt.imshow(img, extent=visextent) - -# Plot horizontal meridian -plt.subplot(1, 2, 2) -plt.plot(x[int(img.shape[0] / 2)], img[int(img.shape[0] / 2), ...]) - -# %% ODOG -sigmas = ((2, 2), (2, 4)) # surround Gaussian is 2:1 in one axis - -img = filters.odog(x, y, sigmas, orientation=(80, 80)) - -# % Apply filter -filt_img = filters.apply(stimulus, img) - -# Plot stimulus -plt.subplot(1, 3, 1) -plt.imshow(stimulus, cmap="gray", extent=visextent) - -# Plot filter + horizontal meridian -plt.subplot(2, 3, 2) -plt.imshow(img, extent=visextent) -plt.subplot(2, 3, 5) -plt.plot(x[int(img.shape[0] / 2)], img[int(img.shape[0] / 2), ...]) - -# Plot filtered image -plt.subplot(1, 3, 3) -plt.imshow(filt_img, extent=visextent) - -# %% diff --git a/demo/demo_flodog.py b/demo/demo_flodog.py deleted file mode 100644 index 4cd0d05..0000000 --- a/demo/demo_flodog.py +++ /dev/null @@ -1,125 +0,0 @@ -# %% -# Third party libraries -import matplotlib.pyplot as plt -import numpy as np -from PIL import Image - -# Import local module -from multyscale import filters, models - -# %% Load example stimulus -stimulus = np.asarray(Image.open("example_stimulus.png").convert("L")) - -# %% Parameters of image -shape = stimulus.shape # filtershape in pixels -# visual extent, same convention as pyplot: -visextent = (-16, 16, -16, 16) - -# %% Create model -model = models.FLODOG_RHS2007(shape, visextent) - -# %% Integrated run -output_1 = model.apply(stimulus) - -# %% Visualise output -plt.subplot(1, 2, 1) -plt.imshow(output_1, extent=visextent) -plt.subplot(1, 2, 2) -plt.plot(output_1[512, 250:750]) - - -# %% Visualise filterbank -for i in range(model.bank.filters.shape[0]): - for j in range(model.bank.filters.shape[1]): - plt.subplot( - model.bank.filters.shape[0], - model.bank.filters.shape[1], - i * model.bank.filters.shape[0] + ((j + i) * 1) + 1, - ) - plt.imshow(model.bank.filters[i, j, ...], extent=visextent) - -# %% Apply filterbank -filters_output = model.bank.apply(stimulus) - -# %% Visualise filter bank output -for i in range(filters_output.shape[0]): - for j in range(filters_output.shape[1]): - plt.subplot( - filters_output.shape[0], - filters_output.shape[1], - i * filters_output.shape[0] + ((j + i) * 1) + 1, - ) - plt.imshow(filters_output[i, j, ...], extent=visextent) - - -# %% Weight each filter output according to scale -weighted_outputs = np.ndarray(filters_output.shape) -for i in range(filters_output.shape[0]): - for j, output in enumerate(filters_output[i, ...]): - weighted_outputs[i, j, ...] = output * model.scale_weights[j] - -# %% Build normalizer for each filter output -center_sigmas = [sigma[0][0] for sigma in model.bank.sigmas] -sdmix = model.sdmix # stdev of Gaussian weights for scale mixing - -# Create normalizer images -normalizers = np.empty(weighted_outputs.shape) -for o, multiscale in enumerate(weighted_outputs): # separate for orientations - for i, filt in enumerate(multiscale): - normalizer = np.empty(filt.shape) - - # Identify relative index of each scale to the current one - rel_i = i - np.asarray(range(multiscale.shape[0])) - - # Gaussian weights, based on relative index - gweights = np.exp(-(rel_i**2) / 2 * sdmix**2) / (sdmix * np.sqrt(2 * np.pi)) - - # Sum filter outputs, by Gaussian weights - normalizer = np.tensordot(multiscale, gweights, axes=(0, 0)) - - # Normalize normalizer... - area = gweights.sum() - normalizer = normalizer / area - - # Accumulate - normalizers[o, i, ...] = normalizer - -# %% Blur normalizers -# Create Gaussian window -window = filters.gaussian2d(model.bank.x, model.bank.y, (model.window_sigma, model.window_sigma)) - -# Normalize window to unit-sum (== spatial averaging filter) -window = window / window.sum() - -for o, multiscale in enumerate(normalizers): - for s, normalizer in enumerate(multiscale): - # Square image - normalizer = np.square(normalizer) - - # Apply Gaussian window - normalizer = filters.apply(normalizer, window) - - # Square root - normalizer = np.sqrt(normalizer) - normalizers[o, s, ...] = normalizer - -# %% Normalize filter output -normalized_outputs = np.ndarray(weighted_outputs.shape) -for o, s in np.ndindex(weighted_outputs.shape[:2]): - normalized_outputs[o, s] = weighted_outputs[o, s] / normalizers[o, s] - -# %% Sum over orientations -output_2 = normalized_outputs.sum((0, 1)) - -# %% Visualise both outputs -plt.subplot(2, 2, 1) -plt.imshow(output_1, extent=visextent) -plt.subplot(2, 2, 2) -plt.plot(output_1[512, 250:750]) - -plt.subplot(2, 2, 3) -plt.imshow(output_2, extent=visextent) -plt.subplot(2, 2, 4) -plt.plot(output_2[512, 250:750]) - -np.allclose(output_1, output_2) diff --git a/demo/demo_flodog_params.ipynb b/demo/demo_flodog_params.ipynb deleted file mode 100644 index babbe5f..0000000 --- a/demo/demo_flodog_params.ipynb +++ /dev/null @@ -1,145 +0,0 @@ -{ - "metadata": { - "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.5" - }, - "orig_nbformat": 2, - "kernelspec": { - "name": "python385jvsc74a57bd09ce64e1dc51e0fcc26138e95c5a56d90c12dc0aa848e7879d92efc252f96a3f1", - "display_name": "Python 3.8.5 64-bit ('lightness_models')" - } - }, - "nbformat": 4, - "nbformat_minor": 2, - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Third party libraries\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "from PIL import Image\n", - "import ipywidgets\n", - "\n", - "# Import local module\n", - "import multyscale" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# %% Load example stimulus\n", - "stimulus = np.asarray(Image.open(\"example_stimulus.png\").convert(\"L\"))\n", - "\n", - "# %% Parameters of image\n", - "shape = stimulus.shape # filtershape in pixels\n", - "# visual extent, same convention as pyplot:\n", - "visextent = (-16, 16, -16, 16)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# %% Create model\n", - "model = multyscale.models.FLODOG_RHS2007(shape, visextent)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "filters_output = model.bank.apply(stimulus)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "weighted_outputs = model.weight_outputs(filters_output)" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "output_type": "display_data", - "data": { - "text/plain": "interactive(children=(FloatSlider(value=0.5, description='Scale-mixing Gaussian SD:', max=3.0, min=0.25, step=…", - "application/vnd.jupyter.widget-view+json": { - "version_major": 2, - "version_minor": 0, - "model_id": "255cb991dd1a4167adbe247a309d8d2d" - } - }, - "metadata": {} - }, - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 29 - } - ], - "source": [ - "def f(sdmix, spatial_window_scalar):\n", - " model.spatial_window_scalar = spatial_window_scalar\n", - " model.sdmix = sdmix\n", - " model.scale_norm_weights = multyscale.normalization.scale_norm_weights_gaussian(\n", - " len(model.scale_weights), model.sdmix\n", - " )\n", - " model.normalization_weights = multyscale.normalization.create_normalization_weights(\n", - " 6, 7, model.scale_norm_weights, model.orientation_norm_weights\n", - " )\n", - " model.window_sigmas = np.broadcast_to(\n", - " np.array(model.center_sigmas)[None, ..., None], (6, 7, 2)\n", - " )\n", - "\n", - " print(f\"Running {model.sdmix,model.spatial_window_scalar}...\")\n", - " output = model.normalize_outputs(weighted_outputs).sum((0,1))\n", - "\n", - " plt.subplot(1, 2, 1)\n", - " plt.imshow(output, extent=visextent)\n", - " plt.subplot(1, 2, 2)\n", - " plt.plot(output[512, 250:750])\n", - " print(\"Done!\")\n", - "\n", - "ipywidgets.interact_manual(f, sdmix=ipywidgets.FloatSlider(description = \"Scale-mixing Gaussian SD:\", min = 0.25, max = 3., value = .5, step=0.25), spatial_window_scalar=ipywidgets.FloatSlider(description = \"Spatial averaging window scalar:\", min = 1, max = 4, value = 2, step=1))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ] -} \ No newline at end of file diff --git a/demo/demo_fodog.py b/demo/demo_fodog.py deleted file mode 100644 index 44a27b6..0000000 --- a/demo/demo_fodog.py +++ /dev/null @@ -1,65 +0,0 @@ -# Third party libraries -import matplotlib.pyplot as plt -import numpy as np -from PIL import Image - -import multyscale - -# Import local module -from multyscale.models import FLODOG_RHS2007, ODOG_RHS2007 - - -# %% -class FODOG(ODOG_RHS2007): - def __init__(self, shape, visextent): - super().__init__(shape, visextent) - - self.sdmix = 0.5 # stdev of Gaussian weights for scale mixing - self.scale_norm_weights = multyscale.normalization.scale_norm_weights_gaussian( - len(self.scale_weights), self.sdmix - ) - self.normalization_weights = multyscale.normalization.create_normalization_weights( - 6, 7, self.scale_norm_weights, self.orientation_norm_weights - ) - - pass - - -# %% Load example stimulus -stimulus = np.asarray(Image.open("example_stimulus.png").convert("L")) - -# %% Parameters of image -shape = stimulus.shape # filtershape in pixels -# visual extent, same convention as pyplot: -visextent = (-16, 16, -16, 16) - -# %% Create models -model_ODOG = ODOG_RHS2007(shape, visextent) -model_FODOG = FODOG(shape, visextent) -model_FLODOG = FLODOG_RHS2007(shape, visextent) - -# %% Run -output_ODOG = model_ODOG.apply(stimulus) -output_FODOG = model_FODOG.apply(stimulus) -output_FLODOG = model_FLODOG.apply(stimulus) - -# %% -output_ODOG / output_ODOG.sum() -output_FODOG / output_FODOG.sum() -output_FLODOG / output_FLODOG.sum() - -# %% Visualise all outputs -plt.subplot(3, 2, 1) -plt.imshow(output_ODOG, extent=visextent) -plt.subplot(3, 2, 2) -plt.plot(output_ODOG[512, 250:750]) - -plt.subplot(3, 2, 3) -plt.imshow(output_FLODOG, extent=visextent) -plt.subplot(3, 2, 4) -plt.plot(output_FLODOG[512, 250:750]) - -plt.subplot(3, 2, 5) -plt.imshow(output_FODOG, extent=visextent) -plt.subplot(3, 2, 6) -plt.plot(output_FODOG[512, 250:750]) diff --git a/demo/demo_lodog.py b/demo/demo_lodog.py deleted file mode 100644 index 9cca32a..0000000 --- a/demo/demo_lodog.py +++ /dev/null @@ -1,106 +0,0 @@ -# %% -# Third party libraries -import matplotlib.pyplot as plt -import numpy as np -from PIL import Image - -# Import local module -from multyscale import filters, models - -# %% Load example stimulus -stimulus = np.asarray(Image.open("example_stimulus.png").convert("L")) - -# %% Parameters of image -shape = stimulus.shape # filtershape in pixels -# visual extent, same convention as pyplot: -visextent = (-16, 16, -16, 16) - -# %% Create model -model = models.LODOG_RHS2007(shape, visextent) - -# %% Integrated run -output_1 = model.apply(stimulus) - -# %% Visualise output -plt.subplot(1, 2, 1) -plt.imshow(output_1, extent=visextent) -plt.subplot(1, 2, 2) -plt.plot(output_1[512, 250:750]) - -# %% Visualise filterbank -for i in range(model.bank.filters.shape[0]): - for j in range(model.bank.filters.shape[1]): - plt.subplot( - model.bank.filters.shape[0], - model.bank.filters.shape[1], - i * model.bank.filters.shape[0] + ((j + i) * 1) + 1, - ) - plt.imshow(model.bank.filters[i, j, ...], extent=visextent) - -# %% Apply filterbank -filters_output = model.bank.apply(stimulus) - -# %% Visualise filter bank output -for i in range(filters_output.shape[0]): - for j in range(filters_output.shape[1]): - plt.subplot( - filters_output.shape[0], - filters_output.shape[1], - i * filters_output.shape[0] + ((j + i) * 1) + 1, - ) - plt.imshow(filters_output[i, j, ...], extent=visextent) - -# %% Sum over spatial scales, weighting relative to scale -multiscale_output = np.tensordot(filters_output, model.scale_weights, axes=(1, 0)) - -# %% Visualise oriented multiscale output -for i in range(multiscale_output.shape[0]): - plt.subplot(multiscale_output.shape[0], 1, i + 1) - plt.imshow(multiscale_output[i, ...], extent=visextent) - -# %% Normalize oriented multiscale outputs by local mean -# Create Gaussian window -window = filters.gaussian2d(model.bank.x, model.bank.y, (model.window_sigma, model.window_sigma)) - -# Normalize window to unit-sum (== spatial averaging filter) -window = window / window.sum() - -# Create normalizer images -normalized_multiscale_output = np.empty(multiscale_output.shape) -normalizers = np.empty(multiscale_output.shape) -for i, image in enumerate(multiscale_output): - # Square image - normalizer = np.square(image) - - # Apply Gaussian window - normalizer = filters.apply(normalizer, window) - - # Square root - normalizer = np.sqrt(normalizer) - normalizers[i, ...] = normalizer - - # Normalize - normalized_multiscale_output[i, ...] = image / normalizer - -# %% Visualise normalized multiscale output -for i in range(normalized_multiscale_output.shape[0]): - plt.subplot(normalized_multiscale_output.shape[0], 3, i * 3 + 1) - plt.imshow(multiscale_output[i, ...], extent=visextent) - plt.subplot(normalized_multiscale_output.shape[0], 3, i * 3 + 2) - plt.imshow(normalizers[i, ...], extent=visextent) - plt.subplot(normalized_multiscale_output.shape[0], 3, i * 3 + 3) - plt.imshow(normalized_multiscale_output[i, ...], extent=visextent) - -# %% Sum over orientations -output_2 = normalized_multiscale_output.sum(0) - -# %% Visualise both outputs -plt.subplot(2, 2, 1) -plt.imshow(output_1, extent=visextent) -plt.subplot(2, 2, 2) -plt.plot(output_1[512, 250:750]) - -plt.subplot(2, 2, 3) -plt.imshow(output_2, extent=visextent) -plt.subplot(2, 2, 4) -plt.plot(output_2[512, 250:750]) diff --git a/demo/demo_odog.py b/demo/demo_odog.py deleted file mode 100644 index 42cdee4..0000000 --- a/demo/demo_odog.py +++ /dev/null @@ -1,86 +0,0 @@ -# %% -# Third party libraries -import matplotlib.pyplot as plt -import numpy as np -from PIL import Image - -# Import local module -from multyscale import models - -# %% Load example stimulus -stimulus = np.asarray(Image.open("example_stimulus.png").convert("L")) - -# %% Parameters of image -shape = stimulus.shape # filtershape in pixels -# visual extent, same convention as pyplot: -visextent = (-16, 16, -16, 16) - -# %% Create model -model = models.ODOG_RHS2007(shape, visextent) - -# %% Integrated run -output_1 = model.apply(stimulus) - -# %% Visualise output -plt.subplot(1, 2, 1) -plt.imshow(output_1, extent=visextent) -plt.subplot(1, 2, 2) -plt.plot(output_1[512, 250:750]) - -# %% Visualise filterbank -for i in range(model.bank.filters.shape[0]): - for j in range(model.bank.filters.shape[1]): - plt.subplot( - model.bank.filters.shape[0], - model.bank.filters.shape[1], - i * model.bank.filters.shape[0] + ((j + i) * 1) + 1, - ) - plt.imshow(model.bank.filters[i, j, ...], extent=visextent) - -# %% Apply filterbank -filters_output = model.bank.apply(stimulus) - -# %% Visualise filter bank output -for i in range(filters_output.shape[0]): - for j in range(filters_output.shape[1]): - plt.subplot( - filters_output.shape[0], - filters_output.shape[1], - i * filters_output.shape[0] + ((j + i) * 1) + 1, - ) - plt.imshow(filters_output[i, j, ...], extent=visextent) - -# %% Sum over spatial scales, weighting relative to scale -multiscale_output = np.tensordot(filters_output, model.scale_weights, axes=(1, 0)) - -# %% Visualise oriented multiscale output -for i in range(multiscale_output.shape[0]): - plt.subplot(multiscale_output.shape[0], 1, i + 1) - plt.imshow(multiscale_output[i, ...], extent=visextent) - -# %% Normalize oriented multiscale outputs by their RMS -normalized_multiscale_output = np.empty(multiscale_output.shape) -rms = np.ndarray(6) -for i in range(multiscale_output.shape[0]): - image = multiscale_output[i] - rms[i] = np.sqrt(np.square(image).mean((-1, -2))) # image-wide RMS - normalized_multiscale_output[i] = image / rms[i] - -# %% Visualise normalized multiscale output -for i in range(normalized_multiscale_output.shape[0]): - plt.subplot(normalized_multiscale_output.shape[0], 1, i + 1) - plt.imshow(normalized_multiscale_output[i, ...], extent=visextent) - -# %% Sum over orientations -output_2 = normalized_multiscale_output.sum(0) - -# %% Visualise both outputs -plt.subplot(2, 2, 1) -plt.imshow(output_1, extent=visextent) -plt.subplot(2, 2, 2) -plt.plot(output_1[512, 250:750]) - -plt.subplot(2, 2, 3) -plt.imshow(output_2, extent=visextent) -plt.subplot(2, 2, 4) -plt.plot(output_2[512, 250:750]) diff --git a/demo/filters.ipynb b/demo/filters.ipynb deleted file mode 100644 index 6984850..0000000 --- a/demo/filters.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells":[{"source":["# Creating and applying image filters"],"cell_type":"markdown","metadata":{}},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[],"source":["# Third party libraries\n","import numpy as np\n","import matplotlib.pyplot as plt\n","from PIL import Image\n","\n","# Import local module\n","from multyscale import filters"]},{"source":["## Example stimulus\n","The example stimulus that this tutorial uses is a version of White's (1979) classic illusion, as also used by Robinson, Hammon, & de Sa (2007).\n","This stimulus is stored as an `.png` file, so it is first loaded in using `PIL` and converted to grayscale,\n","and then cast as a numpy NDArray.\n","The image 1024x1024 pixels represent 32x32 degrees of the visual field,\n","so, if centered, the visual extent of this stimulus subtends\n","from -16 degrees on the left, to 16 degrees on the right,\n","and from -16 degrees on top, to 16 degrees on the bottom."],"cell_type":"markdown","metadata":{}},{"cell_type":"code","execution_count":2,"metadata":{},"outputs":[{"output_type":"execute_result","data":{"text/plain":[""]},"metadata":{},"execution_count":2},{"output_type":"display_data","data":{"text/plain":"
","image/svg+xml":"\n\n\n \n \n \n \n 2021-05-23T17:23:22.922883\n image/svg+xml\n \n \n Matplotlib v3.4.2, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n","image/png":"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\n"},"metadata":{"needs_background":"light"}}],"source":["stimulus = np.asarray(Image.open(\"example_stimulus.png\").convert(\"L\"))\n","shape = stimulus.shape # filtershape, in pixels\n","# visual extent, in degrees visual angle,\n","# same convention as pyplot (left, right, top, bottom):\n","visextent = (-16, 16, -16, 16)\n","\n","plt.imshow(stimulus, cmap='gray', extent=visextent)"]},{"source":["## Image space\n","To construct any filters, first the coordinates of the space must be defined.\n","Here, the stimulus is said to subtend 32x32 degrees of visual extent, and has a resolution of 1024x1024 pixels.\n","The horizontal (`axish`) and vertical (`axisv`) axes of the space, then, sample the range set by the limits of the visual extent, in 1024 steps.\n","The numpy.meshgrid function then gives arrays with the `x` and `y` coordinate, in degrees of visual angle, of each of the 1024x1024 pixels in the space.\n","\n","Defining the coordinates in degrees of visual angle allows for defining all other space quantities also in degrees.\n","If, instead, the coordinates are in pixels, all other quantities (e.g., standard deviations of filters), must also be given in pixels."],"cell_type":"markdown","metadata":{}},{"cell_type":"code","execution_count":3,"metadata":{},"outputs":[],"source":["axish = np.linspace(visextent[0], visextent[1], shape[0])\n","axisv = np.linspace(visextent[2], visextent[3], shape[1])\n","\n","(x, y) = np.meshgrid(axish, axisv)"]},{"source":["## Filter types\n","The [multyscale.filters](../reference/multyscale.filters.rst) module implements several filter types:\n","\n","- `multyscale.filters.gaussian2d`: a two-dimensional Gaussian filter, which has a standard deviation along a major and minor axis, and optionally a rotation\n","- `multyscale.filters.dog`: a isotropic difference-of-Gaussian filter, composed of a center 2D Gaussian and a surround 2D Gaussian, that are each symmetrical (i.e., standard deviation along major and minor axes are identical)\n","- `multyscale.filters.odog`: an oriented difference-of-Gaussian filter, composed of a center 2D Gaussian and a surround 2D Gaussian, which do not have to be isotropic (i.e., can have different standard deviations along their major and minor axes)."],"cell_type":"markdown","metadata":{}},{"source":["### Two-dimensional Gaussian\n","The two-dimensional Gaussian is evaluate over the whole input space, i.e., for each coordinate in `x` and `y`.\n","The shape of a Gaussian is generally defined by two parameters: its central tendency, and its spread.\n","For a 2D Gaussian in space, this corresponds to the `center` location,\n","and the standard deviation `sigma`.\n","The present implementation of the Gaussian defaults to `center`ing on `(0,0)`."],"cell_type":"markdown","metadata":{}},{"cell_type":"code","execution_count":4,"metadata":{},"outputs":[{"output_type":"execute_result","data":{"text/plain":["[]"]},"metadata":{},"execution_count":4},{"output_type":"display_data","data":{"text/plain":"
","image/svg+xml":"\n\n\n \n \n \n \n 2021-05-23T17:23:23.442675\n image/svg+xml\n \n \n Matplotlib v3.4.2, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n","image/png":"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\n"},"metadata":{"needs_background":"light"}}],"source":["img = filters.gaussian2d(x, y, sigma=2)\n","img2 = filters.gaussian2d(x,y, sigma=1, center=(-4,6))\n","\n","# Plot\n","plt.subplot(2, 2, 1)\n","plt.imshow(img, extent=visextent, cmap=\"coolwarm\")\n","plt.subplot(2, 2, 2)\n","plt.imshow(img2, extent=visextent, cmap=\"coolwarm\")\n","\n","# Plot horizontal meridians\n","plt.subplot(2, 2, 3)\n","plt.plot(x[int(img.shape[0] / 2)], img[int(img.shape[0] / 2), ...])\n","plt.subplot(2, 2, 4)\n","plt.plot(x[int(img2.shape[0] / 2)], img2[int(img2.shape[0] / 2), ...])"]},{"source":["### Difference-of-Gaussian\n","A common type of filter of image processing, is the difference-of-Gaussian (DoG) filter.\n","As the name implies, it consists of two, 2D Gaussian filters:\n","a smaller \"center\" Gaussian, and a larger \"surround\" Gaussian,\n","and the filter subtracts the surround Gaussian from the center Gaussian.\n","\n","For an isotropic DoG filter only one standard deviation is required for each of the constituent Gaussians.\n","These filters are radially symmetric, and therefore considered _unoriented_ DoG filters."],"cell_type":"markdown","metadata":{}},{"cell_type":"code","execution_count":5,"metadata":{},"outputs":[{"output_type":"execute_result","data":{"text/plain":["[]"]},"metadata":{},"execution_count":5},{"output_type":"display_data","data":{"text/plain":"
","image/svg+xml":"\n\n\n \n \n \n \n 2021-05-23T17:23:24.023313\n image/svg+xml\n \n \n Matplotlib v3.4.2, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n","image/png":"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\n"},"metadata":{"needs_background":"light"}}],"source":["img = filters.dog(x, y, sigma=(2,4)) # surround Gaussian is 2:1 of center gaussian\n","\n","# Plot\n","plt.subplot(1, 2, 1)\n","plt.imshow(img, extent=visextent, cmap=\"coolwarm\")\n","\n","# Plot horizontal meridian\n","plt.subplot(1, 2, 2)\n","plt.plot(x[int(img.shape[0] / 2)], img[int(img.shape[0] / 2), ...])"]},{"source":["### Oriented 2D Gaussian\n","The previous filters are isotropic: they are radially symmetric and thus _unoriented_.\n","This is because the 2D Gaussian(s) have the same standard deviation along both axes.\n","However, this is not required.\n","_Oriented_ 2D Gaussians can be made by differing the standard deviation along the two axes.\n","Optionally, the major and minor axes of the Gaussian can be rotated away from the horizontal and vertical axes."],"cell_type":"markdown","metadata":{}},{"cell_type":"code","execution_count":6,"metadata":{},"outputs":[{"output_type":"execute_result","data":{"text/plain":["[]"]},"metadata":{},"execution_count":6},{"output_type":"display_data","data":{"text/plain":"
","image/svg+xml":"\n\n\n \n \n \n \n 2021-05-23T17:23:24.625369\n image/svg+xml\n \n \n Matplotlib v3.4.2, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n","image/png":"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\n"},"metadata":{"needs_background":"light"}}],"source":["img = filters.gaussian2d(x, y, sigma=(6, 2))\n","img2 = filters.gaussian2d(x, y, sigma=(6, 2), orientation=65) # rotation is counterclockwise\n","\n","# Plot\n","plt.subplot(2, 2, 1)\n","plt.imshow(img, extent=visextent, cmap=\"coolwarm\")\n","plt.subplot(2, 2, 2)\n","plt.imshow(img2, extent=visextent, cmap=\"coolwarm\")\n","\n","# Plot horizontal meridian\n","plt.subplot(2, 2, 3)\n","plt.plot(x[int(img.shape[0] / 2)], img[int(img.shape[0] / 2), ...])\n","plt.subplot(2, 2, 4)\n","plt.plot(x[int(img2.shape[0] / 2)], img2[int(img2.shape[0] / 2), ...])"]},{"source":["### Oriented Difference-of-Gaussian\n","A difference-of-Gaussian filter also does not have to be isotropic.\n","If at least one of the constituent Gaussians (center or surround) is oriented,\n","so will the combined filter be.\n","This filter now is an _oriented_ difference-of-Gaussians (ODoG) filter."],"cell_type":"markdown","metadata":{}},{"cell_type":"code","execution_count":7,"metadata":{},"outputs":[{"output_type":"execute_result","data":{"text/plain":["[]"]},"metadata":{},"execution_count":7},{"output_type":"display_data","data":{"text/plain":"
","image/svg+xml":"\n\n\n \n \n \n \n 2021-05-23T17:23:25.228805\n image/svg+xml\n \n \n Matplotlib v3.4.2, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n","image/png":"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\n"},"metadata":{"needs_background":"light"}}],"source":["sigma = ((2, 2), (2, 4)) # surround Gaussian is 2:1 in one axis\n","img = filters.odog(x, y, sigma, orientation=80)\n","\n","# Plot filter + horizontal meridian\n","plt.subplot(1, 2, 1)\n","plt.imshow(img, extent=visextent, cmap=\"coolwarm\")\n","plt.subplot(1, 2, 2)\n","plt.plot(x[int(img.shape[0] / 2)], img[int(img.shape[0] / 2), ...])"]},{"source":["## Applying filters"],"cell_type":"markdown","metadata":{}},{"cell_type":"code","execution_count":8,"metadata":{},"outputs":[{"output_type":"execute_result","data":{"text/plain":[""]},"metadata":{},"execution_count":8},{"output_type":"display_data","data":{"text/plain":"
","image/svg+xml":"\n\n\n \n \n \n \n 2021-05-23T17:23:25.978060\n image/svg+xml\n \n \n Matplotlib v3.4.2, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n","image/png":"iVBORw0KGgoAAAANSUhEUgAAAXkAAAD4CAYAAAAJmJb0AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAAsTAAALEwEAmpwYAABVsUlEQVR4nO29f7Rk11Xf+dnn3Hur3nv9u1tq/bBkGUm2kQ3OJD0m4DVrjC2PBYTIsAJjkvF4GGY5JBgmmVmZmDCTMDPxildmhhUGMLFIHAsP4DEEsAYcHNskMQkGLAcDloSwfli2ZEmtbnWru997VXXvOXv+OOfeurde1fv9q1+fz1q1qupW1T23Xvf93l3fs8/eoqokEolE4mBi9voAEolEIrFzJJFPJBKJA0wS+UQikTjAJJFPJBKJA0wS+UQikTjAJJFPJBKJA0y21weQOPgcOnpKT1x/214fRgL46mOfP6eq1+31cSR2jyTyiR3nxPW38Xd/8nN7fRgJ4N3fYZ7a62NI7C7JrkkkEokDTBL5RCKROMAkkU8kEokDTBL5RCKROMAkkU8kEokDTBL5RCKROMAkkU8kEokDTBL5RCKROMAkkU8kEokDTBL5RCKROMAkkU8kEokDTBL5RCKROMAkkU8kEokDTBL5xExE5IMiclZEvtjadkJEPikiX4r3x/fyGBOJxOokkU+sxoeAeya2vQf4tKreCXw6Pk8kEvuUJPKJmajqZ4AXJzbfC9wfH98PvG03jymRSGyMJPKJjXJaVZ+Nj58DTk97k4i8S0QeFJEHr7z0wu4dXSKR6JBEPrFpVFUBnfHafap6RlXPHDqaus0lEntFEvnERnleRG4EiPdn9/h4EonEKiSRT2yUB4B3xsfvBD62h8eSSCTWIIl8YiYi8kvAZ4FXicjTIvIDwPuAt4jIl4C74/NEIrFPyfb6ABL7F1X9vhkvvXlXDySRSGyaFMknEonEASaJfCKRSBxgkl2TSKyCrCMMUr/zx5FIbJYUyScSM1iPwNfvW+97E4ndJkXyiQOFn7o0a22MdJ9vRrTFpKg+sf9IIp+4qlmPqOuM90hL2L2uFPqaWdunjZ2EPrHfSCKfuOpYTdinCfoskW8jMhb6OoqvxV1mif86jymR2EuSyCeuGqYJ6aSATz73qym8gmkpeFvMjYyfzxL59phGktAn9idJ5BP7nrXEvf24FvW2ZTJLfI2AQxEDBumIuUSRnyb29Xiq430noU/sV5LIJ/Y1k6I5Tdwnhb0tvNr6gG8JvzGgUbUt4I0iKtASdmvCrRb32r7xOhZ4UXB+5bFNO/Y2s3z+RGK7SSKf2LfMEvhp4j4p7N63368dgYcg8tJE6YIFiKmQtcBnthZ67UT5QeAF78cCX99PK7xcH8fkRG9zLEnwEztIEvnEvsRPidjbj71qI+5tMXeuK/L1+3QizBYRxCg2KqyINPupBb7IlMyCMYo14887PxZ4cWObx/naApo8runfUVq/DCCJfWJnSCKf2HesJvCT0XsdubfF3fkg7OFew+e8doReRLBW0CzkyBgDIB2BL3KlsEpmPUZCNF9H8ZUzVD4KfCXxOMC3jrV9vGFjd6J3fCzd753EPrGdJJFP7Ctm+djtqLx+n/fjiN057Yi7d4pzHueC0DtfXxwUMTIWeQUwZJk0Nk2eQb9Q+rmjlzly47CiiCiqQqWG0hlGziJiAIOqNJZNffFxTlfYMvVEbxi1q+ZJ7BM7QRL5xL5hNQ++HRG3Bd77EMV7VaqqK+5V5XHOh22VbyL5YNUIWR6jeDu2aoyBXu6ZKxzzeUnfjihMhRWHiOLVUGnGyGRkJkckC8Lfsm3GF52Jid967Pp7yTizp01b7JPQJ7ZKEvnEvmSan11bNJMCX0fxVTkW96pyVKWnKh3eBbFXr3hVjAgmsxTOYkTIMoP2DdZAkUE/98znJYfzZebMMj0GWK0Q73EmpzQFQ9PDSB8PVE7IrDAsBa/hl0TluhaSidF7k5oZs3tEu2JfW0Ipqk9sF0nkE/ueyUnWZlLVh9dqgQ/i7htxL0cVVenCrXLBn/ceMYYst6jPMdZQ9MJpYC30cmW+qDiUDzhsL7NQvkRRLmLcKByLySnzOfL8MCZTFKF0lkFpMCLBLnJQVUpZjSd9pU7XtDF7xwoiGsTfCMaHNM4U1Se2myTyiX3Belazhm3a3Nfee7Bowq0qPeWoYjRylMMyCn2FKx2ucgDRqgn/9bPc4l2OiJDZGMVnIw7byxwenmNu6UXs8iXMaBDGzQuK/mGy+RH0obQZA1uQWQuEDJuqUkaj2i7SRuSNEZyX4M17wZowLwC67qg+CX1ioySRT+w7ZqUcdjx6P2HfuNqiCbda4MthSTksqcoK71wz8ep7BdYa3HwPCEKaZzBXVCxkyxwqLzJ/+Xmyl87CpYswHAJg5uaQQ4ugHmcyhkWf3MxhmzRKpSyVsvQMB1Uz4QtgjWCswdowH6BmnIVjTIjuPXSi+iT0ia2SRD6xr2lbNVBbNToWfK9NFkuwSvzYoimDyI8GwyDylUO9x2Qh6s6LHB+je2uFXq70s5IFc4W5y+fILjyPnv0a1YWLuOUBYgx2fo7s5IDMWHrFHEVxjMxUiGiM4mFUepaXK0bDqpnwFRFsZsjiTVXxNmTlhFJn4QtNs2+S0Ce2QhL5xFVLR+w1ZNWohslV7zwuCn1VVrhRiOYBrCrWWpwb2zdFLvSiVTM/ukRx+RycP8vwa88xOH+R8soAMULv6AJz3pP3+mQLxyjml8mMj8dAjOAdg+WS0aCkKsdjWBvmAvIiw3klzyEIvMerEBykYN8koU9sF0nkE5tCRO4BfpJQ+uWfqer7trK/dnGvWtDCdsEbDctIZ6AacuGBOLkaJli992i8BatmnEZZp2RK9MWDH++YMwN6Sy9hLr3I6Nw5lp49x5XnLjK8PMBkhvmlIabIsUePYE4sYn2J2Jg/72A0UgaDksFyyXBp2BH5LLPkvQznPD3N40ItC1gywEkU+CjqybpJbAdJ5BMbRkQs8DPAW4Cngc+JyAOq+vBOjWkEXPSug8URLBARbfLegdb9ytZOtcAbIyGN0pgQXWeGXgG9zNGTZbLBFfTSSwzPX2Dx7Eu89MxLDC+NyPrB5ukfW6C/tIwtRxjvwIZjG5UwHDpGg4rh0pDBUrCJ1Cs2s1S5DQLfz8MB9fNmYVYd0betm9qjt83xr132OJGYJIl8YjO8HnhMVZ8AEJGPAPcC2yry7Yh+8nmn9rupyxQYqngfInSLGIMYg7EWE6N9Yy0ms9jcRvvEkGdQ2IrcDbHDRfzSIuXlJZZeXGLp/DLD50uyI5be4YJqUKJlCepRkbBAygmjUilHjsHSiMHSkOHSoLGIRIS8VzS/OOpt9S18BzO+aMk49VLraD5+6RTNJzZCEvnEZrgZ+Grr+dPAN211p+upx15H73U2iqrgXLBcvA0CbzJLllmqzJDlGRpLUNbRvcksea8gL3LyIqPIDUWm5MaRuyFSDnHDEdWgxA0rqkuO8pJDcsGVwf4JqmxQsVRR5MtSGQ4dw0HJaDBitDxsRL6e7A3HMT5OWzrEjPPmbbR+wiKq9dk2icRqJJFP7Agi8i7gXQDHr7t1E58fC5kh+PLGR8GLlk0d9TZiHyc2nfP43JK7vBM5e2sRIxhrKfoFvX5OXljyXMgzJROPeAd+PAEgVpBcyI9Y8sOWfC4n6xeYXoEWfSpbUFaWshJGcQFWOSophyPK4agR+cyHUy0sxMqoKkdWOVxmyLzFuSDwzunUaH41UjSfWI0k8onN8AxwS+v5y+K2BlW9D7gP4NY7z6wRn49ZzwTstGhe1ZBldcZN1q04aQSbWVzlGhunP9+j6Of0epYil1hpMlYYMxaxlqyfUywUzF/fpzri6B0pmD8xT3F0ATO/gCvmGNk+o2HGsIRyFMsoVB5fObwLNwBvBOMt6j0u5uuHujqh5EKmZqIGfvtvCe2FsCmKT2yEJPKJzfA54E4ReQVB3N8O/NWdGqwr9iGat/XqI5T2pGWTaRMnN2trpMpdE9VnuaU336M/H0Q+y2gWM6lYNO9h5ucojh5i4bplAHzlKRYKFq4/Su/4UeTwEar+IYY6x7CyjCqagmjOuZDGuYr3pBNdTLRVClmjqteWjbXhwmcn95G8+cQ6SCKf2DCqWonIu4FPELTng6r60Hbtf1o0P8u2qfPKvUr8z2xjto1DRChtFVa2xgJlEEQ+RPEZRRHSJ+sKk85k+GKO7PAReieO4UclWT/YPvl8j/51x8mvO4U/epJB/xgD32NYGcqSpqyxMaGOjW358KY9CRzvxXSzgtayZSYnYBOJ9ZBEPrEpVPXjwMd3av/rEfoQuoaoN8/qPPMQ0RtDUxumyu2KladFL6PXs+R5qD6pKjgVRtkcVf8Q9shxsusGzBlDcWwZ9R471yc7fgxO3UB56ATL+RGWRz0GIxOLkY1LGYfsnSxecOJkcJ6R5RnGmnBrMmvGAh+qVCYRT2wfSeQT+5b1Cv1YE4N1I6IYGwXUGrLMr6ghkxe2sWrqDJbKZ4zoMewdwR4+SeZdWNm6HCwbej04cozq6PUsz5/iih5mqcoZlkJVBfsliHnI3gHQvE7bNCGjp5eT93KymL5prcHYULQsJOx0xb5O9zcS+s+mKD6xUZLIJ/Y167VurJVOc27xYMSEAmbONO3/6mycLDPkeagEaeJ+S28ZaJ/l4ih2oQIx2P4CUsYyw0Uf1z/E8sIpLhcnWCr7LI8yymo8Z5BlhryX45zHWNNE97bJy8/Ii6wj9FlmY+EyMxZzM/bbZ+l60vvEekgin9j3rCb0WJCmiNl4JaxVwZlQ2ybct7tCQZ4JWRZ6ukJo/DF0luWqR54fgj5UtqDoHcL4EhWDtwWjfIGl/AhX3CEWyx6D0lDF5iAmpnAWcdK3KrOmfn1d1qAR9ljDJstDVUprJUT0Nlx4xvbN7J6wk3+jRGIaSeQTVwWTQt95bYp906wW1dCow/vwofHq2NjAwxC7OQmjyrBsc4zM4TOhKnKy/DA2iryTYOcsuXmulH2WRhnDUohZklgrFEWsT28MeeWaTlTNAqh4IchyE0Q/G5cetk25hXH3KJFk1SS2RhL5xFVDHa2uN6o3rXrtbURCBF9bNRAi+dIZhlUWEjLVMLIFmVQY42OjbsvIZwxcwVKZN1G882GfWW7ICxuE3Rq8s90xs+C/WxsEPUTxponeQ0Qfa+vEi1Et8OP9dO8TibXYVyI/Pz+vx44d2+vDSADPPvvsOVW9bq+PYxprRfW12AMtG6f7+VBGIDyvm5CMKkHEoJpReWHks7AKVrTJvim9ZeQsyyPLMIp8fRxZFiJ5gCzTTnVMMdGGacR8LO7SygSqI3jTugCFY5aZwr6XVs2x4yf15pe9DIvDeIeoC6uGQ9/D8b8DErJdRYB45RJB4/P6HoiPCdvrgWZ8eWXvvnzr6LrU1mB4Eh43kYYinb9N+M8nGstlqAcf032bKqrdcST+vDPx56hkGU+9cIFzFy9N/WPsK5E/duwYf/2v//W9PowE8OM//uNP7fUxrMa0qL7zen3y2/YJVn9WGhsEwjnpPJRVqAYZonZh5BQj2vxiUCR495WhrISyCp9rOjsZIc8NIjRt/2rGqZJtwY+/JloWkrSOTUz3eMf7Wvl32CtuvPlWfvXXPsaR6kXmls+TL72EWbqEDJagHKEuCr6xiM0gy9C8B3mB5gU+66FZgcsKvMlRY/EmwxsbFqaJCUXgJFw8FUFbP22miXz79e1C1E/f3oi4b56L+niviIaLn1GHcSXGl5hqhKlGSDXCjAbIcAkGy+jyMn55Cbe4hFtaplpcploeUg1GuNG4BlLWz8kX5iiOHSY7cRx78hTf8nf/ycxj31cin0hslLXEHsBGb779evt9XmvLBajCeytnMUaxZizUzgvex7rxlTRWDYTIO4tnkxhD5umIfBhTGm997LlPeTzhwe9XgYcgaplUZNUAO1rGLF9Gli6jS4voKIo8IPUkSFEgVQW+AvUYVVRDBCuZx5M3+/WieGMRFTBB0MPUujbiLqwUddFVmg9s53dvCX8t7vVj4x3EexN/3dS/cpqbK8GVUFVoVaKjIX4wxC0PqBaXKReXKZeGVIMydjVTTBZKY0hmycoqdI1fo6pfEvnEgWA9Yt/e3n6/6Fisg4cvOBPFtrUjjRO0ztMR+FqY61ILtlVFsnOMZry/SWGvj6kdvbePd8UvlX0g8DW5H2KrUKJZlhfRpUX84iJ+MOyIvOQZpiyh70K068cTJiIG0xLrZkWzB28sxrsmqkd9R9xF3Z7YNm27po7egUbgRf345t34PhbBE+eQqoSqhLJCqwotS/xwRBWroJZLoxDJl+E/m81D/aWsP25nuRZJ5BMHirb4tQOc1SYq20XBXOwna/x44rO9v/o9tU1TB3Ni6toy0tSeaZ9/pjN52hJ6GX8+HP/q0fvkd9xrRJTMj8jKJWS4jA6W8YuLuMtXcMNuJG97BTiHaT4bUksxNhSgEwktU8Qg8V1toQ+RsqHR8/jHD+8f/2PvpOBP+vDt6D08bwu/hu2t/2ASMwFEfah26hV1DnVR4Msq3EZVY9NUw4pq6OJXttgiC72J6/9gZjyfMY0k8okDy0bEUFuWjRHQiQi6fSGoxb79q6GuklkLvdeusE87pklhb483+Xgz32k3EBTrosc8HDSecrW0jFseNo3STZGhzmFVQUL9Hmyo9okZhccupKoa70AkTJirIAiiwYuvhV5XuWrXgrvdYj9zonXaeyc8fMGPLwjaihDqW6xMqs6FCqZVqGZaC3z9d/RW8JXvFLQLk9izjyWJfOKap67gWJ8zbpVSvtNKAUM3jdOrrqgY2WYy331yrKtB3GsEsL7ClAMYDdDh2FOuosiLMZiWdyzWIplFshyyEsmyYFnEaF0bEVy/qLbZqUg+zAls7pjGO/ET9yFaUO1m09RCrz6UrXalxxjBOx03wYl1kTCWFMknEmswKfSzqF+fnOtqC3N7oneSaQK+mpW0X8V9jGJciVQVOhrhlwe4wZBqeUi5NByXdYiRqGQWPxxh8gytSnAF4txY1Ftpl2H3GjIuVdEpf4u1Mm3W9Q2aXwjr+batX11o89nJC0DYPmUCuD7eKRlAdcqtto4lRPdhe+gQNi26WP3Sk0Q+kYjU589qYt8+x7RlifpVov8263nP/hf2LsbXWSIlfjTCDcKkYTUoUa9N0TWTWXxeor0CrRzqXEvgY4ZNjOQnRbe2Z+rJ17bYTor7ZlIoN/KZ+tiacevPqo8BdRR+kdjBTFBMM0aYQ1jjeLw2t6nHECvaibWoyVb9j5VEPnFNo35lULVWn9lpTPan3ciK1KtN1NsIGrJFqrIzcehG4TYp8qaosKMS7buQ/ld70uv6o68U81poJ0V6pyybOnpvnreieBXTCL1ofUFSUNf8h1AJaaBxwURn9n09JaZDDwLTPI7t0Uh2TSKxCtOEfj2fmeRqFutNoxpzvmO7w6p7c6UPZZ+NxIyQuBJYo7fs65nskFK5QuqlK+J1FD9N4Hd6YVQt6N2N4yh+bN+41n2cMGYs9NPyYpuMKyNN1lG7qcyKY6nFXkJ20moT0UnkEwm6qZDrfW8iINFuCamAXYEPWSG2yQhpcrt9awJyyh+047W3bI76vr3ydVY039nfFiP7yQgeJkQ/RvFSL9QSwiIuxqt0VQzE+8ayidtiLYvpY1tBfFgxXV8wa4EXm635nzaJfCLRIgn4JqizYbw22SBB1GOqX5M1Ev+4tdc8y6KZIvC1HbHCf19HNL8dTN1vsxgrin1j14T7cEwaLJvGl49lpGu7xkg3om/aQpqZkfw4yg+f15Qnn0gkdgqBbs53M2EYI3eniBlPIq5nheZ452Mfu75vR/HTovma1eyLrdIpugbQyrAZR/TRf5+WEjR1p6tZMwZjNET0zQUgLiSrfwmsQhL5RCKxJWRC3GvqzJD2fTuyJ1ZeXI8Mtj14YPbjVcRd2Zo/L9QrbFuRt2oj7u2MoHYKZePLx3taN5Ug2O1qm90G7xMWUac0qWk+sxrbMishIh8UkbMi8sXWthMi8kkR+VK8P74dYyUSia2x0+drLebejVMAvdtgylIdvU+pOjnpzTevTYidYjq3rTJtX+0xJ49nIwSPfVLQx1k0IYpvZ9XEcUzt98/e93ZNPX8IuGdi23uAT6vqncCn4/NEIrH3fIjtPl9XmcyYFPhZud8rdildMV11YrUtthsQ9VBdZv23aWOsyNvfQkbPZOS+6nvXaUlti8ir6meAFyc23wvcHx/fD7xtO8ZK7DwPPfQQwGtExIvImfZrIvKjIvKYiDwqIm/dmyNMbIVtP1+bZcDjidZa2Lv2TasBxjpLFgTBXBktd1Mou+I7c1+riPZ6mSb2m0Unq9NRPzWr+vMTG2gascxgJz3506r6bHz8HHB62ptE5F3AuwCOHj26g4eTWC/XX389wGPA+fZ2EbkLeDvwGuAm4FMi8krVXSrgndhJ1nW+QvecvfmmG2fusOvFT0mT9CsvBNAS7Y54S0vUZ9glMwR3vWK+nvdNFj+bVlBgvSUS1hzLjJu7q+8Kv2zg18L2t1CZgmrd52rqa/ep6hlVPTM/P78bh5NYg+uuuw5gOOWle4GPqOpQVZ8kXAhev5vHlth5Vjtf4+vNOXvy+LHuaxPCrS2rpp1109oZsFIYO4ufWrnljdAj2ybwG4nq1/veUEc+/BmlLi082URmM0uraU2+rtPa2UmRf15EbgSI92d3cKzE7nAz8NXW86fjtsTVz5bOV/W+KX27Quin9CntLIiatc9G3FsWS2PX1GK/tUnVaXn3G7+NSx9Pqzff9HStyw3PWAC2U0umd9KueQB4J/C+eP+xHRwrsUHuv/9+rly5smL7m9/8Zl796ldvef/tn/THr7t1y/tL7DibPl+n2RNNVo0PEtwuuKXaEn3fjXCbVaHUUXtL3FsCv15hb1agTmHVImerpKuIaFOvZtaYTa9XbT/2TdOQmfuekTq5FbZF5EXkl4A3AqdE5GngHxD+s3xURH4AeAr43u0YK7E9vPOd79zMx54Bbmk9f1nctgJVvQ+4D+DWO89ssQh3YjvZsfPV+46A1yVyadVBb1a6xshfdYYEt2waLzbe2uLfeutsZ6l5fTWLpSP2ujKyn/KBescrxq/F3NQ9XWPLv3Ep5XFk34nm2x57I/TtfPlwb+zGo/1tEXlV/b4ZL715O/af2Dc8APyiiPwEYeL1TuAP9vaQEhtlR85XnbRjFO8VdR5vBDOxEKpukhHf3ClxMPbhBd+IvMFjO554HTG3o+pmwdJEpL+eblGqK/326b8Y6uMGhDg+TZ/XMFa3ibfRKgh/FPyNTM6KEXDaEfjGl6/LI6xCWvGaWMEjjzwC8I2E/8a/KSJfUNW3qupDIvJR4GGgAn4oZdYkml6r7fo1brwgSmx3WyP2tdBP1GjWmBaoTQRv8Vg8phFiQadG0s1uVhH7qdUqWwLfWew07b3YOOa4eXfbljHejQXeVUHgXdUV+E5UHzs9xSJl3fo1KwV8o1ZOEvnECr7+678e4I9V9czka6r6XuC9u35Qif2NdidZNUbx6hQ3ctjMdEoQ15bNVH9aBC8WZzKcyXGS4dTiWnLVCKqMG2nXwt7UlhFptsFYvNvR/7TIvd42WXChtnJE6j2ZcKGqLzZ1JK8O68sg8L4cR/Cte6Zk28yiTqVc+Wdan3WTRD6RSGyNFYuhupE7gKvar3V7mLazTYIHn+FNiOCdZFSaUZHh1TTiLChWBBSsjH9M1gIv6MokUGlbJK10TG1lyYQcmI43P21SVlGs+BX2kfEO66uxwLsg9uJdrPEThH5W+qTEKpXtKpQhcver1pdfjSTyiURiC4yb3rZTJ9WF3qTexewa55v68kHc40Rkq+Sw1mV3RfAmx5mcSjNKzXE6tmtCJF03s1a0FcHXET6EALtTYqCtq+KZbLfeFvgVK1snJmRFwkVERAHXyqCJHrwvsdUIcSXiSurmKnVzFHw1notolxyu68qbyUJl7QqUswuYTSOJfCKR2BJtX7kpMdxYNopzisnN+LXY37WJ4luosagJVk0lOaVGofcWr9FuESUT6Qh8+1i62S6xEmScHIUo/I1Ah3uvQfDbAt/16bsib+oerq3tgmJ9hXXDIPDVCFMNY/SunV8szQWus1MTo/i6O1S4hRWvUyJ4022mMosk8olEYltoL3qqJ13d0CG54MsQybvSdVa+ai1+0JTfrX34IO45I98VeSMeDBg1+NgsG4l2ibqpE7HdnHjFi0XoRvN1bkxtC2nLtllREFkABV/PCdRpk96FSdZqhCkHSBlEfmqDlLbox1LE9cSrybotADsVKVvP18M1KfJVVeFcNynEGEO/38fa7k845xyDwQA/sTovz3P6/f6KfY9GI4bDlRUB5ubmyLLun1tVWV5eXnEs1lrm5uZWTKw451a8N5HYS6TWrmi/NCWG46Srr4Jd4/Pai2+Je3vVa8yo8cbiTUZlikbghy6ndAanBiOKNYIBKlGsuijwHqOuEXmZTOlsly5uXQQ6dXBaUbxXE62b8QWiPfGqasiNhs/Uq1012DR1BC+jKPLtc7a2ZiYxtcALYm0UehsmXTPbXDzbHaGapiFrcM2JvKryW7/1WzzxxBOd7fPz8/z6r/86r3jFKzrbn3rqKe69914WFxc729/0pjfxsz/7s5iJK+pP//RP84EPfKCzTUR4//vfz913393ZvrS0xNve9jaefPLJzvZbb72VBx54gIWFhc72L3/5y/zGb/xGs3w8kdgXrMiRD8LtvUdLxZWK5A43cnjXmnSNi6Gaz7WsGoel1JyRyxhUGaUzaOiiR26DyFvjxpOeMTXR1L74zL6xdQOP8GtACbn4468SBN5p17JRHefpGA2/Jrxqp5yBqGJcnGwdDZDhAMoR6qrwnrpmvLVgLCsQARuEXVoCL6bqePArJl/XyLK55kQe4PLly7z4YrfS6nA45MiRI5w4caKz/cKFC1y4cGFFCYDBYMDJkyenpjFN7ltE6PV6K/bd7/enHsvx48c5fvw4hw4d6mw/f75TFDKR2D/ouI9rE82XIZIH0FKbFbBTWwHGqLSecB1pwchljJxlWFpGLkTV1oRrihWP8zZO1kY/XMcpi2bK8o26LII3FhHbrKat0y+VrJl89WpiNE9H6AF8TKE06sni9nCBiZkz1QjKEYwG6GjURPJqbRDx+oBq7YjfvWnrZ23w5qPQ11E9TNg1nV8Fe1NqOJFIXAtMi5qjVaNlrGFTTbT+g7FPXdelqSN5yah8xsiHKH5QGkZVLfKKzw2ZtRTajuR9k75oXDk1mg+raC1GLc7k2Cj6zo5lMIh7EPmqjui1a9sYCembThQf7SIgCLyrkDJYNToMIq/OhWDQWiTP64WyIaJvl1WunxsTBF6CuJvMNH+zJqIXaQQ+TbwmEoldoxOpx0jeZBIeu3Y0P3FhiCI/zqoJUfygNAxLYRiyEMmsIAJVJvgs5s1LLCHgK0w1wrphzEV3U8fwNgMbLz5iYtSfN1G885ZKDZUPgu98HcmH3RhR1HiskW7uvnqMK6GqgriPRvjBAJwLFlEe5FZqy0YFME0UrxKj+MaTX7nytXnc9HdNnnwikdglZrX189VkRvoEsWepGhvsGrVU3jKsLKNSGIxgOAqBf5GDNRInYscWiomrTK0bYsphEFs/juabUgkmQ2wGeRR5kyG2rjdDEHUN+698FHov0bYJh2sFNBMyrzhTjVfSalzVWg4bgfeDYZhcNgbTKl+g1kaxj3+D2nqZMvlqMoOvphUtM826gtVIIp9IJLZMOzKvW//Vdo3kEh67kD/fRuoCW9Gq8WKaCddRZVgeCctDGI6it69CZqFygq+zXWJ+unEjTDnEjpaQcjQuH+A1Nr+2YDJ8XtSD42wRInkZT7pWaii9pawMlRecJ0TzceLXGgUMmTH01HQsI/EuRvJDdDjCD6NdYy14j7E2PJ+0uOpMmRWTr6YRe/We9oKotcS9Jol8IpHYNpo68u2OUKXC3JQ3m3qFp4DJol0TIvnSBx9+VAaBH43qImiGIg/CW4u8IWTU2GoUBH64HDJbXDlOX6wj5bzAuF7Yl7HYrByLPHUUHwR+WAllJVQujAdBVzMrgCezhioLqZ3x4BDn0KrEj0b40Qi3PAjllGNqtpQVmjvEOcg8EH14DTsXm3UmX7srX2urppUn39Tfn00S+UQisbeIQY3gJcNJRulDyuSoDCI/GnmGw3HkOyotlRt75IInc3Hx0WiALC+igyWIk57hTUFAKQqkV2FEUJsjedVE1V4NzltKZxiUhsFIGFVQORqRNwLBWjfkmYYMn3YJBF9BVaJlhRuOcMMR6nWc815m4IqmWqeoB7FjXz4ea13eIKx8nShvUPvw61wQlUQ+seN89bHPn3v3d5intml3p4Bz27Svq2Xs7Rz35du0ny3TsR1amTXOGypnGFUhih8OPcOha4p3VT1tJkNFNOTGuyqI/PIiungZv7yMDgZozMcXESTPkV6BcQ4xFlP0Q8plFHmnEn9BBIFfGoa5gKrSRuStgTwPk7+9XKi8jMstxAJkOIeWZRD5sgrH4HMks/iywlQOdRWixco/Su3LyziCb692Hf/xWumXa5BEPrHjqOp127UvEXlwWgnk3WCvxt7L77wexOuqdsHUz7TsBjEmdH0yoQhZpYaRE8oKytIzGoWbEbA2ZL+0u+jVE64yGqCDZfziIu7yFdxgiB+VjcibXoGdC6vUTZYh/bkwQUtt1cQoPgr80kAZDJRR6ZssUWuFft9grdDL48QsMTNGtRF4X1b4UYUbjJpsI5MZtFeEXxduPF8wXjZMJ1tmshLl2JNvpVEyXs07iyTyiURiT2hyvePEqxc7XoTko8hXSjlyVJXDiFBVHudCJA/jejXGVchogF9ewl2+QnnpCuXiAF+WaJx4tb2CrKyi4PehGpcAVoJgD+Nk79JAWVz0DAaO0cjjYyifFxavFmss8z1pyi2MSxt4vHP4OpIfVfjKYwvFFBV2VIbibNG/b3z5FX+XccpkuI/plvV7Uu2a7aPX6/G6172O5eXlzvbbb7993ftQVR5//HH+8A//sLN9eXl5ap2bROIgs2JZvmm1+8PiVKhcyDwMVolSlR5jBBczdMY56x7jfVhlOhzil5cprywxvHiZ0ZUB1aBEvcdklny+FyLqPMMcGmDLYfNzwMVfEMPSMBjB8rJncbFiealkNHJ45xER8p5FNSfPhGFpo10j4wVJXlHn8GWFG1Vx/DCGHVV458aRfNuXn/qHmtIoZFpZgzVIIr8GCwsLfMd3fAej0aiz/ZWvfOW6O7MAfO5zn+Py5cudbWVZriiXkFiT+67BsffyO+8I0uSEmyZ1UCUUBfM+LEBysR59VYZIWutt7f7XaPDVqyFaDvFLy5SXlxheWmZ4aYlyuWysEjcKNWSy+R46HDV59Crh10PpYl7+QFlacly5PGJ5ccgoRuMmMxSjIJm9nm0mgH2TXTP25P2oxI2C0DeNU0bBwvGjEq1a3aFis/PZf6vZUftaq10hifyeMlnZMrE2qrpngrdXY+/ld94QW/7/3C4IFjJanAu9YJ3zWGtiUbP47rq8sHdIVcFohBsMKReDwC9fHASRdx6Th0qOtsioloZoGdMrY5Gx2o8flrC07FlcLFleHLJ4eUA5KhuRd/0CYw3D+ZyyzGL+fCu7Rj1aOXwVGqS4UdVaN+CaSL6+NbX4vWnq688qQLjyF1DKrkkkElcDzQRiq7+qBpszVCOOZYpFO6tqJbbgM+rAhdx0NxxRDUpGiyMGLw0oF0P0bguHMUKxUAafvm5aEvvJVmopnTAcwWDgGCwFkR8sDhguD1HvyYocgLzIGI08lVMqZ5rqlM3Ea7y5UYUrg+AbKyGqr1yI4usyy7Vls7He3BOZNqt/eIO73jgico+IPCoij4nIe3Z6vEQisXm283yti5NtlHqRU7OfGeUSIJb4bactDkZUgxHlUkk1cIwulowulpSDCheja/XjEsd1JUrnDWVlGFXKcOgYLI8YLA0ZLC4zGgwZDYYMF5cZLY8oRxVV5eofAuPWgHUlTlc3LPdUQ4crPdWw6pZYjpOvnYbe7W5Rs5qcb4IdFXkRscDPAN8G3AV8n4jctZNjJg4eIvI9IvKQiHgROTPx2o9GQXpURN66Q+PvWqAiIh8UkbMi8sXWthMi8kkR+VK8P75DY2/7+erLcanhmk7TEGjErG7dZ6Qrbu2oNSwGGpdsEaIYqg9euAvi6ipPtVThlj1u2cdSx76xTqReHBWzeio1VA7KMmTzlMOK0WDEaDCkXB5QDUdUZUVVVri4H+da1hFaK36nUbmvHL50sb9tuNW5+/Xka/ibtLpETdheK4q5TWOV+cGdjuRfDzymqk+o6gj4CHDvDo+ZOHh8Efhu4DPtjVGA3g68BrgHeH8Uqm1jDwKVDxG+S5v3AJ9W1TuBT8fnO8G2nK+TkXct9NPmoHRC6GvCGimJCz9Dbrgx0mwL2ZdNH6dOhsosURRrsPm4ABh2vADLq6FyQlUpVeVDpB5F2tWP40Wk7mHb2bf6xl+vI3lXuqZeT7vMskarphH2TjQ/W9BX+0WzGjst8jcDX209fzpuaxCRd4nIgyLy4NLS0g4fTmKvmBahrhdVfURVH53YnwM+BVwH/LKqPgk8RhCq7WRXAxVV/Qzw4sTme4H74+P7gbft0PBrnq/QPWfPX7i46cF0hbBpWMEqijFhdam1YfWntQZjzVj846SriK5IQazTDG3Pkh2Jt/kMmxlskWHyHMlzyDK8zcICLG/iRC9dUY7dq+pjrTPqjBlfbCRebGq7pd2/tivw3aYp4av7lkXT+rWjrde3yI578muhqvep6hlVPTM/P7/Xh5PYOT7Eygh1KywDvwr8T6r6l+O2qaK0RdYlfDvMaVV9Nj5+Dji9y+N3aJ+zJ48f29hn665R7cYhXptI2OIboQ9rpARrJQi8CfehEuTYoWjSCMVgrMUWGVkvI+tb8oWM4khOsZBRHCrI+gXZfA/pFWiWN6WNQ6MQQku/9uSuCMba5mYzG47Fhl8W1ihWtBPJr/jOboZQtyPzaZ/biMCv4t/vdHbNM8Atrecvi9sS1xiq+hkRua29TURuJ1gh1wFfB7wAjCY++mOq+rFdOcirAFVVEdnc7/a12dXztWtXhM5Ogsc2kbyEpt3WYK1io+BbK00E3exLQjclU2Rk/ZxioUB9mPgUI+RzOb3DfYpDfWy/hykKyHp4m+MJIq8KRgSbhV8NWZGT94omTz3vFfGWkWU2XHxMWJAVIvk6W2bCyrEbjKXXMeG6Eetmp0X+c8CdIvIKwn+WtwN/dYfHTFw93Af8oKp+SUS+CfhHqvqmdX62T7At/rKIXFHVX2dnRGk/BCrPi8iNqvqsiNwInN2hcbZ2vk4rpDWDtkipD3XYw6Srx4jHGiXLgqBnmWnqz2S5aaL4WjqV0GlJsiyULogiD5DP1SJf0DsyR77QJ5ufg14fnxehabiOm4SLQJYZ8l5G0S+a1bJhtWtB0S/o9XOKIpQ8zqwP9lKrp2y7YqQxoeFItw7N+ouLbQc7KvKqWonIu4FPEAo0fFBVH9rJMXeKWQsUNsLkClmzgfoTBw0ROQR8C/DLrb9LL7723cD/NuVjN7Uevxw4BvwK8E9E5AJwJ/AH23yo+yFQeQB4J/C+eL8jv2w2e77qBsWqbdWoH2eliHeYGMlnVrEGskzI87HIWxOjeaPjSF4EbI4UPexcn3xhLi5esk3dmqyfUxyZJz+8gJmfQ/pzaFbgTd7q3RrHKyy9fo4rQ935vClpkNObL+jPFfR6ljwnHKd4pP4ekaa5hxVM+3lza7X1q+v3sLL5+Haw44uhVPXjwMd3epyd4vLly/zKr/wKk5PCb3zjG3n729++7tIGb3jDG3jLW97S2ba0tMQv/MIvcP78+W073qsIA1xU1T83+YKq/irBbwdARL4L+ClgAfhNEfmCqr4VeEZE/h/g7wK/CPx3qrqtZ8puByoi8kvAG4FTIvI08A8I4v5REfkB4Cnge3dq/O08XyVfx7nRZNf4VgqlxwpkNohulgneB08+y8YTns0xG4tmOVIU2H6P/HCY28v6eac4WX54nuzwIczCAtrr47MiVL9EYjMQKHKh17P054pQDsGaVt2ajP5cQX8uo9+39HIorMeKG3vyUbxNZrF57OhklKwX2viZLDb9qJt2txcy1RfLDZRLWQ9pxesalGXJn/3Zn62oMXPHHXesex8iwi233MKrXvWqzvalpSWKYkpN6WsAVb0kIk+KyPeo6i9LuFp+o6r+0ZT3/hrwa/VzETkuIj1VHQIfAP4b4F5VfXiHjnXXAhVV/b4ZL715N8bfKpNWjZki9Ctqseg4EjbqYnaNRpEP9kld3sVaIc9CCz5BQ8s+sajNMEWBmZsjK+Mq19pusRbTK8jm57AL80h/Hl/UIm9RFax4Mqv0CqHft1SHcsSE1a3OheJoWW7p9zMWFnL6faGXQ25905mq+X7WBpEvMrJeqF1j8/DcZGECt24K0mn7t0MzLUnkE7vCjAj1rwE/KyL/M5AT0hNXiPwUvh74gIh4wi+C9+2UwCfWR1u4jRWM7Yq75LKmPSmEEgVWPJnxQeRtjORVMDI9kvfGojZHiz4yN4+tQjnhpqSvteEC0O8hhw6j/Tk07+NNjhcbfj0YbYm8wfkMaw2jnhtbPpmh17PMzxvm+0IvV3LjycSFkgbhD9FMANsiI58LFxobn5s8xxQ5Js9iNB++jJhgSdWRvYisvBhukiTyiV1hlQh1w2mVqvq7wDds7YgS24ZMn3A1maywbNqTkg1eu3YNceLVKnkm5HnsbyohsreGxpNXYplim0NeIL1+6PyUZU3rP7E25MX3+sGL782HKN7mIf1SwYrSy5R+AYfmBSOWYW6oKouPAp7H+YH5vtAvoMg91jgM42qWYi1iLTbPyPpFkxNvi/A8CH0WJorjgqymEudGPfmW1bUaSeQTG0ZEbgF+npCvrcB9qvqTe3tUiT2lJfTNLRdMFic1MwmTkKYV5beFPq76DHaNxxrfiHzRulAUmZBZsDG7RjGoWLzN0KIP/VGYRs2zYAFBzLwJFwHtz+GL3tiqQTB4MvHkmaeXG5wPK2vzXKicoj58PWuEIocih/me0ss8uXHjFEqiVZOPs3zqRVC2CNtMrwhtCPMcal/e2tghSsbNzWdQ728jJJFPbIYK+B9V9T+KyGHg8yLyyVmWyalTp/S2227b1QNMTOfzn//8ue1sx9imyRaBIOitSF7yIO5iu31L24JWT14aM86wCZOhQMyAybIwQWpa2TVeTMh3z3rYoo8CkmXjbJc4MUsWLgS+zo83oQKGiGKNIzeOfm4AgzXB+/d+fIExBnILeab0c09hHZkETz7uKFxQiiDmthfm29RrsG/6eRD62qqxNtTPEQPGI2pm2vKbLWkASeQTmyCuvnw2Pr4sIo8QVoFOFfnbbruNBx98cBePMDELEdmuhuoAqDCOQOsxYrTejuTt3Dht0GR2Qujr1nlhMtXGyDozSpEplRvvO89i2mKceA3HYEIqpM3QvEAgiHodgougWR58+6xoUifrC4dBySSItsvDMRkj5Jl0esmaaBPlmVJkjsJWWHFNjryaLKRy5kHMs7keYqQR+WyuHyL5ogi/LIwdR/OstGvqVNNOCYSJIgUzu0q1SCKf2BJxFet/Avz+Hh9KYq+RcZQu1mCMQfIgQsYYbDEuEFYvMJIJy8Z4h7EOaxyZ8eSZoXB142ooosgbGUfyioQm4FkRFlWJCeWH690aG6pN2nAh8DYLaZdi4hyAw0qI5H1cnZrZ2J2qVfbYEH5BZMZTZOH9VlxzsUEErSP5fp/MOSTm6pvMNittJc+DnZTnrTTKllj7ceOQrUTwNUnkE5smLmj6l8DfUtVLE6+9C3gXwK233roHR7d9qCoPfe0Sd1x/iH6+rUUuDwQqJghrXORjakumZdmIFcS2FwEJktXZJe3oNOTJZ+LJrSfPQo/Xej1KnimFDUJbV6Csa8JrzLKB7uJFNTYKcB4KksWsmqb5dp2bbzwFFSJK5U2sZ9OdOLbiMUbpmWDVZDFHvhknev+m3wudn6xdmeEzGcnD1DnXdgTfjurrgmfrJYl8YlOISE4Q+F+Ii5c6xJZ19wGcOXNmp2qt7Arv/7eP83984lHectdpfu6/PrP2B64pJrNnTLOc39hQCRLA5ibUhMnCoqDOas/6sxpa+YVVryFS7mUW76V5W/Dpg2dv25G8SLBgMsWINCmNGiNllRjtSx3FS2ulqyfD4aUCU4u+6bb1a75tGDczVYziw8Wm/jVRr7yl38eoomXW9HA1RRFey4twMbC2Ka4mtcrXK2ebcsSzI/r1rsJPIp/YMHHh0j8HHlHVn9jr49lJKuf5Z7/zBACffPh5vnJ+iVtPpmqpHaInP06PNOMJ1phJYwuDycerQOsVn9JOeq8rUWrViHxuHC4LFw2AzCi5jamLrawWrSN5taFWTExprI/PSxai+dgkpBFXjVUvcdi4+tUYxaiPvWYnRD6WFc5MuBAZ8aDh14wai88LTNFHqjJk/9QTwCKxvHHMrDE2ePjxe7fpljjuCn3oatWqP+91nKM/g2tS5A8dOsTx493mOvPz81y+fJkLFy50tl++fJljx46R53lne6/X48KFC1PLGkzuW0QYDocr9r24uDj1WA4fPszFixcpy7KzfXLV7R7yBuAdwJ+IyBfitr8XV4YeKB559jIXlkr+h7e8kp/45J/x7770Au84+fK9Pqz9SacIV6zb4muxn7BqzITA17vQMJGZRZEvrEMRrI8TolIvQAqFzES0EVkvFkyOTFZQF2lEWJGweKot9GjTL1apC6XJVJEPxxCLqHUuMjK2i/ICin74DdCaABYb7Zw626f+eRKD+KYqZ+yaVdeab1s0HZtmnZbNNSfyIsK3fdu34dxKE+zjH/841nY9V1XlHe94x4r3ZlnGBz7wgRXbVZUf/MEfXLH90Ucf5fHHH1+x/du//dtXHIsxhg9/+MMr3uvqvpB7jKr+eyZ/px9Qfv/JUFfov/xPb+EXf/8rfO7JF3nHX0wi36Hlq7dF3FhBXYjmbWZiFF9bNjZ48tG2UWgKlVlfkmUlmckpbIyuozVjRcOEp2mlLkK0a8Lq1VAiQJsLSN0gXGvbpuXHQ1xpKx5VRybgavsFaf6X12JfR/JGfFgE1c7wkazJ8MGH6pdSVSuyfGitdA0RedMsthF6X4XWgO02ghBtm7YnP6OGfZtrTuQhCHSWrfzqzrmp4t/r9abuZzSaLH0++/3e+6nvn3Uss/ad2F3+6OmXuPnYHKeP9HntzUf40+curf2haxRppVKaeqLV6or8eDHR3hEZ/xLW2HjDO4x6LBW5VDgTastkEiTdAJmpyMQ1kfRYxC1eFGmLc7z4aIzmqcWert9eC3dIsgffek1VOiGNSEjzrH8BNHtoZfiY+iKTjfu4NnVqTCuQbNfTi31q68qc43aBOmHZ+HH1zmb02VyTIp9IrJfHz17hztOHAHj1DUf4N4++wLAKE4KJFu3IuFVX3ljB5GHpvs1tE8lLU6CrZa1o3a/VYX1FZkoysWCh8qYZxopvUhfbkXTIl7dIy2JpfHlkiuDHV1SbCL1OqexYPsKKC0Id/Uv8NdF48jZHshCZi7GIK8crb+mWZRavIK2I3Ecf3tXRu19h02hnMtZ3hH4WSeQTiRl4rzxx7grfcvtJAF5942GcVx47e4XX3HR0j49u/9AIpqlTKceNtzW27et49SbWeKkvDO1JUO+wrsTaCoujkFHctelkw6ywS2KGTagGvVLka3tG25F8vDDU1D1j0XG2Sz1mO1Ju3tf5GwjOZIjJERsLlokE/127YizxuRrfaHxdv6fOqgm58r4j9jCO5Dt+fJp4TSQ2xzMXlxmUntuvD5H8150K90+dX0oivwZ1Zo34ln3TsmrGb+wuhhL1zeSr1QovhpwKF1Maw0eULOaztwkVJf2KZIhaqBvBbwn9iuMOCt95PivDpnOBqfdpLN6GcgZGJCzKilZUfa8avHiJvV+b3rA+RPHqHN45tHJde4Z2ho2OLwxrkEQ+kZjB4y+EbKbbrwvifsuJOQC+8uLSzM9cq4gxUzPNjBn78eF90kT7XasmCl4UReMrrJTB3hAQbWfD1JF81y6pJzinxbU6Ed13I/quFbPyy00+1RXvDb8KLM60JDWKfP2d0JAaGbZV4ReMjidS1VXjblnx3keh95WLJRLGtk3zN0sTr4nE5njqfBDz206FvPjD/ZwTC0US+VWYtGXUuY5HP36fWVGaOES6sQ2gd1hT4dViRRBRPBPWSltkiZOaUwSvbclMRvWdY49ZNfV9e/u097b3H5qXGCRO/hKnbMIFzTZiLzoj+q6za2Ikr953BN47DXn3k43Cm8ezLZsk8onEDL52cZnCGk4tjLOlbjkxz1fOJ5FfL/ViqK74T88FqW0Lo1WwbLzDSkWdVtMW3yaa1ilC32LSkpkm+FOPZYNtmsKvAo2VLTUcswXxoY5O86PFu5AzLxO/Ylp+fBD4cVZNbdl4DBY6+fPh88mTTyQ2xddeGnDjsX6z2hLg1hPzfOGrF1b5VGJd1HVjavVrC1adThm9+bAoKYhoKEdgGpsGJsR9cvXolIi9a91sbrnH5EVg7NLHVM5Yc6z5evFxMyE7hWaVq69XukbBd7Wg+47oT/u+09ie/lKJxAHkaxeXufFov7PtZcfneO6lAX4bqgMedDo+/Hpa2WkdnfvG2qjLDxuNKZOxsUh435R6LjFjprm1xbeVGx/SKOuMm63JYNsCCouiYiOTuoSCsU3Vy3EtndZFqZ5EbWXWtFe6NuNMFiZb54rXJPKJxAyevbjMTcfmOttuONKndMqLS2mx2maYatW0otFmEnEimq+zbhqp1lUi6QlBh7Got8U9fG7jEjjNyulcUGoxj5O64ZdG+AUy7ZfF5N9AdZxRM5ldE97aXhzl15x43ZLIi8j3iMhDIuJF5MzEaz8qIo+JyKMi8tatjJNI7DaV8zx/echNR7sif/pI8Oefe2mwF4e1HRzfq3N22gRshybNcJy9EoReW49989pqzBL1znumyN9q+52c7G1bRuNxJ9I0W7dZNg2tC9u0siXqtbFsNlNffquR/BeB7wY+094oIncBbwdeQ2jU/H4RSUsEE1cNZy8PcV658VjXrjl9JDx//tJVK/LL7KNzdqq3rD6ufI0CPyHuqwn9aqIOQdjr2yy6Qq4rxH3N79SyhOpofrzD1Y8PaEXxEwXJNsmWRF5VH1HVR6e8dC/wEVUdquqTwGPA67cyViKxmzz70jLASrsmevTPXb0iP9irc3aynd1q1IuHmudoR+Cn7n8d4r5eNirsYXwz9fGq1H+LaStZ1xpvncUKd8qTvxn4auv503HbCkTkXSLyoIg8uLSUUtMS+4Nnox0zOfF63aEeRuD5q9eumcWmztnzFy7O3OHURherCLz4sT3TbFMP0YNfbzS/G0yzamB2WubU7fUFa4cn8ddMoRSRTwE3THnpx1T1Y1s9gHYHoZtuuimlLCT2BWcvDQE4fbgr8pk1nDrU29eR/N13381zzz23Yvt73/vebdl/+5x93WvvUqnT/XS6Zzy1XV07vz1OHurE642Qq0ejcxRy5acjqjOjecFvOYum3s/kmOPHflcvOtNWGE9jTZFX1bs3Mf4zwC2t5y+L2xKJq4JzV4ZkRjg6l6947YajfZ6LF4H9yKc+9anNfGzbztm2qKvTyfVJnfetWMgzUR99vOgp1KTR+Lhe3Vo3424WSa0h9NvJWh2ZumO3Lwa7G8vulF3zAPB2EemJyCuAO4E/2KGxEolt54XLQ04eKjoLoWpOH+kfRLtm8+dsk+sdKieON0+2tVuZDrhiP5F2Qa+V9g0rnk+KqLSydLabyX2vNVfQobUWYCM0zdFbNwgZS2v5/1ta8Soi3wX8FHAd8Jsi8gVVfauqPiQiHwUeBirgh1R1Sj/yRGJ/cu7KkOsOT28Wc/pIjwe//OIuH9G2cUxEnmbbzlntFN+aXMTjvSJThF3bC3/aFRV1HN0Hi2U8Th2lC9oqRiZNP9d2VcianYyaJ62ZJs1zQvQ3cwydssxGmr/fOA011uJfxwTvlkReVX8N+LUZr70X2B4TMJHYZc5dGXHq0HSRP3Wox4WlktJ5cnvVrSe8qKpnpr2wmXNWNEal9YpNWimAblLYW42ouy90Kip2ovcmmg/piCLSCPyk0AMdse8MsUqdmnV9zxle+2oTwOE7tCL3NcS+Fu726uCx2GtstiKYzI4bpxuJ+fezv99V9z80kdgNXrg8nCnyJ+P2FxfTqlcgFN1SP3WCtRb7lZG8NrVaxhvbj/1Y8JsVruMyBzARObe9+9atZvp62PXfmv1M2X9n7NYxrZuJGvsiQdhNFqL40BvXhi5btdBb2/SKVbP6coZUoCyRmEBVOb8426657lBoCnHuyrBZHHXtoohzsTfpeCm+dzq7vs/k9noCtmXXSG3b1Fk2dXZNzLTpWDS1mE+pMLlR73u9TAp/vW2cEaRTLzar71QQG5ubW9s0PK8vkOG5wRYZktn43ij0q+w2iXwiMcFLyyWl01XtGgiWzrVOXR+97je62sRq57W2J999U5NO2fW1g2WjYmIlR1Zk16xl2Ww3k78Upj2ehdblhuvm3tY2LRFrMTeZxRZBomubxhYZJs8xeY7kOWpD8bOZJRNIIp9IrOCFyyE98lSM2CdpRP7y/k2j3D002DWtSH4WM2vJt9rfdbYRJ1/rRtuqqNTiahqhjzuPRyNrRs7rXY26ngh8hZXT9uDXEvtYkbK5NwbJc0yeYYocU1bYKsx918Kf9Yso9BmS5ZDleLu6jCeRTyQmeOFKEO9Zds3Jll2TIAj8lIVQxoTMdLHdtD/q1n/1BOMaohsi+65d07zWiuInI/hZk63bYeGsyKzZQDaNSphA1tg1qo7iyXIkGyF5ju0VaEvg1XvEGLK5Hnauh+n3oSjQvMCbfNWJ5STyicQEdSR/3Qy75lAvo5cZzqeJ1+A9O9exXcLEoOCtYFsNvGvLoe7xKiJdoZ9VnXIFdbQ8e+ETjIV4q5k10/a5uQ9LE7WrsYixaCPuFeQlUhSYKrQAtN6DEbwNvrzJDLZXkM3PYfo9pNfHZwXOFknkE4mNUHvtszx5EeHUoV6yawDaE5/QEnODcSHitrkh62Utr9l0JhibNEAJed8q3fzvlfZKu8rjake2feI+uc/VxD4cl2l+XahoKMYgipcMjGLUN0KPzdHcNRPYpt2k21p8XgHRsukVmPk5ZG4eLfr4vI+zBaulUCaRT2wYEfkg8JeAs6r62r0+nu3m3JUhuZ1e0qDm1KGisXWuaZTx4qXW5GDWy8JqTO+xedzWz7FFHvzmzCJ5Ns4QsbYV6dZ14GPlSKmbccgKwZ/WmHsnxH3l1+42+u50egobqYXeG9tpBaiElcFqc7wqps4gamXiGABrMdkIdbETlrVIr8AuLCBzc/j+HC7vU9kiZdcktp0PAT8N/PweH8eO8MLlIScXelNLGtScOtTjawevtMEmiPJSp/3leRT0ApONPeWsn4dJw17wm01RIHmYOJQ8R00GJmva5DX537XYNy3zxp2X6sYc4SjaXaB2eflP2+Nvi/0MoW/PCBj1qI4n+AVC1o212DxHi6Ir8kUPmZtD5xbwvQVcPkdlClRTJJ/YRlT1MyJy214fx06xWkmDmlOHevzJMy/t0hHtc2J+tylybD8nL8Pfzle+G93P9bBzfWy/h0ShJ8vA5pBlaJahNkdthjcWjf1RfeyXChK210K/iSh+KxeAWRO20ypkChIXUoXCaqKKN4KqYOrjZjwfIWIwTZZNhmQZ5D2oyvG4YpCiQHvz+LnDuGKOMutTSvLkE3uAiLwLeBfArbfeusdHszFeuDzk+rVE/nDB+cUR3uuqEf81gcQovijI5vpo5ZDMNtkhklmyXoGNIm/64Uavj/T6IVrNe1Hgc7zNQ8aIsTiTjwU/Ru9e7IoIfjfsGl2lUVZH4OsFWyjEhVsisRm5jn+VWClxIqgpMSKosRhjY2pkBlWF+KpJLVVr0bwIXnxvgapYoLR9Kp1tK0IS+cQO0a45fubMmauqT8C5K0PuuvHIqu85udDDeeXicsmJhen59NcKai0UBabfw5YlAGY4atL+go2TBZtmfg7T7yNzc0Hg8x5a9NGswGcF3mZB4G3WEXgfhdGL7Qh7t9XexHHtUtWWUEit7dF3/fo6qlckir00Fy0jLn6vDGNLMBZxJZIViCtR7xCvaFw0pVmBz/uUxQJlNkdpe5Q+u3oi+Wefffbcj//4jz+1Tbs7BZzbpn1dDeNu99gv36b9XFV4r5y/MuLUmpF8vep1eE2L/B8++sS5+W9/13acswflvNmrsWeer/tK5FX1uu3al4g8OKva3k6yV+Pu9dgHhZeWSyo/u6RBzanWgqhXnj68G4e2L9muc/ZaPW92Y+xUhTKxYUTkl4DPAq8SkadF5Af2+pi2i/OLq5c0qEn1axJXC/sqkk9cHajq9+31MewUL1xefSFUTapfk7haOMiR/H3X2Lh7PfaBYBzJry7yx+ZyrJFUv2b7uFbPmx0f+8CKfMzuuGbG3euxDwrno/1ycg27xhjh5EKRRH6buFbPm90Y+8CKfCKxGc5dGWIEjs+vnTFz6lAvefKJfU8S+USixbkrI04sFNh1LHA6dbiXIvnEvudAibyIfI+IPCQiXkTOTLz2oyLymIg8KiJv3aHx74n7f0xE3rMTY7TG+qCInBWRL7a2nRCRT4rIl+L98Z08hoPI+Suhbs16OHWoaOydxObYy3P2WjlfD5TIA18Evhv4THujiNwFvB14DXAP8H6RVdYob4K4v58Bvg24C/i+OO5O8SHCd2nzHuDTqnon8On4PLEBzl0Zcurw+hY3XXeoxwtXht1m1ImNsifn7LV0vh4okVfVR1T10Skv3Qt8RFWHqvok8Bjw+m0e/vXAY6r6hKqOgI/EcXcEVf0M8OLE5nuB++Pj+4G37dT4B5Xzi6MNRPI9RpXn8rDa4aM6uOzhOXvNnK8HSuRX4Wbgq63nT8dtV9sYa3FaVZ+Nj58DTu/y+Fc95y4P10yfrGnaAKZc+Z1gp8+na+Z8veoWQ4nIp4Abprz0Y6r6sd0+nv2KqqqIJB9hAyyPHIsjt2b6ZE171evXbVtBjoNHOmfXZifP16tO5FX17k187Bngltbzl8Vt28lujLEWz4vIjar6rIjcCJzd5fGvatZb0qBmLPIpkl+NfXrOXjPn67Vi1zwAvF1EeiLyCuBO4A+2eYzPAXeKyCtEpCBMGj2wzWOsxQPAO+PjdwIpStoAa/V2naSeoE0ivyPs9Dl7zZyvB0rkReS7RORp4JuB3xSRTwCo6kPAR4GHgd8CfkhV3XaOraoV8G7gE8AjwEfjuDvCjCJh7wPeIiJfAu6OzxPr5HwU65PrFPkT8wUiyZPfCnt1zl5L5+tVZ9eshqr+GvBrM157L/DeHR7/48DHd3KM1lizioS9eTfGP4icbyL59dk1mTWcmC94IeXKb5q9PGevlfP1QEXyicRWeKGO5NeZQgl1aYMUySf2L0nkE4nI+SsjFgrLXLH+NTenDheNzZNI7EeSyCcSkbDadf1RPKQiZYn9TxL5RCJyfnHIyQ32a012TWK/k0Q+kYicvzJad2ZNzalDPZZGjqVRKm2Q2J8kkU8kImcvD7l+w3ZNXdogWTaJ/UkS+UQCGFaOFxdHnD7S39Dnag//hWTZJPYpSeQTCeCFuKDp9JENRvILqbRBYn+TRD6RIFg1ANdvOJJPpQ0S+5sk8okEcPbSAGDDnny9cCp58on9ShL5RAJ4/lJt12wski8yw7H5nBeuDHbisBKJLZNEPpEAzl4ekBnhxPzG8uQBbjjS57mXkl2T2J8kkU9sit1sgrwbPH8ppE8aIxv+7I1H+zx3aXkHjiqR2DpJ5BMbZg+aIO84z18acN0GrZqaG47O8dxLya5J7E8OVKnhxK7RNEEGEJG6CfLD6/nwz/ybxxiUjswYMitkRsisIbdCZgz93HBioeDkQo9ThwtOH+5vKsLeCGcvDXn5yflNffbGo33OXRkxrBy9bP3FzTaKqnJxqeS5SwOevzTgyrBiaRhW2y6XHuc9zoNXxavivPI33ng7h/v5jh1TYv+TRD6xGaY1Qf6m9htE5F3AuwBuvfXWzoc//NmneP7yAF1nR8uFwnLn6cP8hZcf51tfdT1/8etOkNnt/RF69vKA17/ixKY+e8PR8Avg7KUht5zY3IViGqrKw89e4pMPP8+DX77AF7/2EheXynV91ggYEd7xzS9PIn+Nk0Q+sSOo6n3AfQBnzpzpyPnv/b3QJ8F5pfKeyimVU8r4eGlUcWFpxPkrI56/POTxs1d45NlLfPj3nuKf//snufFon3d888v5/m95xYbKAs9ieeS4sFQ2Yr1Rboyfe/alwbaIvPfK//fHX+MD/+4JHn72EiJw141HuOc1N3DH9Ye48egcp4/0ODqXM1dY5ouMudySWcGIYAREdvaXT+LqIYl8YjNsSxNkawRrLL11/i9cGlV85s/O8Qu//xT/+Lce5Rd+7yu897teyxtfdf1Gh+7wzMUwaXrzsblNfX4s8luffH3s7BXe8y//mAefusAd1x/iH77ttdzz2hvW3Xc2kZgkiXxiMzRNkAni/nbgr+70oPNFxj2vvYF7XnsDv//Eef7+xx7i+z/0Of723a/kh990x6aj16cvLAHwsuObE/kbjobPbXXy9dOPPM9//5EvkFnhH/+Vb+Sv/PmX7fhcROLgk0Q+sWFUtRKRugmyBT64k02Qp/FNX3eSj737Dfzor/4JP/HJP+PKsOJHv+3VmxL6JpLfpMgf6mUc7mU8uwWR/1d/8iw/9Iv/kbtuOsJ97zjDTZv8VZFITJJEPrEpdrMJ8iz6ueUnvvd1HO5n3PeZJzg+X/A33nj7hvfzzIVlMiNcf3hznjyEydfN2jW/+/g5fuQjf8ifu+UYH/6Bb2Jhvf5VIrEO0v+mxFWNiPC//uXX8OLiiH/8iT/l6288vGGP/ukLy9x0bA67BWvklhPzfPXFjYv8cy8N+JFf+kNefnKBf/H9r08Cn9h20mKoxFWPSPCwX3X6MH/nV/6Yi0sbKxb2zMXlTU+61tx6Yp6nzi+i680LJaRI/u3/9wssjRz/9L/68xydS6mOie0niXziQDBfZPxf3/s6LiyO+PEHNjY98MyF5U378TW3nZxnceQ21NT7lx98ms8+cZ6//5fu4o7rD29p/ERiFknkEweG19x0lL/5rXfw61/4Gr/3xPl1fWZpVPHcpQEv32J++8tPLQDw1PnFdb3//JUh7/34I7z+thN875lb1v5AIrFJksgnDhR/8423c9PRPv/wNx/G+7WtkyfPBVH+uusObWnc204Gkf/y+aV1vf+f/rvHuTwoee93vTalSSZ2lCTyiQNFP7f8nXtexRefucTH/mjt9VlPvBBE/vbrF7Y07s1x4vYr64jkz10Z8uHfe4p7/9zN3Hk62TSJnSWJfOLAce/rbuauG4/wf3/6Mdwa0fzjL1xBZByJb5YiM9x8bI4nzq0t8j/3mScYVZ53v+mOLY2ZSKyHJPKJA4cxwo+8+Q6ePLfIb/zx11Z97xMvLHLzsTn6+dZr4Lzy9CEefe7yqu85f2XIz3/2Kb7zdTdx+xYtokRiPSSRTxxI/ou7buBVpw/zU7/92Kre/J8+d4lXbpNlcteNR3j8hSsMSjfzPT/3O08yqBw/nKL4xC6RRD5xIDFGePeb7uCxs1f4V198bup7lkYVj529wmtvProtY9510xG8MjOaf3FxxM9/9st85zfelFImE7tGEvnEgeXbv+FGbr9ugZ/67S9NjeYf/tolvMI3bJfI3xj28/Czl6a+/s9+5wmWS8ePvDlF8YndI4l84sBijfDDb7qTP33uMp94aGU0/4WvXgS2T+RfdnyOw/2MP4r7bXNhccT9v/tlvuMbbkxRfGJXSSKfONB85+tu4utOLfCTn14ZzX/28fPcdnJ+081CJjFG+KZXnOR3H1+5EOvnfucJlkrHj7z5zm0ZK5FYL0nkEwcaa4QffvMd/Olzl/nXD4+j+VHl+b0nzvOGO05t63hvuOMkX3lxia++OF4U9cLlIf/iPwQvfrsmeROJ9ZJEPnHg+c5vvIlXnFrg//zXf8awCpkvv/2nz7M4ctx91+ltHetbYwXMj//Js822n/rtLzGsHH/r7hTFJ3afJPKJA09mDf/LX/p6Hjt7hZ/81JdQVT74H77M9Yd7/GfbHMnfdmqBv/Dy4/zSH3yFUeV58Msv8uHfe4p3/MWXb7l0QiKxGVLx6sQ1wZtefZrvPfMy3v9vH+d3Hz/PF756kf/9ba8ls9sf5/zQt97Of/uhB/mbv/B5/vArF3nZ8Tn+zj2v3vZxEon1kCL5xDXDe7/rG/j+N9zGi4sjfvA/v52/9vpbd2ScN736ND/0rbfzbx59gesO97j/+1/PodQMJLFHyEaaHCQSm+HMmTP64IMP7vVh7Dql82RGNt1gfCcQkc+r6pm9Po7E7pHCi0Rih8h3wApKJDZK+l+YSCQSB5gk8olEInGASZ58YscRkReAp3Zg16eAczuw34M83stV9bpt3mdiH5NEPnHVIiIP7uYk4kEfL3EwSXZNIpFIHGCSyCcSicQBJol84mrmvjReIrE6yZNPJBKJA0yK5BOJROIAk0Q+cVUhIt8jIg+JiBeRMxOv/aiIPCYij4rIW7dxzHviPh8Tkfds135b+/+giJwVkS+2tp0QkU+KyJfi/fHtHjdxbZBEPnG18UXgu4HPtDeKyF3A24HXAPcA7xcRu9XB4j5+Bvg24C7g++JY28mHCMfc5j3Ap1X1TuDT8XkisWGSyCeuKlT1EVV9dMpL9wIfUdWhqj4JPAa8fhuGfD3wmKo+oaoj4CNxrG1DVT8DvDix+V7g/vj4fuBt2zlm4tohiXzioHAz8NXW86fjtv2637U4rap1e6nngO1tYZW4ZkhVKBP7DhH5FHDDlJd+TFU/ttvHs9eoqopISoNLbIok8ol9h6revYmPPQPc0nr+srhtq+zUftfieRG5UVWfFZEbgbO7MGbiAJLsmsRB4QHg7SLSE5FXAHcCf7AN+/0ccKeIvEJECsLk7gPbsN+1eAB4Z3z8TuCa+wWT2B6SyCeuKkTku0TkaeCbgd8UkU8AqOpDwEeBh4HfAn5IVd1Wx1PVCng38AngEeCjcaxtQ0R+Cfgs8CoReVpEfgB4H/AWEfkScHd8nkhsmLTiNZFIJA4wKZJPJBKJA0wS+UQikTjAJJFPJBKJA0wS+UQikTjAJJFPJBKJA0wS+UQikTjAJJFPJBKJA0wS+UQikTjA/P8Xe5BMavyEVAAAAABJRU5ErkJggg==\n"},"metadata":{"needs_background":"light"}}],"source":["img = filters.odog(x, y, sigma=((2, 2), (2, 4)), orientation=80)\n","\n","# % Apply filter\n","filt_img = filters.apply(stimulus, img)\n","\n","# Plot stimulus\n","plt.subplot(1, 3, 1)\n","plt.imshow(stimulus, cmap=\"gray\", extent=visextent)\n","\n","# Plot filter + horizontal meridian\n","plt.subplot(2, 3, 2)\n","plt.imshow(img, extent=visextent, cmap=\"coolwarm\")\n","plt.subplot(2, 3, 5)\n","plt.plot(x[int(img.shape[0] / 2)], img[int(img.shape[0] / 2), ...])\n","\n","# Plot filtered image\n","plt.subplot(1, 3, 3)\n","plt.imshow(filt_img, cmap=\"coolwarm\", extent=visextent)"]}],"nbformat":4,"nbformat_minor":2,"metadata":{"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.5"},"orig_nbformat":2,"kernelspec":{"name":"python385jvsc74a57bd02092d3496cec6223f7568d467ac3bd437caae5364fbbef5eee989d574fa6e351","display_name":"Python 3.8.5 64-bit ('lightness_models': pyenv)"}}} \ No newline at end of file diff --git a/demo/replication_flodog_params.py b/demo/replication_flodog_params.py deleted file mode 100644 index 58eac43..0000000 --- a/demo/replication_flodog_params.py +++ /dev/null @@ -1,46 +0,0 @@ -# %% -# Third party libraries -import matplotlib.pyplot as plt -import numpy as np -from PIL import Image - -# Import local module -import multyscale - -# %% Load example stimulus -stimulus = np.asarray(Image.open("example_stimulus.png").convert("L")) -stimulus = (stimulus - stimulus.min()) / (stimulus.max() - stimulus.min()) - -# %% Parameters of image -shape = stimulus.shape # filtershape in pixels -# visual extent, same convention as pyplot: -visextent = (-16, 16, -16, 16) - -# %% Create model -model = multyscale.models.FLODOG_RHS2007(shape, visextent) - -# %% Weighted filter outputs -filter_outputs = model.bank.apply(stimulus) -weighted_outputs = model.weight_outputs(filter_outputs) - -# %% Parameterized normalization -model.spatial_window_scalar = 4 -model.sdmix = 0.5 -model.scale_norm_weights = multyscale.normalization.scale_norm_weights_gaussian( - len(model.scale_weights), model.sdmix -) -model.normalization_weights = multyscale.normalization.create_normalization_weights( - 6, 7, model.scale_norm_weights, model.orientation_norm_weights -) -model.window_sigmas = np.broadcast_to(np.array(model.center_sigmas)[None, ..., None], (6, 7, 2)) - -# %% -output_4_05 = model.normalize_outputs(weighted_outputs).sum((0, 1)) - -# %% -plt.subplot(2, 1, 1) -plt.imshow(output_4_05, extent=visextent) -plt.subplot(2, 1, 2) -plt.plot(output_4_05[512, :]) - -# %% diff --git a/demo/replication_lodog_param.py b/demo/replication_lodog_param.py deleted file mode 100644 index c2ac116..0000000 --- a/demo/replication_lodog_param.py +++ /dev/null @@ -1,61 +0,0 @@ -# %% -# Third party libraries -import matplotlib.pyplot as plt -import numpy as np -from cycler import cycler -from PIL import Image - -# Import local module -from multyscale import models - -# %% Load example stimulus -stimulus = np.asarray(Image.open("example_stimulus.png").convert("L")) - -# %% Parameters of image -shape = stimulus.shape # filtershape in pixels -# visual extent, same convention as pyplot: -visextent = (-16, 16, -16, 16) - -# %% Create models -ODOG = models.ODOG_RHS2007(shape, visextent) -LODOG = models.LODOG_RHS2007(shape, visextent) - -# %% -output_odog = ODOG.apply(stimulus) -filters_output = LODOG.bank.apply(stimulus) -weighted_outputs = LODOG.weight_outputs(filters_output) -output_odog2 = ODOG.normalize_outputs(weighted_outputs).sum((0, 1)) - - -# %% -plt.plot(output_odog[512, :] / 32, label="ODOG") -np.allclose(output_odog2, output_odog) - -# %% -LODOG.window_sigma = 1 -LODOG.window_sigmas = np.ones(shape=(6, 7, 2)) * LODOG.window_sigma -output_lodog1 = LODOG.normalize_outputs(weighted_outputs).sum((0, 1)) - -LODOG.window_sigma = 2 -LODOG.window_sigmas = np.ones(shape=(6, 7, 2)) * LODOG.window_sigma -output_lodog2 = LODOG.normalize_outputs(weighted_outputs).sum((0, 1)) - -LODOG.window_sigma = 4 -LODOG.window_sigmas = np.ones(shape=(6, 7, 2)) * LODOG.window_sigma -output_lodog4 = LODOG.normalize_outputs(weighted_outputs).sum((0, 1)) - -# %% -scale = 32 -linestyle_cycler = cycler("linestyle", ["-", ":", "-.", "--"]) -f = plt.figure(figsize=(5, 5)) -plt.rc("axes", prop_cycle=linestyle_cycler) -plt.plot(output_odog[512, :] / scale, label="ODOG") -plt.plot(output_lodog4[512, :] / scale * 2, label="LODOG 4deg") -plt.plot(output_lodog2[512, :] / scale * 2, label="LODOG 2deg") -plt.plot(output_lodog1[512, :] / scale * 4.5, label="LODOG 1deg") -plt.ylim(-9, 9) -plt.yticks(np.arange(-9, 9, step=3)) -plt.grid(True, axis="y") -plt.legend() -plt.savefig("RHS_Fig_3_LODOG_params.pdf", bbox_inches=0, transparent=False) -# %% diff --git a/demo/run_flodog.ipynb b/demo/run_flodog.ipynb deleted file mode 100644 index 6986566..0000000 --- a/demo/run_flodog.ipynb +++ /dev/null @@ -1,300 +0,0 @@ -{ - "metadata": { - "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.5" - }, - "orig_nbformat": 2, - "kernelspec": { - "name": "python385jvsc74a57bd02092d3496cec6223f7568d467ac3bd437caae5364fbbef5eee989d574fa6e351", - "display_name": "Python 3.8.5 64-bit ('lightness_models': pyenv)" - } - }, - "nbformat": 4, - "nbformat_minor": 2, - "cells": [ - { - "source": [ - "# How-to: Run an existing model" - ], - "cell_type": "markdown", - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Third party libraries\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "from PIL import Image\n", - "\n", - "# Import local module\n", - "import multyscale" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# %% Load example stimulus\n", - "stimulus = np.asarray(Image.open(\"example_stimulus.png\").convert(\"L\"))\n", - "\n", - "# %% Parameters of image\n", - "shape = stimulus.shape # filtershape in pixels\n", - "# visual extent, same convention as pyplot:\n", - "visextent = (-16, 16, -16, 16)" - ] - }, - { - "source": [ - "## Integrated run" - ], - "cell_type": "markdown", - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "model = multyscale.models.FLODOG_RHS2007(shape, visextent)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "output = model.apply(stimulus)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "[]" - ] - }, - "metadata": {}, - "execution_count": 7 - }, - { - "output_type": "display_data", - "data": { - "text/plain": "
", - "image/svg+xml": "\n\n\n \n \n \n \n 2021-05-23T21:08:14.336031\n image/svg+xml\n \n \n Matplotlib v3.4.2, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "image/png": "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\n" - }, - "metadata": { - "needs_background": "light" - } - } - ], - "source": [ - "plt.subplot(1, 3, 1)\n", - "plt.imshow(stimulus, cmap=\"gray\", extent=visextent)\n", - "plt.subplot(1, 3, 2)\n", - "plt.imshow(output, cmap=\"coolwarm\", extent=visextent)\n", - "plt.subplot(1, 3, 3)\n", - "plt.plot(output[512, 250:750])" - ] - }, - { - "source": [ - "## Step-wise execution" - ], - "cell_type": "markdown", - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "output_type": "display_data", - "data": { - "text/plain": "
", - "image/svg+xml": "\n\n\n \n \n \n \n 2021-05-23T21:12:59.462471\n image/svg+xml\n \n \n Matplotlib v3.4.2, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "image/png": "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\n" - }, - "metadata": { - "needs_background": "light" - } - } - ], - "source": [ - "filters_output = model.bank.apply(stimulus)\n", - "\n", - "for i in range(filters_output.shape[0]):\n", - " for j in range(filters_output.shape[1]):\n", - " plt.subplot(\n", - " filters_output.shape[0],\n", - " filters_output.shape[1],\n", - " i * filters_output.shape[0] + ((j + i) * 1) + 1,\n", - " )\n", - " plt.imshow(filters_output[i, j, ...], cmap=\"coolwarm\", extent=visextent)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "output_type": "display_data", - "data": { - "text/plain": "
", - "image/svg+xml": "\n\n\n \n \n \n \n 2021-05-23T21:13:39.021849\n image/svg+xml\n \n \n Matplotlib v3.4.2, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "image/png": "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\n" - }, - "metadata": { - "needs_background": "light" - } - } - ], - "source": [ - "weighted_outputs = model.weight_outputs(filters_output)\n", - "\n", - "for i in range(weighted_outputs.shape[0]):\n", - " for j in range(weighted_outputs.shape[1]):\n", - " plt.subplot(\n", - " weighted_outputs.shape[0],\n", - " weighted_outputs.shape[1],\n", - " i * weighted_outputs.shape[0] + ((j + i) * 1) + 1,\n", - " )\n", - " plt.imshow(weighted_outputs[i, j, ...], cmap=\"coolwarm\", extent=visextent)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "output_type": "display_data", - "data": { - "text/plain": "
", - "image/svg+xml": "\n\n\n \n \n \n \n 2021-05-23T21:14:08.576625\n image/svg+xml\n \n \n Matplotlib v3.4.2, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "image/png": "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\n" - }, - "metadata": { - "needs_background": "light" - } - } - ], - "source": [ - "normalized_outputs = model.normalize_outputs(weighted_outputs)\n", - "\n", - "for i in range(normalized_outputs.shape[0]):\n", - " for j in range(normalized_outputs.shape[1]):\n", - " plt.subplot(\n", - " normalized_outputs.shape[0],\n", - " normalized_outputs.shape[1],\n", - " i * normalized_outputs.shape[0] + ((j + i) * 1) + 1,\n", - " )\n", - " plt.imshow(normalized_outputs[i, j, ...], cmap=\"coolwarm\", extent=visextent)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "[]" - ] - }, - "metadata": {}, - "execution_count": 11 - }, - { - "output_type": "display_data", - "data": { - "text/plain": "
", - "image/svg+xml": "\n\n\n \n \n \n \n 2021-05-23T21:14:16.976875\n image/svg+xml\n \n \n Matplotlib v3.4.2, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "image/png": "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\n" - }, - "metadata": { - "needs_background": "light" - } - } - ], - "source": [ - "output = normalized_outputs.sum((0,1))\n", - "\n", - "plt.subplot(1, 3, 1)\n", - "plt.imshow(stimulus, cmap=\"gray\", extent=visextent)\n", - "plt.subplot(1, 3, 2)\n", - "plt.imshow(output, cmap=\"coolwarm\", extent=visextent)\n", - "plt.subplot(1, 3, 3)\n", - "plt.plot(output[512, 250:750])" - ] - }, - { - "source": [ - "## Non-default parameters" - ], - "cell_type": "markdown", - "metadata": {} - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "model.spatial_window_scalar = 3\n", - "model.sdmix = .75\n", - "model.scale_norm_weights = multyscale.normalization.scale_norm_weights_gaussian(\n", - " len(model.scale_weights), model.sdmix\n", - ")\n", - "model.normalization_weights = multyscale.normalization.create_normalization_weights(\n", - " 6, 7, model.scale_norm_weights, model.orientation_norm_weights\n", - ")\n", - "model.window_sigmas = np.broadcast_to(\n", - " np.array(model.center_sigmas)[None, ..., None], (6, 7, 2)\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ouput = model.apply(stimulus)\n", - "\n", - "plt.subplot(1, 3, 1)\n", - "plt.imshow(stimulus, cmap=\"gray\", extent=visextent)\n", - "plt.subplot(1, 3, 2)\n", - "plt.imshow(output, cmap=\"coolwarm\", extent=visextent)\n", - "plt.subplot(1, 3, 3)\n", - "plt.plot(output[512, 250:750])" - ] - } - ] -} \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index d4bb2cb..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000..590f3c7 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,96 @@ +# Book settings +# Learn more at https://jupyterbook.org/customize/config.html + +title: multyscale +author: Joris Vincent +logo: logo.png + +# Cache execution of notebooks +# See https://jupyterbook.org/content/execute.html +execute: + execute_notebooks: cache + timeout: 300 + +# Define the name of the latex output file for PDF builds +#latex: +# latex_documents: +# targetname: multyscale.tex + +# Add a bibtex file so that we can create citations +#bibtex_bibfiles: +# - references.bib + +# Information about where the book exists on the web +repository: + url: https://github.com/computational-psychology/multyscale # Online location of your book + path_to_book: docs # Optional path to your book, relative to the repository root + #branch: dev_docs # Which branch of the repository should be used when creating links (optional) + +# Add GitHub buttons to your book +# See https://jupyterbook.org/customize/config.html#add-a-link-to-your-repository +html: + use_issues_button: true + use_repository_button: true + +#launch_buttons: +# notebook_interface: "jupyterlab" # The interface interactive links will activate ["classic", "jupyterlab"] +# binderhub_url: "https://mybinder.org" + +sphinx: + extra_extensions: + - sphinx.ext.intersphinx + - sphinx.ext.autosummary # generate summary tables of functions in modules + - sphinx.ext.napoleon # recognize NumPy style docstrings + - sphinx.ext.viewcode # add links to source code in API reference + - hoverxref.extension + + config: + nb_custom_formats: + .py: + - jupytext.reads + - fmt: py:percent + exclude_patterns: ['_build', '_templates'] + suppress_warnings: ["etoc.toctree"] + #autosummary_generate: True # autosummary generates module-level .rst files? + add_module_names: False # Don't include module names in autosummary tables + autosummary_imported_members: False + autosummary_ignore_module_all: False + autodoc_default_options: { + "members": True, # Include module/class members. + "member-order": 'bysource', # Order members as in source file. + } + # templates_path: ['_templates'] # Path(s) that contain templates, relative to this config + intersphinx_mapping: + python: + - 'https://docs.python.org/3/' + - null + numpy [stable]: + - 'https://numpy.org/doc/stable/' + - null + matplotlib [stable]: + - 'https://matplotlib.org/stable/' + - null + pandas [latest?]: + - 'https://pandas.pydata.org/docs/' + - null + scipy [latest]: + - 'https://docs.scipy.org/doc/scipy/' + - null + + # Hoverxref Extension + hoverxref_auto_ref: True + hoverxref_intersphinx: [ + "python", + "numpy", + "matplotlib", + "scipy", + ] + hoverxref_domains: ["py", "numpy", "matplotlib", "scipy"] + hoverxref_role_types: { + "hoverxref": "modal", + "ref": "modal", # for hoverxref_auto_ref config + "mod": "tooltip", + "class": "tooltip", + "func": "tooltip", + "obj": "tooltip" + } \ No newline at end of file diff --git a/docs/_toc.yml b/docs/_toc.yml new file mode 100644 index 0000000..940731b --- /dev/null +++ b/docs/_toc.yml @@ -0,0 +1,41 @@ +# Table of contents +# Learn more at https://jupyterbook.org/customize/toc.html + +format: jb-book +root: index +parts: + - caption: Quickstart with `multyscale` + numbered: False + chapters: + - file: quickstart/installation + - file: quickstart/quickstart + - caption: Overiew + chapters: + - file: topic_guides/overview + - file: topic_guides/history + - caption: Filtering + numbered: True + chapters: + - file: filtering/filters + - file: filtering/multiscale + - file: filtering/filterbanks + - caption: Normalization + numbered: True + chapters: + - file: normalization/normalization_ODOG + - file: normalization/normalization_LODOG + sections: + - file: normalization/explore_LODOG_parameter + - file: normalization/normalization_FLODOG + sections: + - file: normalization/explore_FLODOG_parameters + - file: normalization/normalization_generalized + # - caption: Decoding + # numbered: False + - caption: Reference + chapters: + - file: reference/api.md + - caption: Get in touch + chapters: + - file: contributing/get_in_touch + - file: contributing/contribute \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index f3fe729..0000000 --- a/docs/conf.py +++ /dev/null @@ -1,76 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import os -import sys - -sys.path.insert(0, os.path.abspath("../multyscale/")) - - -# -- Project information ----------------------------------------------------- - -project = "multyscale" -copyright = "2021, Joris Vincent" -author = "Joris Vincent" - -# The full version, including alpha/beta/rc tags -release = "0.1.0" - - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.napoleon", - "sphinx.ext.intersphinx", - "sphinx_tabs.tabs", - "nbsphinx", - "nbsphinx_link", -] -autodoc_typehints = "description" -napoleon_google_docstring = False -napoleon_numpy_docstring = True -napoleon_include_init_with_doc = True -napoleon_use_param = True -napoleon_use_keyword = True - -intersphinx_mapping = { - "python": ("https://docs.python.org/", None), - "numpy": ("http://docs.scipy.org/doc/numpy/", None), -} - -# Add any paths that contain templates here, relative to this directory. -# templates_path = ["_templates"] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -import sphinx_rtd_theme - -html_theme = "sphinx_rtd_theme" -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ["_static"] diff --git a/docs/contributing/contribute.md b/docs/contributing/contribute.md new file mode 100644 index 0000000..8aa0b69 --- /dev/null +++ b/docs/contributing/contribute.md @@ -0,0 +1,6 @@ +# Contribute to `multyscale` + +```{toctree} +dev_environ +docs +``` diff --git a/docs/contributing/dev_environ.md b/docs/contributing/dev_environ.md new file mode 100644 index 0000000..67d1342 --- /dev/null +++ b/docs/contributing/dev_environ.md @@ -0,0 +1,21 @@ +# Setting up a development environment + +1. **Fork** the [GitHub repository](https://github.com/computational-psychology/multyscale/) +2. **Clone** the fork repository to your local machine +3. **Install**`multyscale` with the development requirements `pip install -e ".[dev]"` + - Recommended to create an *editable* installation using + - `".[dev,docs]"` to also edit and build the documentation + +The `multyscale` project uses a couple of development tools +to work towards more consistent code quality: + +- `pytest` for unit and integration tests +- `pyupgrade` for possible syntax improvements using newer language features +- `black` for consistent code formatting +- `flake8` for all kinds of linting + +These tools get installed as part of the `[dev]` extra dependencies. +Additionally, to run these more consistently, one can install: + +- `nox` for automatically running tests across different Python versions +- `precommit` for automatically running formatters and linters diff --git a/docs/contributing/docs.md b/docs/contributing/docs.md new file mode 100644 index 0000000..1810930 --- /dev/null +++ b/docs/contributing/docs.md @@ -0,0 +1,52 @@ +# Contribute documentation + +## How the documentation is organized +All documentation lives in the `docs` subdirectory, +and consists of a collection of Markdown (`.md`) files, +specifically in the [MyST Markdown](https://jupyterbook.org/en/stable/content/myst.html) flavor +(as well as some ReStructured Text). + + +## Building the documentation +`multyscale`'s documentation is build using [Jupyter Book](https://jupyterbook.org/en/stable/intro.html) +which is installed as part of the `[docs]` extra dependencies. +To compile the documentation locally (from the toplevel directory): +``` +jupyter-book build --all docs/ +``` +which will then provide an output message on how to view the locally-built documentation. + +## Executable content +Some pages are pure (MyST) Markdown files; +others (e.g. the tutorials) are executable notebooks, +either in the same MyST format, +or in `.py` Python files. +Both of these are automatically executed and converted +to Jupyter Notebook style documents +using [JupyText](https://jupytext.readthedocs.io/en/latest/). + +The MyST Notebooks can contain both Markdown syntax, +as well as `code-cell` blocks which are executed during the build +and their output is "woven" into the resulting page. +They also have a YAML frontmatter specifying how they should be converted. + +The `.py`-files have a similar structure, +although here all cells are specified with the +["percent format"](https://jupytext.readthedocs.io/en/latest/formats-scripts.html#the-percent-format) +`# %%` demarking the beginning of a new cell. +Markdown cells starting with `# %% [markdown]` will be interpreted entirely +as containing only markdown, no executable code. + + +## Contributing back to `multyscale` + +0. **Edit** documentation +1. **Commit & Push** changes to your fork + - We use [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) messages; + for documentation please start your commit message(s) with `docs: ...` +2. **Pull request** from your fork to our repository + - GitHub Actions will automatically run tests and linters + - If linters fail, run `black`, `pyupgrade` and `flake8` -- + either separately or all together through `pre-commit`: + `pre-commit run --all-files` +3. Changes will be reviewed by one of the maintainers \ No newline at end of file diff --git a/docs/contributing/get_in_touch.md b/docs/contributing/get_in_touch.md new file mode 100644 index 0000000..8c8cbc7 --- /dev/null +++ b/docs/contributing/get_in_touch.md @@ -0,0 +1,5 @@ +# Getting in touch + +```{toctree} +Ask a question +`````` \ No newline at end of file diff --git a/docs/cookbook/cookbook.rst b/docs/cookbook/cookbook.rst deleted file mode 100644 index 1ec6f35..0000000 --- a/docs/cookbook/cookbook.rst +++ /dev/null @@ -1,5 +0,0 @@ -How-To Guides (Cookbook) -========================= - -.. toctree:: - howto_run_flodog \ No newline at end of file diff --git a/docs/cookbook/howto_run_flodog.nblink b/docs/cookbook/howto_run_flodog.nblink deleted file mode 100644 index ddc0b57..0000000 --- a/docs/cookbook/howto_run_flodog.nblink +++ /dev/null @@ -1,3 +0,0 @@ -{ - "path": "../../demo/run_flodog.ipynb" -} \ No newline at end of file diff --git a/docs/filtering/example_stimulus.npy b/docs/filtering/example_stimulus.npy new file mode 100644 index 0000000..a602b2d Binary files /dev/null and b/docs/filtering/example_stimulus.npy differ diff --git a/demo/example_stimulus.png b/docs/filtering/example_stimulus.png similarity index 100% rename from demo/example_stimulus.png rename to docs/filtering/example_stimulus.png diff --git a/docs/filtering/example_stimulus_mask.npy b/docs/filtering/example_stimulus_mask.npy new file mode 100644 index 0000000..0ea9b61 Binary files /dev/null and b/docs/filtering/example_stimulus_mask.npy differ diff --git a/docs/filtering/filterbanks.py b/docs/filtering/filterbanks.py new file mode 100644 index 0000000..abc3469 --- /dev/null +++ b/docs/filtering/filterbanks.py @@ -0,0 +1,186 @@ +# %% [markdown] +# # Bank of filters + +# %% Setup +# Third party libraries +import matplotlib.pyplot as plt +import numpy as np + +# Import local module +import multyscale + +# %% [markdown] +# ## Example stimulus +# The example stimulus used for this exploration +# is a version of White's (1979) classic illusion, +# as also used by Robinson, Hammon, & de Sa (2007) as `WE_thick`. +# +# This stimulus is provided here +# as an NumPy `.npy` file, +# so it can be loaded in directly as a NumPy ndarray. +# +# The image of $1024 \times 1024$ pixels represent $32° \times 32°$ of the visual field; +# if centered, the visual extent of this stimulus subtends +# from $-16°$ on the left, to $16°$ on the right, +# and from $-16°$ on top, to $16°$ on the bottom. + +# %% Load example stimulus +stimulus = np.load("example_stimulus.npy") + +# visual extent, in degrees visual angle, +# same convention as pyplot (left, right, top, bottom): +# +# NOTE that Robinson, Hammon, & de Sa (2007) actually implement +# a visual extent slightly smaller: (1023/32) +visextent = tuple(np.asarray((-0.5, 0.5, -0.5, 0.5)) * (1023 / 32)) + +# Visualise +plt.subplot(1, 2, 1) +plt.imshow(stimulus, cmap="gray", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(1, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + stimulus[512, 256:768], + color="black", +) + +plt.show() + +# %% Image coordinate system: +axish = np.linspace(visextent[0], visextent[1], stimulus.shape[0]) +axisv = np.linspace(visextent[2], visextent[3], stimulus.shape[1]) + +(x, y) = np.meshgrid(axish, axisv) + +# %% [markdown] +# In the stimulus image on the left, the left gray patch appears brighter than the right gray patch. +# On the right, the pixel intensity/gray scale values along the horizontal cut +# (indicated by the dashed line in the image) are shown. +# These reveal that, in fact, the two gray patches are identical in their physical intensity. + +# %% [markdown] +# ## Difference-of-Gaussian bank +# The -DOG family of models starts with a _multiscale spatial filtering_ frontend. +# This consists of a set of filters, $\mathbf{F}$ +# which span a range of spatial scales $S$ + +# %% Spacing of filters +# Scales (spatial frequency sensitivities) +num_scales = 7 +largest_center_sigma = 3 # in degrees +center_sigmas = multyscale.utils.octave_intervals(num_scales) * largest_center_sigma +cs_ratio = 2 # center-surround ratio +sigmas = [((s, cs_ratio * s)) for s in center_sigmas] + +# %% Create filterbank +bank_DOG = multyscale.filterbanks.DOGBank(sigmas, x, y) + +# Visualise filterbank +fig, axs = plt.subplots(1, *bank_DOG.shape[:-2], sharex="all", sharey="all") +for s in np.ndindex(*bank_DOG.shape[:-2]): + axs[s].imshow(bank_DOG.filters[s], cmap="coolwarm", extent=visextent) +fig.supxlabel("Spatial scale/freq. $s$") +plt.show() + +# %% [markdown] +# In this visualisation of all filters, +# the columns differ in the spatial scale of the filter. + +# %% [markdown] +# These filters are then convolved with the stimulus image. +# +# Filterbank-objects have an `apply(...)` method, +# which filters the input stimulus with the whole bank. +# The output is an $O \times S \times Y \times X$ tensor +# of channel responses. + +# %% Apply filterbank to (example) stimulus +filters_output = bank_DOG.apply(stimulus) + +# Visualise each filter output +fig, axs = plt.subplots(1, *filters_output.shape[:-2], sharex="all", sharey="all") +for s in np.ndindex(filters_output.shape[:-2]): + axs[s].imshow( + filters_output[s], + cmap="coolwarm", + extent=visextent, + vmin=filters_output.min(), + vmax=filters_output.max(), + ) +fig.supxlabel("Spatial scale/freq. $s$") +plt.show() + + +# %% [markdown] +# ## ODoG bank +# The _oriented_ -ODOG subfamily of models uses filters +# that also have one of several orientations $O$. +# Thus, filter $f_{o,s}$ is a single filter in the set $\mathbf{F}$ +# with orientation $o$ and scale $s$. +# Since each filter is 2D, it also has an implied $x,y$ pixels. +# +# As a result, we can also think of the 2D ($O\times S$) set $\mathbf{F}$ of filter(outputs), +# where each filter(output) $f_{o,s}$ is an image, +# as a 4D ($O \times S \times X \times Y$) set $\mathbf{I}$ of pixel intensities. +# $$ \mathbf{I}_{O \times S \times X \times Y} \equiv \mathbf{F}_{O \times S} $$ +# +# For the current topic, we use the default ODOG filterbank, +# which can be created by `multyscale.filterbanks.RHS2007()`, +# with 6 orientations, and 7 spatial scales. + +# %% Number of filters +n_orientations = 6 +num_scales = 7 + +# %% Spacing of filters + +# Orientations +orientations = tuple(np.arange(0, 180, 180 / n_orientations)) + +# Scales (spatial frequency sensitivities) +largest_center_sigma = 3 # in degrees +center_sigmas = multyscale.utils.octave_intervals(num_scales) * largest_center_sigma +cs_ratio = 2 # center-surround ratio +sigmas = [((s, s), (s, cs_ratio * s)) for s in center_sigmas] + +# %% Create filterbank +bank_ODOG = multyscale.filterbanks.ODOGBank(orientations, sigmas, x, y) + +# Visualise filterbank +fig, axs = plt.subplots(*bank_ODOG.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(*bank_ODOG.shape[:2]): + axs[o, s].imshow(bank_ODOG.filters[o, s, ...], cmap="coolwarm", extent=visextent) +fig.supxlabel("Spatial scale/freq. $s$") +fig.supylabel("Orientation $o$") +plt.show() + +# %% [markdown] +# In this visualisation of all filters, +# the rows differ in orientation of the filter +# and columns differ in the spatial scale of the filter. + +# %% [markdown] +# These filters are then convolved with the stimulus image. +# +# Filterbank-objects have an `apply(...)` method, +# which filters the input stimulus with the whole bank. +# The output is an $O \times S \times Y \times X$ tensor +# of channel responses. + +# %% Apply filterbank to (example) stimulus +filters_output = bank_ODOG.apply(stimulus) + +# Visualise each filter output +fig, axs = plt.subplots(*filters_output.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(filters_output.shape[:2]): + axs[o, s].imshow( + filters_output[o, s], + cmap="coolwarm", + extent=visextent, + vmin=filters_output.min(), + vmax=filters_output.max(), + ) +fig.supxlabel("Spatial scale/freq. $s$") +fig.supylabel("Orientation $o$") +plt.show() diff --git a/docs/filtering/filters.py b/docs/filtering/filters.py new file mode 100644 index 0000000..c681fb3 --- /dev/null +++ b/docs/filtering/filters.py @@ -0,0 +1,236 @@ +# %% [markdown] +# # Creating and applying image filters + +# %% Setup +# Third party libraries +import matplotlib.pyplot as plt +import numpy as np + +# Import local module +from multyscale import filters + +# %% [markdown] +# ## Example stimulus +# The example stimulus used for this exploration +# is a version of White's (1979) classic illusion, +# as also used by Robinson, Hammon, & de Sa (2007) as `WE_thick`. +# +# This stimulus is provided here +# as an NumPy `.npy` file, +# so it can be loaded in directly as a NumPy ndarray. +# +# The image of $1024 \times 1024$ pixels represent $32° \times 32°$ of the visual field; +# if centered, the visual extent of this stimulus subtends +# from $-16°$ on the left, to $16°$ on the right, +# and from $-16°$ on top, to $16°$ on the bottom. + +# %% Load example stimulus +stimulus = np.load("example_stimulus.npy") + +# visual extent, in degrees visual angle, +# same convention as pyplot (left, right, top, bottom): +# +# NOTE that Robinson, Hammon, & de Sa (2007) actually implement +# a visual extent slightly smaller: (1023/32) +visextent = tuple(np.asarray((-0.5, 0.5, -0.5, 0.5)) * (1023 / 32)) + +# Visualise +plt.subplot(1, 2, 1) +plt.imshow(stimulus, cmap="gray", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(1, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + stimulus[512, 256:768], + color="black", +) + +plt.show() + +# %% [markdown] +# In the stimulus image on the left, the left gray patch appears brighter than the right gray patch. +# On the right, the pixel intensity/gray scale values along the horizontal cut +# (indicated by the dashed line in the image) are shown. +# These reveal that, in fact, the two gray patches are identical in their physical intensity. + +# %% [markdown] +# ## Image space +# To construct any filters, first the coordinates of the space must be defined. +# Here, the stimulus is said to subtend $32 \times 32$ degrees of visual angle, +# and has a resolution of $1024 \times 1024$ pixels. +# The horizontal (`axish`) and vertical (`axisv`) axes of the space +# sample the range set by the limits of the visual extent, in $1024$ steps. +# The `numpy.meshgrid()` function produces arrays with the `x` and `y` coordinate, +# in degrees of visual angle, of each of the $1024 \times 1024$ pixels in the image(space). +# +# Defining the coordinates in degrees of visual angle +# allows for defining all other space quantities also in degrees. +# If, instead, the coordinates are in pixels, +# all other quantities (e.g., size of filters) +# must also be given in pixels. + +# %% +axish = np.linspace(visextent[0], visextent[1], stimulus.shape[0]) +axisv = np.linspace(visextent[2], visextent[3], stimulus.shape[1]) + +(x, y) = np.meshgrid(axish, axisv) + +# %% [markdown] +# ## Filter types +# The `multyscale.filters` module implements several filters: +# +# - `multyscale.filters.gaussian2d`: a two-dimensional Gaussian filter, +# which is defined by a standard deviation along a major and minor axis, +# and optionally a rotation +# - `multyscale.filters.dog`: a isotropic difference-of-Gaussian filter, +# composed of a center 2D Gaussian and a surround 2D Gaussian, +# which are each symmetrical +# (i.e., standard deviation along major and minor axes are identical) +# - `multyscale.filters.odog`: an oriented difference-of-Gaussian filter, +# composed of a center 2D Gaussian and a surround 2D Gaussian, +# which do not have to be isotropic +# (i.e., can have different standard deviations along their major and minor axes). + +# %% [markdown] +# ### Two-dimensional Gaussian +# The two-dimensional Gaussian is evaluate over the whole input space, +# i.e., for each coordinate in `x` and `y`. +# The shape of a Gaussian is generally defined by two parameters: +# its central tendency, and its spread. +# For a 2D Gaussian in space, this corresponds to the `center` location, +# and the standard deviation `sigma`. +# The present implementation of the Gaussian defaults to `center`ing on `(0,0)`. + +# %% 2D Gaussians, both circular and elliptic +gaussian_circular = filters.gaussian2d(x, y, sigma=2) +gaussian_elliptic = filters.gaussian2d(x, y, sigma=1, center=(-4, 6)) + +# Plot +plt.subplot(2, 2, 1) +plt.imshow(gaussian_circular, extent=visextent, cmap="coolwarm") +plt.subplot(2, 2, 2) +plt.imshow(gaussian_elliptic, extent=visextent, cmap="coolwarm") + +# Plot horizontal meridians +plt.subplot(2, 2, 3) +plt.plot( + x[int(gaussian_circular.shape[0] / 2)], + gaussian_circular[int(gaussian_circular.shape[0] / 2), ...], +) +plt.subplot(2, 2, 4) +plt.plot( + x[int(gaussian_elliptic.shape[0] / 2)], + gaussian_elliptic[int(gaussian_elliptic.shape[0] / 2), ...], +) + +plt.show() + +# %% [markdown] +# ### Oriented 2D Gaussian +# The previous filters are isotropic: they are radially symmetric and thus _unoriented_. +# This is because the 2D Gaussian(s) have the same standard deviation along both axes. +# However, this is not required. +# _Oriented_ 2D Gaussians can be made by differing the standard deviation along the two axes. +# Optionally, the major and minor axes of the Gaussian +# can be rotated away from the horizontal and vertical axes. + +# %% Oriented Gaussians filters +gaussian_circular = filters.gaussian2d(x, y, sigma=(6, 2)) +gaussian_elliptic = filters.gaussian2d( + x, y, sigma=(6, 2), orientation=65 +) # rotation is counterclockwise + +# % Visualize +fig, axs = plt.subplots(2, 2, sharex="all", sharey="row") + +# Plot images +axs[0, 0].imshow(gaussian_circular, extent=visextent, cmap="coolwarm") +axs[0, 1].imshow(gaussian_elliptic, extent=visextent, cmap="coolwarm") + +# Plot horizontal meridians +axs[1, 0].plot( + x[int(gaussian_circular.shape[0] / 2)], + gaussian_circular[int(gaussian_circular.shape[0] / 2), ...], +) +axs[1, 1].plot( + x[int(gaussian_elliptic.shape[0] / 2)], + gaussian_elliptic[int(gaussian_elliptic.shape[0] / 2), ...], +) + +plt.show() + +# %% [markdown] +# ### Difference-of-Gaussian +# A common type of filter of image processing, is the difference-of-Gaussian (DoG) filter. +# As the name implies, it consists of two, 2D Gaussian filters: +# a smaller "center" Gaussian, and a larger "surround" Gaussian, +# and the filter subtracts the surround Gaussian from the center Gaussian. +# +# For an isotropic DoG filter only one standard deviation is required +# for each of the constituent Gaussians. +# These filters are radially symmetric +# and therefore considered _unoriented_ DoG filters. + +# %% Difference-of-Gaussian +filt_dog = filters.dog(x, y, sigma=(2, 4)) # surround Gaussian is 2:1 of center gaussian + +# Plot +plt.subplot(1, 2, 1) +plt.imshow(filt_dog, extent=visextent, cmap="coolwarm") + +# Plot horizontal meridian +plt.subplot(1, 2, 2) +plt.plot(x[int(filt_dog.shape[0] / 2)], filt_dog[int(filt_dog.shape[0] / 2), ...]) + +plt.show() + +# %% [markdown] +# ### Oriented Difference-of-Gaussian +# A difference-of-Gaussian filter also does not have to be isotropic. +# If at least one of the constituent Gaussians (center or surround) is oriented, +# so will the combined filter be. +# This filter now is an _oriented_ difference-of-Gaussians (ODoG) filter. + +# %% ODOG filter +sigma = ((2, 2), (2, 4)) # surround Gaussian is 2:1 in one axis +filt_odog = filters.odog(x, y, sigma, orientation=80) + +# Plot filter and horizontal meridian +plt.subplot(1, 2, 1) +plt.imshow(filt_odog, extent=visextent, cmap="coolwarm") +plt.subplot(1, 2, 2) +plt.plot(x[int(filt_odog.shape[0] / 2)], filt_odog[int(filt_odog.shape[0] / 2), ...]) + +plt.show() + + +# %% [markdown] +# ## Applying filters +# The `multyscale.filters.apply()` function is used to apply +# a filter (as an `numpy.NDArray`) to an image (also an `numpy.NDArray`). +# +# If the filter and image are *not* the same `shape` (in pixels), +# the smaller will be _padded_ to match the larger one. +# The `padval` argument specifies what value will be used, by default `0.5`. + +# %% ODOG filter +filt_odog = filters.odog(x, y, sigma=((1, 1), (1, 2)), orientation=90) + +# % Apply filter +filtered_img = filters.apply(stimulus, filt_odog) + +# Plot stimulus +plt.subplot(1, 3, 1) +plt.imshow(stimulus, cmap="gray", extent=visextent) + +# Plot filter + horizontal meridian +plt.subplot(2, 3, 2) +plt.imshow(filt_odog, extent=visextent, cmap="coolwarm") +plt.subplot(2, 3, 5) +plt.plot(x[int(filt_odog.shape[0] / 2)], filt_odog[int(filt_odog.shape[0] / 2), ...]) + +# Plot filtered image +plt.subplot(1, 3, 3) +plt.imshow(filtered_img, cmap="coolwarm", extent=visextent) + +plt.show() diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..1ccd419 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,62 @@ +# multyscale + +Multyscale aims to provide a modular and flexible, +yet intuitive and robust library +for implementing multiscale spatial filtering models of early vision +(initially and primarily for modeling brightness perception). + +--- + +::::{grid} 2 +:gutter: 3 + +:::{grid-item-card} Quickstart +:link: quickstart/quickstart +:link-type: doc +:text-align: center +🏃 +^^^ + +Install `multyscale` +and get started, +if you're already familiar with multiscale filtering +::: + +:::{grid-item-card} Learn more +:link: overview/overview +:link-type: doc +:text-align: center +🧑‍🏫 +^^^ + +Learn more about multiscale spatial filtering, +and how `multyscale` implements these ideas +::: + +:::{grid-item-card} Reference +:link: reference/api +:link-type: doc +:text-align: center +📑 +^^^ + +Look up components and functions, +and their specific parameters + +::: + +:::{grid-item-card} Get in touch, or contribute +:link: contributing/get_in_touch +:link-type: doc +:text-align: center +📨, 🎁 +^^^ + +Ask a question, +report a bug, +request a feature, +or contribute. +::: +:::: + +--- \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 2841bfc..0000000 --- a/docs/index.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. multyscale documentation master file, created by - sphinx-quickstart on Fri May 21 00:51:39 2021. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to multyscale's documentation! -====================================== - -Multyscale aims to provide a modular and flexible, yet intuitive and robust library -for implementing multiscale spatial filtering models of early vision -(initially and primarily for modeling brightness perception). - -.. note:: - This documentation uses `The Documentation System`_'s four sections: - Explanations, Tutorials, a "Cookbook" of How-To guides, and the API reference. - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - install - topics/topics - tutorials/tutorials - cookbook/cookbook.rst - -.. toctree:: - :maxdepth: 1 - - reference/multyscale - -:ref:`genindex` - -.. _The Documentation System: https://documentation.divio.com/ \ No newline at end of file diff --git a/docs/install.rst b/docs/install.rst deleted file mode 100644 index a647c92..0000000 --- a/docs/install.rst +++ /dev/null @@ -1,70 +0,0 @@ -Installation -============= - -Multyscale is a true Python package, but not (yet) published on PyPI [#fn_PyPI]_. -For now, multyscale can be retrieved primarily from the `GitHub repository`_ - -.. code-block:: bash - - git clone git@github.com:computational-psychology/multyscale.git - -After downloading the repository, it can be installed (from the main directory): - -.. tabs:: - - .. tab:: pip - - .. code-block:: python - - pip install . - - This will install the package in your local Python packages, - after which the cloned repository can be removed - without removing the installed package. - - To remove the installed package completely, using pip - - .. code-block:: python - - pip uninstall multyscale - - .. tab:: pip, for developers - - .. code-block:: python - - pip install -e . - - This will only create a link to the local repository, - so that changes to the files in this repository are reflected in Python - without the necessity of reinstalling the package. - - As a result, removing the repository removes the packages, - but it's also still a good idea to run - - .. code-block:: python - - pip uninstall multyscale - - -.. _GitHub repository: https://github.com/computational-psychology/multyscale -.. [#fn_PyPI] This will be fixed in an upcoming update. - -.. TODO: add conda installation - -Requirements -------------- - -Multyscale requires: - -- numpy -- scipy - -and recommends working with: - -- pillow -- matplotlib - -as well as: - -- ipython -- jupyter diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 2119f51..0000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/normalization/WE_dual.npy b/docs/normalization/WE_dual.npy new file mode 100644 index 0000000..416bec8 Binary files /dev/null and b/docs/normalization/WE_dual.npy differ diff --git a/docs/normalization/WE_dual_mask.npy b/docs/normalization/WE_dual_mask.npy new file mode 100644 index 0000000..bea451e Binary files /dev/null and b/docs/normalization/WE_dual_mask.npy differ diff --git a/docs/normalization/example_stimulus.npy b/docs/normalization/example_stimulus.npy new file mode 100644 index 0000000..a602b2d Binary files /dev/null and b/docs/normalization/example_stimulus.npy differ diff --git a/docs/tutorials/example_stimulus.png b/docs/normalization/example_stimulus.png similarity index 100% rename from docs/tutorials/example_stimulus.png rename to docs/normalization/example_stimulus.png diff --git a/docs/normalization/example_stimulus_mask.npy b/docs/normalization/example_stimulus_mask.npy new file mode 100644 index 0000000..0ea9b61 Binary files /dev/null and b/docs/normalization/example_stimulus_mask.npy differ diff --git a/docs/normalization/explore_FLODOG_parameters.py b/docs/normalization/explore_FLODOG_parameters.py new file mode 100644 index 0000000..d18e9a2 --- /dev/null +++ b/docs/normalization/explore_FLODOG_parameters.py @@ -0,0 +1,413 @@ +# -*- coding: utf-8 -*- +# --- +# jupyter: +# jupytext: +# cell_metadata_filter: sdmix,incorrectly_encoded_metadata,spatial_window_scalar,size,title,-all +# custom_cell_magics: kql +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.11.2 +# kernelspec: +# display_name: multyscale +# language: python +# name: python3 +# --- + +# %% [markdown] +# # Exploring FLODOG normalization parameter +# This guide describes how to change the normalization parameters +# of the FLODOG model +# and explores how this affects model output. +# This variation is also a replication of Robinson, Hammon, & de Sa (2007; fig 5). + +# %% Setup +# Third party libraries +import matplotlib.pyplot as plt +import numpy as np + +# Import local module +import multyscale + +# %% [markdown] +# ## Example stimulus +# The example stimulus used for this exploration +# is a version of White's (1979) classic illusion, +# as also used by Robinson, Hammon, & de Sa (2007) as `WE_thick`. +# +# This stimulus is provided here +# as an NumPy `.npy` file, +# so it can be loaded in directly as a NumPy ndarray. +# +# The image of $1024 \times 1024$ pixels represent $32° \times 32°$ of the visual field; +# if centered, the visual extent of this stimulus subtends +# from $-16°$ on the left, to $16°$ on the right, +# and from $-16°$ on top, to $16°$ on the bottom. + +# %% Load example stimulus +stimulus = np.load("example_stimulus.npy") + +# visual extent, in degrees visual angle, +# same convention as pyplot (left, right, top, bottom): +# +# NOTE that Robinson, Hammon, & de Sa (2007) actually implement +# a visual extent slightly smaller: (1023/32) +visextent = tuple(np.asarray((-0.5, 0.5, -0.5, 0.5)) * (1023 / 32)) + +# Visualise +plt.subplot(1, 2, 1) +plt.imshow(stimulus, cmap="gray", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(1, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + stimulus[512, 256:768], + color="black", +) +plt.show() + +# %% [markdown] +# In the stimulus image on the left, the left gray patch appears brighter than the right gray patch. +# On the right, the pixel intensity/gray scale values along the horizontal cut +# (indicated by the dashed line in the image) are shown. +# These reveal that, in fact, the two gray patches are identical in their physical intensity. + +# %% [markdown] +# ## Exploring FLODOG normalization +# The FLODOG model differs from the (L)ODOG models in its normalization step +# (see normalization_FLODOG). +# The parameters we are exploring here, only affect this normalization step. +# Thus, we can apply differently parameterized normalizations +# to the same (weighted) filterouputs. + +# %% Instantiate model +FLODOG = multyscale.models.FLODOG_RHS2007(shape=stimulus.shape, visextent=visextent) + +# %% Apply filterbank to (example) stimulus +filters_output = FLODOG.bank.apply(stimulus) +filters_output = FLODOG.weight_outputs(filters_output) + +# Visualise each filter output +fig, axs = plt.subplots(*filters_output.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(filters_output.shape[:2]): + axs[o, s].imshow( + filters_output[o, s], + cmap="coolwarm", + extent=visextent, + vmin=filters_output.min(), + vmax=filters_output.max(), + ) +fig.supxlabel("Spatial scale/freq. $s$") +fig.supylabel("Orientation $o$") +plt.show() + +# %% Output default model +normalized_4_05 = FLODOG.normalize_outputs(filters_output, eps=1e-6) +output_4_05 = np.sum(normalized_4_05, axis=(0, 1)) + +# %% [markdown] +# Generally, the -ODOG normalization steps consist of three parts: +# +# 1. The _normalizing coefficients_: weighted combinations of all filter outputs +# 2. Energy-calculating (as spatial averaging) of the normalizing coefficients +# 3. Divisive normalization, where a filter output is divided by the energy (2) +# of its normalizing coefficient (1) +# +# Which (other) filter outputs get weighted stronger (1) in the normalizing coefficients, +# differs between the models. +# The (L)ODOG models weight every filer with the same orientation, equally. +# The FLODOG model biases the normalization towards similar spatial scales: +# the weight is a (1D) Gaussian function of the difference between +# the scale of the filter being normalized, and the other scales. +# It's centered around 0, such that filters are most strongly normalized by themselves. +# The width of this Gaussian weighting function is specified using +# the parameter `sdmix`: +# $\sigma_w = \mathrm{sdmix} \times s$ + +# %% FLODOG, with sdmix=3 +# Set parameter +sdmix = 3 +# FLODOG.sdmix = sdmix + +# Determine weight +scale_norm_weights = multyscale.normalization.scale_norm_weights_gaussian( + len(FLODOG.scale_weights), sdmix +) +# FLODOG.scale_norm_weights = scale_norm_weights +normalization_weights = multyscale.normalization.create_normalization_weights( + n_orientations=FLODOG.bank.shape[0], + n_scales=FLODOG.bank.shape[1], + scale_norm_weights=scale_norm_weights, + orientation_norm_weights=FLODOG.orientation_norm_weights, +) +# FLODOG.normalization_weights = normalization_weights + +# %% Determine normalizing coefficients +normalizing_coefficients_3 = multyscale.normalization.norm_coeffs( + filters_output, normalization_weights +) + +# Visualise each normalizing coefficient +fig, axs = plt.subplots(*normalizing_coefficients_3.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalizing_coefficients_3.shape[:2]): + axs[o, s].imshow( + normalizing_coefficients_3[o, s], + cmap="coolwarm", + extent=visextent, + vmin=normalizing_coefficients_3.min(), + vmax=normalizing_coefficients_3.max(), + ) +fig.supxlabel("Spatial scale/freq. $s$") +fig.supylabel("Orientation $o$") +plt.show() + +# %% [markdown] +# The (F)LODOG models also perform a local spatial averaging (2) +# to calculate the energy of the normalizing coefficients. +# The size of the local averaging window will affect the model predictions. +# The FLODOG model specifically scales the local averaging window +# (the $\sigma$ of the 2D Gaussian) +# to the spatial scale of the filter being normalized. +# This is controlled by a parameter `spatial_window_scalar`: +# $\sigma = \texttt{spatial window scalar} \times s$. +# To explore how, here we apply various parameterizations of this normalization +# to the same stimulus image and corresponding (weighted) filter outputs. + +# %% FLODOG with spatial_window_scalar=2 +# Set parameter +spatial_window_scalar = 2 +# FLODOG.spatial_window_scalar = spaial_window_scalar + +# Create spatial window sigmas +window_sigmas = spatial_window_scalar * np.broadcast_to( + np.array(FLODOG.center_sigmas)[None, ..., None], (*FLODOG.bank.shape[:2], 2) +) +FLODOG.window_sigmas = window_sigmas + +# Apply spatial averaging windows to normalizing coefficients +energies_2_3 = FLODOG.norm_energies(normalizing_coefficients_3, eps=1e-6) + +# Visualize each energy estimate +fig, axs = plt.subplots(*energies_2_3.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(energies_2_3.shape[:2]): + axs[o, s].imshow( + energies_2_3[o, s], + cmap="coolwarm", + extent=visextent, + vmin=energies_2_3.min(), + vmax=energies_2_3.max(), + ) +fig.supxlabel("Spatial scale/freq. $s$") +fig.supylabel("Orientation $o$") +plt.show() + +# %% Output +normalized_2_3 = filters_output / (energies_2_3 + 1e-6) +output_2_3 = np.sum(normalized_2_3, axis=(0, 1)) + +# %% Comparing FLODOG outputs +vmin = min(np.min(output_4_05), np.min(output_2_3)) +vmax = max(np.max(output_4_05), np.max(output_2_3)) + +plt.subplot(2, 2, 1) +plt.imshow(output_4_05, cmap="coolwarm", vmin=vmin, vmax=vmax, extent=visextent) +plt.subplot(2, 2, 2) +plt.imshow(output_2_3, cmap="coolwarm", vmin=vmin, vmax=vmax, extent=visextent) +plt.subplot(2, 1, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1]), + output_4_05[512, :], + color="black", + linestyle="dotted", + label="FLODOG $\sigma=4; sdmix=0.5$", +) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1]), + output_2_3[512, :], + color="black", + linestyle="dashed", + label="FLODOG $\sigma=2s; sdmix=1$", +) +plt.legend() +plt.show() + + +# %% [markdown] +# ### Construct different models +# An easy(r) way to explore different values for these parameter, +# is to create a different instance of the `FLODOG_RHS2007` model-class +# passing in the `sdmix` and/or spatial_window_scaler parameter(s) to the constructor. + +# %% Instantiate new model object +FLODOG_4_3 = multyscale.models.FLODOG_RHS2007( + shape=stimulus.shape, visextent=visextent, spatial_window_scalar=4, sdmix=3 +) + +# %% [markdown] +# Since the model-object has then been constructed already with the different attributes, +# we can just run the `.normalize_outputs()` method +# to normalize the filter outputs accordingly. + +# %% Normalize +normalized_4_3 = FLODOG_4_3.normalize_outputs(filters_output, eps=1e-6) + +# %% [markdown] +# To then readout the final model prediction, +# we sum over orientations and spatial scales. + +# %% Readout +output_4_3 = np.sum(normalized_4_3, axis=(0, 1)) + +# Visualize +vmin = min(np.min(output_4_05), np.min(output_4_3)) +vmax = max(np.max(output_4_05), np.max(output_4_3)) + +plt.subplot(2, 2, 1) +plt.imshow(output_4_05, cmap="coolwarm", vmin=vmin, vmax=vmax, extent=visextent) +plt.subplot(2, 2, 2) +plt.imshow(output_4_3, cmap="coolwarm", vmin=vmin, vmax=vmax, extent=visextent) +plt.subplot(2, 1, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1]), + output_4_05[512, :], + color="black", + linestyle="dotted", + label="FLODOG $\sigma=4; sdmix=0.5$", +) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1]), + output_4_3[512, :], + color="black", + linestyle="dashed", + label="FLODOG $\sigma=2s; sdmix=1$", +) +plt.legend() +plt.show() + +# %% [markdown] +# ## Recreate Robinson, Hammon, & de Sa (2007, Fig. 5) +# Robinson, Hammon, & de Sa (2007) compare +# three sets of parameters, +# varying the window size scaling factor between $\sigma=2s$ and $\sigma=4s$, +# and varying the width of the Gaussian defining the mixing weights between $m=0.5$ and $m=3.0$. +# +# Figure 5 shows and compares the spatial inhomogeneities +# of the three different parameterizations of the model. +# Also, these three versions of FLODOG are compared to the default LODOG model ($\sigma=4$). + +# %% FLODOG parameterizations +# FLODOG_4_05 = multyscale.models.FLODOG_RHS2007( +# shape=stimulus.shape, visextent=visextent, spatial_window_scalar=4, sdmix=0.5 +# ) +FLODOG_2_05 = multyscale.models.FLODOG_RHS2007( + shape=stimulus.shape, visextent=visextent, spatial_window_scalar=2, sdmix=0.5 +) +# FLODOG_4_3 = multyscale.models.FLODOG_RHS2007( +# shape=stimulus.shape, visextent=visextent, spatial_window_scalar=4, sdmix=3 +# ) + +# %% Outputs +output_2_05 = FLODOG_2_05.normalize_outputs(filters_output, eps=1e-6).sum((0, 1)) + +# %% LODOG, $\sigma=4$ +LODOG = multyscale.models.LODOG_RHS2007(shape=stimulus.shape, visextent=visextent, window_sigma=4) +output_LODOG_4 = LODOG.normalize_outputs(filters_output, eps=1e-6).sum((0, 1)) + + +# %% [markdown] +# To more properly compare the outputs of these different model instances, +# Robinson, Hammon, & de Sa (2007) standardize model outputs such that +# the difference between the mean output in the two target regions +# on the `WE_thick` stimulus +# is equal to $1.0$. +# +# To implement this here, we: +# - load a mask image (as a `numpy.NDArray`) that defines the two target regions +# - apply this mask to model output to get model output in just the two target regions +# - calculate the mean for each target region +# - subtract the two means +# - divide the whole model output by this difference + +# %% Standardize such that effect size=1 +mask = np.load("example_stimulus_mask.npy") + + +def target_diff(output, mask): + left_target_mask = mask == 1 + right_target_mask = mask == 2 + + left_target_output = output[left_target_mask] + right_target_output = output[right_target_mask] + return left_target_output.mean() - right_target_output.mean() + + +output_LODOG_4 /= target_diff(output_LODOG_4, mask) +output_4_05 /= target_diff(output_4_05, mask) +output_2_05 /= target_diff(output_2_05, mask) +output_4_3 /= target_diff(output_4_3, mask) + +# %% [markdown] +# This, then, gives us the outputs scaled into the same range, +# which replicates Figure 5 from Robinson, Hammon, & de Sa (2007). + +# %% +plt.figure(figsize=(6, 10)) + +ax = plt.subplot(3, 1, 1) +ax.imshow(stimulus[275:750, :], cmap="gray") +ax.axhline(y=235, color="black", dashes=(1, 1)) + + +plt.subplot(6, 1, 3) +plt.plot(output_LODOG_4[512, :], color="blue", linestyle="solid", label="LODOG n=4") +plt.grid(axis="y") +plt.yticks(ticks=range(-4, 6, 2)) +plt.ylim(-4, 4) +plt.xlim(0, output_LODOG_4.shape[0]) +plt.legend() + + +plt.subplot(6, 1, 4) +plt.plot( + output_4_05[512, :], + color="blue", + linestyle="solid", + label="FLODOG $\sigma=4s, m=0.5$", +) +plt.grid(axis="y") +plt.yticks(ticks=range(-4, 6, 2)) +plt.ylim(-4, 4) +plt.xlim(0, output_4_05.shape[0]) +plt.legend() + + +plt.subplot(6, 1, 5) +plt.plot( + output_2_05[512, :], + color="blue", + linestyle="solid", + label="FLODOG $\sigma=2s, m=0.5$", +) +plt.grid(axis="y") +plt.yticks(ticks=range(-4, 6, 2)) +plt.ylim(-4, 4) +plt.xlim(0, output_2_05.shape[0]) +plt.legend() + + +plt.subplot(6, 1, 6) +plt.plot( + output_4_3[512, :], + color="blue", + linestyle="solid", + label="FLODOG $\sigma=4s, m=3.0$", +) +plt.grid(axis="y") +plt.yticks(ticks=range(-4, 6, 2)) +plt.ylim(-4, 4) +plt.xlim(0, output_4_3.shape[0]) +plt.legend() + + +plt.show() diff --git a/docs/normalization/explore_LODOG_parameter.py b/docs/normalization/explore_LODOG_parameter.py new file mode 100644 index 0000000..ba77ea5 --- /dev/null +++ b/docs/normalization/explore_LODOG_parameter.py @@ -0,0 +1,394 @@ +# %% [markdown] +# # Exploring LODOG normalization parameter +# This guide describes how to change the normalization parameter +# of the LODOG model +# and explores how this affects model output. +# This variation is also a replication of Robinson, Hammon, & de Sa (2007; fig 3). + +# %% Setup +# Third party libraries +import matplotlib.pyplot as plt +import numpy as np + +# Import local module +import multyscale + +# %% [markdown] +# ## Example stimulus +# The example stimulus used for this exploration +# is a version of White's (1979) classic illusion, +# as also used by Robinson, Hammon, & de Sa (2007) as `WE_thick`. +# +# This stimulus is provided here +# as an NumPy `.npy` file, +# so it can be loaded in directly as a NumPy ndarray. +# +# The image of $1024 \times 1024$ pixels represent $32° \times 32°$ of the visual field; +# if centered, the visual extent of this stimulus subtends +# from $-16°$ on the left, to $16°$ on the right, +# and from $-16°$ on top, to $16°$ on the bottom. + +# %% Load example stimulus +stimulus = np.load("example_stimulus.npy") + +# visual extent, in degrees visual angle, +# same convention as pyplot (left, right, top, bottom): +# +# NOTE that Robinson, Hammon, & de Sa (2007) actually implement +# a visual extent slightly smaller: (1023/32) +visextent = tuple(np.asarray((-0.5, 0.5, -0.5, 0.5)) * (1023 / 32)) + +# Visualise +plt.subplot(1, 2, 1) +plt.imshow(stimulus, cmap="gray", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(1, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + stimulus[512, 256:768], + color="black", +) +plt.show() + +# %% [markdown] +# In the stimulus image on the left, the left gray patch appears brighter than the right gray patch. +# On the right, the pixel intensity/gray scale values along the horizontal cut +# (indicated by the dashed line in the image) are shown. +# These reveal that, in fact, the two gray patches are identical in their physical intensity. + + +# %% [markdown] +# ## Exploring LODOG normalization +# The LODOG model differs from the original/base ODOG model in its normalization step +# (see normalization_LODOG). +# Specifically, where the ODOG normalization calculates the global energy +# as the spatial RMS image-wide, +# the LODOG normalization calculates _local_ RMS, +# by averaging within a Gaussian window. + +# %% Instantiate model +LODOG = multyscale.models.LODOG_RHS2007(shape=stimulus.shape, visextent=visextent) + +# %% Apply filterbank to (example) stimulus +filters_output = LODOG.bank.apply(stimulus) +filters_output = LODOG.weight_outputs(filters_output) + +# Visualise each filter output +fig, axs = plt.subplots(*filters_output.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(filters_output.shape[:2]): + axs[o, s].imshow( + filters_output[o, s], + cmap="coolwarm", + extent=visextent, + vmin=filters_output.min(), + vmax=filters_output.max(), + ) +fig.supxlabel("Spatial scale/freq. $s$") +fig.supylabel("Orientation $o$") +plt.show() + + +# %% [markdown] +# Generally, the -ODOG normalization steps consist of three parts: +# +# 1. The _normalizing coefficients_: weighted combinations of all filter outputs +# 2. Energy-calculating (as spatial averaging) of the normalizing coefficients +# 3. Divisive normalization, where a filter output is divided by the energy (2) +# of its normalizing coefficient (1) +# +# The LODOG model performs a local, rather than image-wide, spatial averaging (2) +# to calculate the energy of the normalizing coefficients. +# +# The size of the local averaging window will affect the model predictions. +# To explore how, here we apply various parameterizations of this normalization +# to the same stimulus image and corresponding (weighted) filter outputs. + +# %% [markdown] +# The normalizing coefficients (1) are not affected by +# the LODOG-specific spatial window parameter, +# and thus also only need to be calculated once for all the parameterizations explored here. + +# %% +normalizing_coefficients = LODOG.norm_coeffs(filters_output) + +# %% Gaussian spatial averaging window [markdown] +# The spatial averaging window in the LODOG model is +# a circular 2D Gaussian. +# Thus, its size is specified as a single standard deviation $\sigma$ +# that defines the width of the Gaussian in both directions. +# The default is $\sigma = 4°$. +# The same $\sigma$ is used for all normalizing coefficients +# (though `multyscale` implements this not as a single Gaussian filter, +# but as a set of identical filters). + +# %% +window_sigma = LODOG.window_sigma +print(f"sigma = {window_sigma} deg") +window_sigmas = LODOG.window_sigmas + +assert np.all(window_sigmas == window_sigma) + +spatial_filters = np.ndarray(filters_output.shape) +for o, s in np.ndindex(LODOG.window_sigmas.shape[:2]): + spatial_filters[o, s, :] = multyscale.normalization.spatial_kernel_gaussian( + LODOG.bank.x, LODOG.bank.y, LODOG.window_sigmas[o, s, :] + ) +plt.subplot(1, 2, 1) +plt.imshow(spatial_filters[0, 0, ...], cmap="coolwarm", extent=visextent) +plt.colorbar() +plt.subplot(1, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1]), + spatial_filters[0, 0, 512, :], + color="black", +) +plt.axvline(x=-window_sigma, color="grey", linestyle="dashed") +plt.axvline(x=window_sigma, color="grey", linestyle="dashed") +plt.xlabel("X (deg. vis. angle)") +plt.ylabel("Y (deg. vis. angle)") +plt.show() + +# %% [markdown] +# The model method `.norm_energies()` automatically generates and applies +# these spatial averaging windows to the proved normalizing coeffiencts. +# This produces an $O \times S$ set of locally ($Y \times X$) calculated energies, +# one for each normalizaing coefficient. + +# %% +energies_4 = LODOG.norm_energies(normalizing_coefficients, eps=1e-6) + +# Visualize each local energy +fig, axs = plt.subplots(*energies_4.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(energies_4.shape[:2]): + axs[o, s].imshow(energies_4[o, s], cmap="coolwarm", extent=visextent) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + +# %% [markdown] +# These locally calculated energies form the denominator of each divisive normalization. +# Since the local energy tensor is the same $(O, S, X, Y)$ shape as the filter outputs +# we can simply divide. + +# %% +normalized_outputs_4 = filters_output / energies_4 + +# Visualize each normalized output +fig, axs = plt.subplots(*normalized_outputs_4.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalized_outputs_4.shape[:2]): + axs[o, s].imshow(normalized_outputs_4[o, s], cmap="coolwarm", extent=visextent) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + +# %% [markdown] +# Combining the normalized outputs gives a final model prediction. + +# %% +output_LODOG_4 = np.sum(normalized_outputs_4, axis=(0, 1)) + +# %% [markdown] +# Adjusting spatial averaging window size +# The easiest way to adjust the window sigma, +# is to set the `window_sigma` attribute of the model-object. + +# %% +LODOG.window_sigma = 1 + +# %% [markdown] +# Since `multyscale` actually implements the spatial averaging window +# separately for each normalizing coefficient (i.e., for each model-filter) +# this requires then to also set the whole set of ($O \times S$) `window_sigmas`: + +# %% +LODOG.window_sigmas = np.ones(shape=(*LODOG.bank.shape[:2], 2)) * LODOG.window_sigma +assert np.all(LODOG.window_sigmas == LODOG.window_sigma) + +# %% +spatial_filters = np.ndarray(filters_output.shape) +for o, s in np.ndindex(LODOG.window_sigmas.shape[:2]): + spatial_filters[o, s, :] = multyscale.normalization.spatial_kernel_gaussian( + LODOG.bank.x, LODOG.bank.y, LODOG.window_sigmas[o, s, :] + ) + +# %% +plt.subplot(1, 2, 1) +plt.imshow(spatial_filters[0, 0, ...], cmap="coolwarm", extent=visextent) +plt.colorbar() +plt.subplot(1, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1]), + spatial_filters[0, 0, 512, :], + color="black", +) +plt.axvline(x=-window_sigma, color="grey", linestyle="dashed") +plt.axvline(x=window_sigma, color="grey", linestyle="dashed") +plt.xlabel("X (deg. vis. angle)") +plt.ylabel("Y (deg. vis. angle)") +plt.show() + +# %% [markdown] +# From here, the exact same steps can be taken to calculate these +# more locally restricted (smaller spatial averaging window) energies + +# %% +energies_1 = LODOG.norm_energies(normalizing_coefficients, eps=1e-6) + +# Visualize each local energy +fig, axs = plt.subplots(*energies_4.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(energies_1.shape[:2]): + axs[o, s].imshow(energies_1[o, s], cmap="coolwarm", extent=visextent) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + +# %% [markdown] +# What can be seen here, is that the more local energy calculation +# means much more spatial variation over the whole image, +# as would be expected. + +# %% +normalized_outputs_1 = filters_output / (energies_1 + 1e-6) + +# Visualize each normalized output +vmin = min(np.min(normalized_outputs_4), np.min(normalized_outputs_1)) +vmax = max(np.max(normalized_outputs_4), np.max(normalized_outputs_1)) +fig, axs = plt.subplots(*normalized_outputs_1.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalized_outputs_1.shape[:2]): + axs[o, s].imshow(normalized_outputs_1[o, s], cmap="coolwarm", extent=visextent) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + +# %% [markdown] +# Resultingly, the normalized filter outputs can also vary much more locally. + +# %% [markdown] +# Combining the normalized outputs gives a final model prediction. + +# %% +output_LODOG_1 = np.sum(normalized_outputs_1, axis=(0, 1)) + +# %% Comparing LODOG outputs +vmin = min(np.min(output_LODOG_4), np.min(output_LODOG_1)) +vmax = max(np.max(output_LODOG_4), np.max(output_LODOG_1)) + +plt.subplot(2, 2, 1) +plt.imshow(output_LODOG_4, cmap="coolwarm", vmin=vmin, vmax=vmax, extent=visextent) +plt.subplot(2, 2, 2) +plt.imshow(output_LODOG_1, cmap="coolwarm", vmin=vmin, vmax=vmax, extent=visextent) +plt.subplot(2, 1, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1]), + output_LODOG_4[512, :], + color="black", + linestyle="dotted", + label="LODOG $\sigma=4$", +) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1]), + output_LODOG_1[512, :], + color="black", + linestyle="dashed", + label="LODOG $\sigma=1$", +) +plt.legend() +plt.show() + +# %% [markdown] +# Here it can be seen that in the complete model output +# for the LODOG model with spatial filtering $\sigma = 1°$ +# shows a lot more spatial inhomogeneity; +# the neutral gray background/surround shows strong inhomogeneity in the model output, +# but also the bars of carrier grating show stronger edge artifacts. + +# %% [markdown] +# ### Construct different models +# An easier way to explore different values for this parameter, +# is to create a different instance of the `LODOG_RHS2007` model-class +# passing in the `window_sigma` parameter to the constructor. +# +# Since the model-object has then been constructed already with the different `window_sigma`, +# we can just run the `.normalize_outputs()` method +# to normalize the filter outputs accordingly. +# +# To then readout the final model prediction, +# we sum over orientations and spatial scales. + +# %% +LODOG_2 = multyscale.models.LODOG_RHS2007( + shape=stimulus.shape, visextent=visextent, window_sigma=2 +) +output_LODOG_2 = LODOG_2.normalize_outputs(filters_output).sum((0, 1)) + +# %% [markdown] +# ## Recreate Robinson, Hammon, & de Sa (2007, Fig. 3) +# Robinson, Hammon, & de Sa (2007) compare +# three values for the $\sigma$ parameter +# controlling the size of the (local) spatial normalization window: +# $1°$, $2°$, and $4°$. +# Figure 3 shows and compares the spatial inhomogeneities +# of the three different parameterizations of the model. +# Also, these three versions of LODOG will be compared to the default ODOG model. + +# %% +ODOG = multyscale.models.ODOG_RHS2007(shape=stimulus.shape, visextent=visextent) +output_ODOG = ODOG.normalize_outputs(filters_output).sum((0, 1)) + + +# %% [markdown] +# To more properly compare the outputs of these different model instances, +# Robinson, Hammon, & de Sa (2007) standardize model outputs such that +# the difference between the mean output in the two target regions +# on the `WE_thick` stimulus +# is equal to $1.0$. +# +# To implement this here, we: +# - load a mask image (as a `numpy.NDArray`) that defines the two target regions +# - apply this mask to model output to get model output in just the two target regions +# - calculate the mean for each target region +# - subtract the two means +# - divide the whole model output by this difference + +# %% Standardize such that effect size = 1 +mask = np.load("example_stimulus_mask.npy") + + +def target_diff(output, mask): + left_target_mask = mask == 1 + right_target_mask = mask == 2 + + left_target_output = output[left_target_mask] + right_target_output = output[right_target_mask] + return left_target_output.mean() - right_target_output.mean() + + +output_ODOG /= target_diff(output_ODOG, mask) +output_LODOG_1 /= target_diff(output_LODOG_1, mask) +output_LODOG_2 /= target_diff(output_LODOG_2, mask) +output_LODOG_4 /= target_diff(output_LODOG_4, mask) + +# %% [markdown] +# This, then, gives us the outputs scaled into the same range, +# which perfectly replicates Figure 3 from Robinson, Hammon, & de Sa (2007). + +# %% +plt.figure(figsize=(6, 10)) + +ax = plt.subplot(2, 1, 1) +ax.imshow(stimulus[275:750, :], cmap="gray") +ax.axhline(y=235, color="black", dashes=(1, 1)) + + +plt.subplot(2, 1, 2) +plt.plot(output_ODOG[512, :], color="black", linestyle="solid", label="ODOG") +plt.plot(output_LODOG_4[512, :], color="grey", linestyle="dotted", label="LODOG N=4") +plt.plot(output_LODOG_2[512, :], color="grey", linestyle="solid", label="LODOG N=2") +plt.plot(output_LODOG_1[512, :], color="black", linestyle="dashed", label="LODOG N=1") + +plt.grid(axis="y") +plt.yticks(ticks=range(-9, 12, 3)) + +plt.legend() + +plt.show() diff --git a/docs/normalization/normalization_FLODOG.py b/docs/normalization/normalization_FLODOG.py new file mode 100644 index 0000000..02dba20 --- /dev/null +++ b/docs/normalization/normalization_FLODOG.py @@ -0,0 +1,390 @@ +# %% [markdown] +# # FLODOG normalization +# This Tutorial describes the rationale behind +# the normalization step of the FLODOG model (Robinson, Hammon, de Sa 2007), +# and its implementation in `multyscale`. + +# %% Setup +# Third party libraries +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +# Import local module +import multyscale + +# %% [markdown] +# ## Example stimulus +# The example stimulus used for this exploration +# is a version of White's (1979) classic illusion, +# as also used by Robinson, Hammon, & de Sa (2007) as `WE_thick`. +# +# This stimulus is provided here +# as an NumPy `.npy` file, +# so it can be loaded in directly as a NumPy ndarray. +# +# The image of $1024 \times 1024$ pixels represent $32° \times 32°$ of the visual field; +# if centered, the visual extent of this stimulus subtends +# from $-16°$ on the left, to $16°$ on the right, +# and from $-16°$ on top, to $16°$ on the bottom. + +# %% Load example stimulus +stimulus = np.load("example_stimulus.npy") + +# visual extent, in degrees visual angle, +# same convention as pyplot (left, right, top, bottom): +# +# NOTE that Robinson, Hammon, & de Sa (2007) actually implement +# a visual extent slightly smaller: (1023/32) +visextent = tuple(np.asarray((-0.5, 0.5, -0.5, 0.5)) * (1023 / 32)) + +# Visualise +plt.subplot(1, 2, 1) +plt.imshow(stimulus, cmap="gray", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(1, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + stimulus[512, 256:768], + color="black", +) +plt.show() + +# %% [markdown] +# In the stimulus image on the left, the left gray patch appears brighter than the right gray patch. +# On the right, the pixel intensity/gray scale values along the horizontal cut +# (indicated by the dashed line in the image) are shown. +# These reveal that, in fact, the two gray patches are identical in their physical intensity. + + +# %% [markdown] +# ## FLODOG: making -ODOG more biologically plausible +# +# Robinson, Hammon, & de Sa (2007) argue for two changes to the -ODOG models, +# based on known properties of the visual system. +# +# Firstly, the divisive normalization should be spatially local. +# This is partly implemented in the LODOG normalization step (see normalization_LODOG). +# However, it would be more plausible if the spatial window for normalization +# depends on scale of the filter being normalized: +# since small filters operate on small spatial areas, +# the normalization should similarly only be affected by nearby activity; +# and large scale filter should be affected by energy in more distal regions. +# +# Secondly, they argue that because similar spatial frequencies cluster in early visual regions, +# they are also more likely to inhibit each other. +# In other words, filters at similar scales (sensitive to similar spatial frequencies) +# should normalize each other more strongly than filters with very different sensitivies. +# +# Thus, Robinson, Hammon, & de Sa (2007) propose a +# (spatial) **F**requency-specific **L**ocally normalized -ODOG (FLODOG) model, +# which builds on the LODOG model but differs in these two aspects. + +# %% +LODOG = multyscale.models.LODOG_RHS2007(shape=stimulus.shape, visextent=visextent) +FLODOG = multyscale.models.FLODOG_RHS2007(shape=stimulus.shape, visextent=visextent) +assert np.array_equal(FLODOG.bank.filters, LODOG.bank.filters) + + +# %% [markdown] +# The models share the same filterbank frontend, +# so it's only necessary to apply this bank +# (and weight the filteroutputs) +# once. + +# %% +filters_output = FLODOG.bank.apply(stimulus) +filters_output = FLODOG.weight_outputs(filters_output) + +# %% [markdown] +# ## FLODOG normalization +# Generally, the -ODOG normalization step consists of three parts: +# +# 1. The _normalizing coefficients_: weighted combinations of all filter outputs +# 2. Energy-calculating (as spatial averaging) of the normalizing coefficients +# 3. Divisive normalization, where a filter output is divided by the energy (2) +# of its normalizing coefficient (1) +# +# The FLODOG differs from the (L)ODOG models in both normalizing coefficients (1), +# and how energy is estimated (2). + +# %% [markdown] +# ### Normalizing coefficients +# Where the (L)ODOG models use equal weighting +# for filters of all scales to construct the normalizing coefficients $\mathbf{N}$, +# the FLODOG normalizing coefficients weight more heavily +# those spatial scales that are more similar to the filter being normalized +# than more different scales. + +# %% [markdown] +# Instead of the equal weighting, +# the FLODOG model uses a 1D Gaussian as the weights profile: +# centered on the spatial scale of the filter being normalized +# and dropping off as a Gaussian function of the relative index the other spatial scales. + +# %% Define weights +scale_norm_weights_FLODOG = multyscale.normalization.scale_norm_weights_gaussian(7, sdmix=0.5) +assert np.array_equal(scale_norm_weights_FLODOG, FLODOG.scale_norm_weights) + +# %% +fig, axs = plt.subplots(2, 2, sharex="row", sharey="row") +axs[0, 0].pcolor( + LODOG.scale_norm_weights, + cmap="Greens", + edgecolors="k", + linewidths=1, + vmin=0, + vmax=1, +) +axs[0, 0].set_ylabel("scale of filter to normalize (idx)") +axs[0, 0].set_title("LODOG weights") + +axs[0, 1].pcolor( + FLODOG.scale_norm_weights, + cmap="Greens", + edgecolors="k", + linewidths=1, + vmin=0, + vmax=1, +) +axs[0, 1].set_xlabel("scale of filter to normalize (idx)") +axs[0, 1].set_title("FLODOG weights") + +axs[1, 0].plot(LODOG.scale_norm_weights[3, :], color="black") +axs[1, 0].set_xlabel("scale of other filter (idx)") +axs[1, 0].set_ylabel("weight") + +axs[1, 1].plot(FLODOG.scale_norm_weights[3, :], color="black") +axs[1, 1].set_xlabel("scale of other filter (idx)") + +plt.show() + +# %% [markdown] +# Since these weights are strongly biased towards same/similar spatial scales, +# the resulting normalizing coefficients also more strongly resemble +# the filter outputs at these spatial scales. + +# %% Normalizing coefficients +normalizing_coefficients_LODOG = LODOG.norm_coeffs(filters_output) +normalizing_coefficients_FLODOG = FLODOG.norm_coeffs(filters_output) + +# Visualize each norm. coeff. +vmin = min(np.min(normalizing_coefficients_LODOG), np.min(normalizing_coefficients_FLODOG)) +vmax = max(np.max(normalizing_coefficients_LODOG), np.max(normalizing_coefficients_FLODOG)) + +fig, axs = plt.subplots(*normalizing_coefficients_LODOG.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalizing_coefficients_LODOG.shape[:2]): + axs[o, s].imshow( + normalizing_coefficients_LODOG[o, s], + cmap="coolwarm", + extent=visextent, + vmin=vmin, + vmax=vmax, + ) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +fig.suptitle("LODOG") +plt.show() + +fig, axs = plt.subplots(*normalizing_coefficients_FLODOG.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalizing_coefficients_FLODOG.shape[:2]): + axs[o, s].imshow( + normalizing_coefficients_FLODOG[o, s], + cmap="coolwarm", + extent=visextent, + vmin=vmin, + vmax=vmax, + ) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +fig.suptitle("FLODOG") +plt.show() + + +# %% [markdown] +# ### Scale-localized energy estimate +# Secondly, the LODOG and FLODOG models differ in +# the Gaussian spatial averaging window that they use +# to locally calculate the energy (spatial RMS) of each normalization coefficients. + +# Where the LODOG model uses a single Gaussian window for all normalization coefficients, +# in FLODOG, the width of this spatial averaging window scales with the scale of the filter +# being normalized. +# +# Thus the normalization of the **F**LODOG model is "localized" +# both in space, and in _spatial scale_ / _**F**requency_. + +# %% Spatial averaging window +spatial_windows_LODOG = np.ndarray(filters_output.shape) +for o, s in np.ndindex(LODOG.window_sigmas.shape[:2]): + spatial_windows_LODOG[o, s, :] = multyscale.normalization.spatial_kernel_gaussian( + LODOG.bank.x, LODOG.bank.y, LODOG.window_sigmas[o, s, :] + ) + +spatial_windows_FLODOG = np.ndarray(filters_output.shape) +for o, s in np.ndindex(FLODOG.window_sigmas.shape[:2]): + spatial_windows_FLODOG[o, s, :] = multyscale.normalization.spatial_kernel_gaussian( + FLODOG.bank.x, FLODOG.bank.y, FLODOG.window_sigmas[o, s, :] + ) + +# Visualize each spatial avg. window +fig, axs = plt.subplots(2, spatial_windows_LODOG.shape[1], sharex="all", sharey="all") +for s in range(spatial_windows_LODOG.shape[1]): + axs[0, s].imshow( + spatial_windows_LODOG[3, s], + cmap="coolwarm", + extent=visextent, + ) + axs[1, s].imshow( + spatial_windows_FLODOG[3, s], + cmap="coolwarm", + extent=visextent, + ) +axs[0, 3].set_title("LODOG") +axs[1, 3].set_title("FLODOG") +plt.show() + +# %% [markdown] +# Both these factors influence the final energy estimates +# that form the denominators of the normalization + +# %% Energy estimates +energies_LODOG = LODOG.norm_energies(normalizing_coefficients_LODOG, eps=1e-6) +energies_FLODOG = FLODOG.norm_energies(normalizing_coefficients_FLODOG, eps=1e-6) + +# Visualize +vmin = min(np.min(energies_LODOG), np.min(energies_FLODOG)) +vmax = max(np.max(energies_LODOG), np.max(energies_FLODOG)) +fig, axs = plt.subplots(*energies_LODOG.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(energies_LODOG.shape[:2]): + axs[o, s].imshow( + energies_LODOG[o, s], + cmap="coolwarm", + extent=visextent, + vmin=vmin, + vmax=vmax, + ) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +fig.suptitle("LODOG") +plt.show() + +fig, axs = plt.subplots(*energies_FLODOG.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(energies_FLODOG.shape[:2]): + axs[o, s].imshow( + energies_FLODOG[o, s], + cmap="coolwarm", + extent=visextent, + vmin=vmin, + vmax=vmax, + ) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +fig.suptitle("FLODOG") +plt.show() + +# %% [markdown] +# With these parameters, +# the normalizing energy coefficients (i.e., denominators in normalization) +# of the FLODOG model are much more spatially localized, +# and look much more similar to the filters that they are normalizing. +# Moreover, in absolute terms, the values are a lot smaller, +# meaning stronger enhancement of channels + +# %% +pd.DataFrame( + { + "LODOG": pd.Series({"min": np.min(energies_LODOG), "max": np.max(energies_LODOG)}), + "FLODOG": pd.Series({"min": np.min(energies_FLODOG), "max": np.max(energies_FLODOG)}), + } +) + +# %% [markdown] +# As a result, +# the FLODOG normalization results in much stronger local "peaks" +# and thus more spatial inhomogeneities + +# %% Normalize +LODOG_normalized = filters_output / energies_LODOG +FLODOG_normalized = filters_output / energies_FLODOG + +# Visualize normalized outputs +vmin = min(np.min(LODOG_normalized), np.min(FLODOG_normalized)) +vmax = max(np.max(LODOG_normalized), np.max(FLODOG_normalized)) +fig, axs = plt.subplots(*LODOG_normalized.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(LODOG_normalized.shape[:2]): + axs[o, s].imshow( + LODOG_normalized[o, s], + cmap="coolwarm", + extent=visextent, + vmin=vmin, + vmax=vmax, + ) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +fig.suptitle("LODOG") +plt.show() + +fig, axs = plt.subplots(*FLODOG_normalized.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(FLODOG_normalized.shape[:2]): + axs[o, s].imshow( + FLODOG_normalized[o, s], + cmap="coolwarm", + extent=visextent, + vmin=vmin, + vmax=vmax, + ) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +fig.suptitle("FLODOG") +plt.show() + + +# %% [markdown] +# Similarly, this then shows up in the final integrated outputs + +# %% Integrated +output_LODOG = LODOG_normalized.sum((0, 1)) +output_FLODOG = FLODOG_normalized.sum((0, 1)) + +# Visualize outputs +vmin = min(np.min(output_LODOG), np.min(output_FLODOG)) +vmax = max(np.max(output_LODOG), np.max(output_FLODOG)) + +plt.subplot(2, 2, 1) +plt.imshow(output_LODOG, cmap="coolwarm", vmin=vmin, vmax=vmax, extent=visextent) +plt.title("LODOG") +plt.subplot(2, 2, 2) +plt.imshow(output_FLODOG, cmap="coolwarm", vmin=vmin, vmax=vmax, extent=visextent) +plt.title("FLODOG") +plt.subplot(2, 1, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1]), + output_LODOG[512, :], + color="black", + linestyle="solid", + label="LODOG", +) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1]), + output_FLODOG[512, :], + color="black", + linestyle="dotted", + label="FLODOG", +) +plt.legend() +plt.show() + +# %% [markdown] +# The "more biological plausible" FLODOG model predicts +# the direction of many brightness stimuli well, +# including ones that the (L)ODOG models fail to predict. +# However, it also produces these strong spatial inhomogeneities, +# which strongly emphasize borders and are clearly visible in the model outputs. +# These inhomogeneities are not immediately perceptually experienced in the original stimuli, +# though some suggest they can be measured psychophysically. +# +# From a modeling point of view, it is also important to recognize that the output ranges, +# i.e., the units on the vertical axes, are vastly different. +# Thus, for meaningful quantitative comparisons, +# some form of standardization is required. diff --git a/docs/normalization/normalization_LODOG.py b/docs/normalization/normalization_LODOG.py new file mode 100644 index 0000000..1f76de1 --- /dev/null +++ b/docs/normalization/normalization_LODOG.py @@ -0,0 +1,521 @@ +# %% [markdown] +# # LODOG normalization +# This Tutorial describes the rationale behind +# the normalization step of the LODOG model (Robinson, Hammon, de Sa 2007), +# and its implementation in `multyscale`. + +# %% Setup +# Third party libraries +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +# Import local module +import multyscale + +# %% [markdown] +# ## Example stimulus +# The example stimulus used for this exploration +# is an image containing two version of White's (1979) classic illusion, +# as also used by Robinson, Hammon, & de Sa (2007) as `WE_dual`. +# +# This stimulus is provided here +# as an NumPy `.npy` file, +# so it can be loaded in directly as a NumPy ndarray. +# +# The image of $1024 \times 1024$ pixels represent $32° \times 32°$ of the visual field; +# if centered, the visual extent of this stimulus subtends +# from $-16°$ on the left, to $16°$ on the right, +# and from $-16°$ on top, to $16°$ on the bottom. + +# %% Load example stimulus +stimulus = np.load("WE_dual.npy") + +# visual extent, in degrees visual angle, +# same convention as pyplot (left, right, top, bottom): +# +# NOTE that Robinson, Hammon, & de Sa (2007) actually implement +# a visual extent slightly smaller: (1023/32) +visextent = tuple(np.asarray((-0.5, 0.5, -0.5, 0.5)) * (1023 / 32)) + +# Visualise +plt.subplot(2, 2, 1) +plt.imshow(stimulus, cmap="gray", extent=visextent) +plt.axhline(y=0, xmin=0, xmax=0.5, color="black", dashes=(1, 1)) +plt.axvline(x=8, ymin=0.25, ymax=0.75, color="black", dashes=(1, 1)) +plt.xlabel("x (deg. vis. angle)") +plt.ylabel("y (deg. vis. angle)") + +plt.subplot(2, 2, 3) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[0:512], + stimulus[512, 0:512], + color="black", + dashes=(1, 1), +) +plt.xlabel("x") +plt.ylabel("luminance (normalized)") + +plt.subplot(2, 2, 2) +plt.plot( + stimulus[256:768, 768], + np.linspace(visextent[0], visextent[1], stimulus.shape[0])[256:768], + color="black", + dashes=(1, 1), +) +plt.ylabel("y") +plt.xlabel("luminance (normalized)") +plt.show() + +# %% [markdown] +# In this image there are two White's stimuli: +# one oriented horizontally (left), and one oriented vertically (right). +# In both stimuli, +# the gray target patch embedded in the black phase of the grating +# looks brighter than the identical gray target embedded in the white phase. +# For the left stimulus, this means the left target looks brighter +# than the isoluminant right target +# (cut-through shown in bottom left panel). +# For the right stimulus, this means the top target looks brighter +# than the isoluminant bottom target +# (cut-through shown in top right panel). + +# %% [markdown] +# ## ODOG model fails +# +# This image, with two stimuli in different regions, +# challenges the original ODOG model by Blakeslee & McCourt (1997) + +# %% ODOG +ODOG = multyscale.models.ODOG_RHS2007(shape=stimulus.shape, visextent=visextent) +output_ODOG = ODOG.apply(stimulus) + +# %% Extract target predictions +targets_ODOG = [] +mask = np.load("WE_dual_mask.npy") +for idx in np.unique(mask.astype(int)): + if idx > 0: + targets_ODOG.append(np.median(output_ODOG[mask == idx])) + +# %% Visualise +plt.subplot(2, 2, 1) +plt.imshow(output_ODOG, cmap="coolwarm", extent=visextent) +plt.axhline(y=0, xmin=0, xmax=0.5, color="black", dashes=(1, 1)) +plt.axvline(x=8, ymin=0.25, ymax=0.75, color="black", dashes=(1, 1)) +plt.xlabel("x (deg. vis. angle)") +plt.ylabel("y (deg. vis. angle)") + +plt.subplot(2, 2, 3) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[0:512], + output_ODOG[512, 0:512], + color="black", +) +plt.xlabel("x") +plt.ylabel("brightness (ODOG)") + +plt.subplot(2, 2, 2) +plt.plot( + output_ODOG[256:768, 768], + np.linspace(visextent[0], visextent[1], stimulus.shape[0])[256:768], + color="black", +) +plt.ylabel("y") +plt.xlabel("Brightness (ODOG)") + +plt.subplot(2, 2, 4) +plt.bar(x=["left", "right", "top", "bottom"], height=targets_ODOG, color=plt.colormaps["tab10"](0)) +plt.ylim(-2, 2) +plt.axhline(y=0, linestyle="dashed", color="k") +plt.xlabel("Target region") +plt.ylabel("Brightness (ODOG; median)") + +plt.show() + +# %% [markdown] +# Here we see that the ODOG model incorrectly predicts that +# the targets in the black phases +# (left target in left/horizontal stimulus; +# top target in right/vertical stimulus) +# are _darker_ than the targets in the white phases. + + +# %% [markdown] +# **ODOG is affected by global energy** +# This unsuccessful prediction arises from the fact that +# the ODOG model normalizes each filter output at each location +# by the _image-wide_/_global_ energy of other filter outputs. +# This means that the (energy of the) right stimulus +# affects the normalization of the left stimulus, +# and vice versa. + + +# %% [markdown] +# ## LODOG model +# To overcome these kinds of issues, Robinson, Hammon, and de Sa (2007) +# developed the LODOG model variant. +# The LODOG model differs from the original/base ODOG model in its normalization step. +# Specifically, where the ODOG normalization calculates the global energy +# as the spatial RMS image-wide, +# the LODOG normalization calculates _local_ RMS, +# by averaging within a Gaussian window. +# +# Since the models differ only in the _normalization_ step, +# and not in the filterbank used in the _encoding_ step, +# the same filter(outputs) can be used for both models. + +# %% Instantiate LODOG +LODOG = multyscale.models.LODOG_RHS2007(shape=stimulus.shape, visextent=visextent) +assert np.array_equal(ODOG.bank.filters, LODOG.bank.filters) + +# %% Run LODOG normalization on -ODOG filter output +filters_output = ODOG.bank.apply(stimulus) +weighted_outputs = ODOG.weight_outputs(filters_output) + +norm_outputs = LODOG.normalize_outputs(weighted_outputs, eps=1e-6) +output_LODOG = norm_outputs.sum(axis=(0, 1)) + +# %% Extract target prediction +targets_LODOG = [] +for idx in np.unique(mask.astype(int)): + if idx > 0: + targets_LODOG.append(np.median(output_LODOG[mask == idx])) + +targets = pd.DataFrame( + { + "ODOG": targets_ODOG, + "LODOG": targets_LODOG, + }, + index=["Left", "Right", "Top", "Bottom"], +) + +# %% Visualise +plt.subplot(2, 2, 1) +plt.imshow(output_LODOG, cmap="coolwarm", extent=visextent) +plt.axhline(y=0, xmin=0, xmax=0.5, color="black", dashes=(1, 1)) +plt.axvline(x=8, ymin=0.25, ymax=0.75, color="black", dashes=(1, 1)) +plt.xlabel("x (deg. vis. angle)") +plt.ylabel("y (deg. vis. angle)") + +plt.subplot(2, 2, 3) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[0:512], + output_LODOG[512, 0:512], + color="black", +) +plt.xlabel("x") +plt.ylabel("brightness (LODOG)") + +plt.subplot(2, 2, 2) +plt.plot( + output_LODOG[256:768, 768], + np.linspace(visextent[0], visextent[1], stimulus.shape[0])[256:768], + color="black", +) +plt.ylabel("y") +plt.xlabel("brightness (LODOG)") + +ax = plt.subplot(2, 2, 4) +targets.plot(ax=ax, kind="bar") +plt.axhline(y=0.0, linestyle="dashed", color="k") +plt.ylim(-2, 2) +plt.xlabel("Target region") +plt.ylabel("Brightness (median)") + +plt.show() + + +# %% [markdown] +# This LODOG model _can_ successfully predict that +# the targets in the black phases +# (left target in left/horizontal stimulus; +# top target in right/vertical stimulus) +# are _brighter_ than the targets in the white phases. + +# %% [markdown] +# ## LODOG normalization (compared to ODOG) +# The power of the -ODOG models comes from their normalization step, +# in which information from all the filters regulates the activity of each filter output. +# Generally, this normalization consists of three parts: +# +# 1. The _normalizing coefficients_: weighted combinations of all filter outputs +# 2. Energy-calculating (as spatial averaging) of the normalizing coefficients +# 3. Divisive normalization, where a filter output is divided by the energy (2) +# of its normalizing coefficient (1) + +# %% [markdown] +# ### Normalizing coefficients +# The LODOG model uses the same _normalizing coefficients_ $\mathbf{N}$ +# as the original ODOG model. +# Briefly, $\mathbf{N}$ consists of $6 \times 7$ 2D ($X\times Y$) matrices, +# (one _normalizing coefficient_ $n_{o',s'}$ per filter $f_{o',s'}$ to normalize). +# Each normalizing coefficient $n_{o', s'}$ is a weighted combination of all filter outputs $F$, +# where the weights $w_{o', s', o, s}$ can vary. + +# %% Define weights +scale_norm_weights = multyscale.normalization.scale_norm_weights_equal(7) +orientation_norm_weights = multyscale.normalization.orientation_norm_weights(6) +norm_weights = multyscale.normalization.create_normalization_weights( + *filters_output.shape[:2], scale_norm_weights, orientation_norm_weights +) +assert np.array_equal(norm_weights, LODOG.normalization_weights) + +# %% Normalizing images as weighted combination (tensor dot-product) of filter outputs +normalizing_coefficients = multyscale.normalization.norm_coeffs(weighted_outputs, norm_weights) +assert np.array_equal(normalizing_coefficients, LODOG.norm_coeffs(weighted_outputs)) + +# Visualize each normalizing coefficient n_{o,s}, i.e. for each individual filter f_{o,s} +fig, axs = plt.subplots(*normalizing_coefficients.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalizing_coefficients.shape[:2]): + axs[o, s].imshow(normalizing_coefficients[o, s], cmap="coolwarm", extent=visextent) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") + +# %% [markdown] +# NOTE that all normalizing coefficients within a row are identical, +# i.e., all filters of an orientation get the same normalizing coefficient in the ODOG model. + +# %% [markdown] +# ### Normalize by energy +# Rather than normalizing by this weighted sum of all filter outputs at each pixel location, +# instead the -ODOG models normalize by +# the _energy_ of the normalizing coefficient. +# Energy here is expressed as the (spatial) root-mean-square of the signal. +# +# 1. Square each ($X \times Y = 1024 \times 1024$) pixel of the combined normalizing coefficient +# 2. Spatial average (e.g., mean) over pixels ($X, Y$) +# 3. Square-root of this mean +# +# $$ RMS(n_{o',s'}) = \sqrt{\mathrm{avg}(n_{o',s',x,y})^2} $$ +# +# %% [markdown] +# The base ODOG normalized calculates an image-wide mean: + +# %% Global image RMS +normalization_RMSs = np.ndarray(weighted_outputs.shape[:2]) +for o, s in np.ndindex(normalizing_coefficients.shape[:2]): + normalization_RMSs[o, s] = np.sqrt((normalizing_coefficients[o, s] ** 2).mean()) + +# Visualise +plt.pcolor(normalization_RMSs, cmap="Greens", edgecolors="k", linewidths=1, vmin=0, vmax=1) +plt.ylabel("Orientation $o'$") +plt.xlabel("Spatial scale $s'$") +plt.show() + +# %% [markdown] +# This heatmap represents the RMS value of the normalizing coefficient +# that each filter gets normalized by. +# +# As mentioned, this image-wide calculation +# means that the (energy of the) right stimulus +# affects the normalization of the left stimulus, +# and vice versa. + +# %% [markdown] +# #### L is for Local +# The way LODOG and ODOG differ, is in how the two models +# spatially average the normalizing energy. +# +# Robinson, Hammon, & de Sa (2007) consider the ODOG kind of global image averaging +# not plausible in the human visual system. +# Considering each pixel in the 4D filter output $F_{o,s,x,y}$ +# to represent the "activity" of one "neural channel/unit", +# a global image mean would imply that a neural channel is influenced +# not just by its close neighbors with similar/overlapping receptive fields, +# but also by units responding to distant visual regions. +# +# Instead in the LODOG model, units are influenced only by (spatially) nearby units. +# Instead of the global image mean, +# the LODOG model uses a Gaussian window to average over pixels, +# giving the _local_ (estimate of) energy: +# +# $$ RMS_\mathrm{local}(n_{o',s'},\sigma) = \sqrt{G(\sigma) * (n_{o',s',x,y})^2}$$ + +# %% Gaussian averaging window +sigma = LODOG.window_sigma +window = multyscale.filters.gaussian2d(LODOG.bank.x, LODOG.bank.y, (sigma, sigma)) + +# Normalize window to unit-sum (== spatial averaging filter) +window = window / window.sum() + +# Visualize Gaussian spatial averaging window +plt.subplot(2, 2, 1) +plt.imshow(window, extent=visextent, cmap="coolwarm") +plt.subplot(2, 2, 2) +plt.plot(LODOG.bank.x[int(window.shape[0] / 2)], window[int(window.shape[0] / 2), ...]) +plt.show() + +# %% [markdown] +# The function `multyscale.normalization.spatial_kernel_gaussian()` +# generates a ( $O\times S$ set of) Gaussian filters $G$, +# where each Gaussian spatial averaging window +# $G_{o',s'}$ is used to locally average filter $f_{o',s'}$. +# In the LODOG model, all $G$ are identical. +# +# The parameter $\sigma$ controls the spatial size of each $G(\sigma)$ Gaussian filter; +# thus this function takes in $O \times S$ $\sigma_{o', s'}$. +# In the LODOG model, all $\sigma_{o', s'}$ are identical. + +# %% All sigmas identical +print(LODOG.window_sigmas) + +# %% Generate Gaussian filters +spatial_windows = np.ndarray(filters_output.shape) +for o, s in np.ndindex(LODOG.window_sigmas.shape[:2]): + spatial_windows[o, s, :] = multyscale.normalization.spatial_kernel_gaussian( + LODOG.bank.x, LODOG.bank.y, LODOG.window_sigmas[o, s, :] + ) + +assert np.array_equal(spatial_windows[0, 0, :], window) +assert np.array_equal(spatial_windows, LODOG.spatial_kernels()) + +idx = (2, 3) + +plt.imshow(spatial_windows[*idx], cmap="coolwarm", extent=visextent) +plt.show() + +# %% [markdown] +# Applying this Gaussian window gives the _local_ (estimate of) of energy + +# %% Local energy estimates +normalization_local_energies = np.ndarray(normalizing_coefficients.shape) +for o, s in np.ndindex(normalizing_coefficients.shape[:2]): + coeff = normalizing_coefficients[o, s] ** 2 + energy = multyscale.filters.apply( + spatial_windows[o, s], coeff, padval=0 + ) + energy = np.sqrt(energy + 1e-6) # minor offset to avoid negatives/0's + normalization_local_energies[o, s, :] = energy + +assert np.allclose(normalization_local_energies, LODOG.norm_energies(normalizing_coefficients, eps=1e-6)) + +# Visualize each local RMS +fig, axs = plt.subplots(*normalization_local_energies.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalization_local_energies.shape[:2]): + axs[o, s].imshow(normalization_local_energies[o, s], cmap="coolwarm", extent=visextent) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + +# %% [markdown] +# NOTE that the _local_ energy here for each filter has to be a full $X \times Y$ pixel array, +# whereas the global energy in the base ODOG model is a single scalar value for each filter. +# Also NOTE that the energy in each _row_ (i.e., across scales) is constant, +# only along columns (i.e., across orientations) does the energy vary. +# +# Here we can see that +# the normalizing coefficients constructed from more vertically-oriented filer(outputs) +# -- top and bottom rows -- +# have more energy on the right hand side of the image. +# Conversely, +# the normalizing coefficients constructed from more horizontally-oriented filter(outputs) +# -- middle rows -- +# have more energy on the left hand side of the image. + + +# %% [markdown] +# ### Divisive normalization +# These matrices, expressing the local energy (Gaussian RMS) of the normalizing coefficients, +# form the denominator of the divisive normalization. +# +# Since the local energy is different in the two halves, +# we can see that the filters sensitive to vertical contrast (top/bottom row) +# will get normalized more strongly on the right hand side of the image. +# Conversely, the filters sensitive to horizontal contrast (middle row(s)) +# will get normalized more strongly on the left hand side of the image. +# Thus, the filter outputs to the two halves of the image, i.e., to the two stimuli, +# get normalized quite differently. +# +# We now divide each filter(output) $f_{o',s'}$ +# by the spatial RMS of the normalizing coefficient $n_{o',s'}$: +# $$f'_{o',s'} = \frac{f_{o',s'}}{RMS(n_{o',s'})}$$ + +# %% Divisive normalization +# Since the local RMSs tensor is the same (O, S, X, Y) shape as the filter outputs +# we can simply divide +normalized_outputs = weighted_outputs / (normalization_local_energies + 1e-6) +assert np.allclose(normalized_outputs, norm_outputs) + +# Visualize each normalized f'_{o',s'} +fig, axs = plt.subplots(*normalized_outputs.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalized_outputs.shape[:2]): + axs[o, s].imshow(normalized_outputs[o, s], cmap="coolwarm", extent=visextent) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + + +# %% [markdown] +# Thus, the full normalization schema of LODOG can be formulated as: +# $$ F' = \frac{f_{o',s',x,y}}{\sqrt{G(\sigma) * +# (\sum_{o=1}^{O}\sum_{s=1}^{S} w_{o',s',o,s}f_{o,s,x,y})^2}}$$ +# where $w_{o', s', o, s} = \begin{cases} +# 1 & o = o' \\ +# 0 & else +# \end{cases} +# $ + +# %% [markdown] +# ## Readout +# Finally, we readout a final model output +# by recombining the normalized filter outputs +# -- simply by summing them all up. + +# %% Recombine +recombined_outputs = np.sum(normalized_outputs, axis=(0, 1)) +assert np.allclose(recombined_outputs, output_LODOG) + +plt.subplot(2, 2, 1) +plt.imshow(recombined_outputs, cmap="coolwarm", extent=visextent) +plt.axhline(y=0, xmin=0, xmax=0.5, color="black", dashes=(1, 1)) +plt.axvline(x=8, ymin=0.25, ymax=0.75, color="black", dashes=(1, 1)) +plt.xlabel("x (deg. vis. angle)") +plt.ylabel("y (deg. vis. angle)") + +plt.subplot(2, 2, 3) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[0:512], + recombined_outputs[512, 0:512], + color="black", +) +plt.xlabel("x") +plt.ylabel("brightness (ODOG)") + +plt.subplot(2, 2, 2) +plt.plot( + recombined_outputs[256:768, 768], + np.linspace(visextent[0], visextent[1], stimulus.shape[0])[256:768], + color="black", +) +plt.ylabel("y") +plt.xlabel("Brightness (ODOG)") + +plt.subplot(2, 2, 4) +plt.bar( + x=["left", "right", "top", "bottom"], height=targets_LODOG, color=plt.colormaps["tab10"](1) +) +plt.ylim(-2, 2) +plt.axhline(y=0, linestyle="dashed", color="k") +plt.xlabel("Target region") +plt.ylabel("Brightness (LODOG; median)") + +plt.show() + + +# %% [markdown] +# In the horizontal cut, we can see that the model output +# is now greater for the targets in the black phases +# than for the targets in the white phaes +# -- in the same direction as the perceived brightness effect. + +# %% [markdown] +# The normalization by global energy in the original ODOG model, +# means this model fails to correctly predict for those images +# where the perceptual effect is present in more localized regions. +# The different regions of the image, which are perceived separately, +# affect each other's normalization. +# +# Instead, the LODOG model implements normalization by _local_ energy estimates. +# This prevents some of this cross-contamination by different image regions. +# As a result, the LODOG model overcomes the challenge to the original model, +# and correctly predicts the direction of effect for the combined stimulus. diff --git a/docs/normalization/normalization_ODOG.py b/docs/normalization/normalization_ODOG.py new file mode 100644 index 0000000..08e31f3 --- /dev/null +++ b/docs/normalization/normalization_ODOG.py @@ -0,0 +1,537 @@ +# %% [markdown] +# # ODOG normalization +# This Tutorial describes the rationale behind +# the normalization step of the original ODOG model (Blakeslee & McCourt, 1997), +# and its implementation in `multyscale`. + + +# %% Setup +# Third party libraries +import matplotlib.pyplot as plt +import numpy as np + +# Import local module +import multyscale + +# %% [markdown] +# ## Frontend + + +# %% [markdown] +# ### Example stimulus +# The example stimulus used for this exploration +# is a version of White's (1979) classic illusion, +# as also used by Robinson, Hammon, & de Sa (2007) as `WE_thick`. +# +# This stimulus is provided here +# as an NumPy `.npy` file, +# so it can be loaded in directly as a NumPy ndarray. +# +# The image of $1024 \times 1024$ pixels represent $32° \times 32°$ of the visual field; +# if centered, the visual extent of this stimulus subtends +# from $-16°$ on the left, to $16°$ on the right, +# and from $-16°$ on top, to $16°$ on the bottom. + +# %% Load example stimulus +stimulus = np.load("example_stimulus.npy") + +# visual extent, in degrees visual angle, +# same convention as pyplot (left, right, top, bottom): +# +# NOTE that Robinson, Hammon, & de Sa (2007) actually implement +# a visual extent slightly smaller: (1023/32) +visextent = tuple(np.asarray((-0.5, 0.5, -0.5, 0.5)) * (1023 / 32)) + +# Visualise +plt.subplot(1, 2, 1) +plt.imshow(stimulus, cmap="gray", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(1, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + stimulus[512, 256:768], + color="black", +) +plt.show() + +# %% [markdown] +# In the stimulus image on the left, the left gray patch appears brighter than the right gray patch. +# On the right, the pixel intensity/gray scale values along the horizontal cut +# (indicated by the dashed line in the image) are shown. +# These reveal that, in fact, the two gray patches are identical in their physical intensity. + + +# %% [markdown] +# ### Filterbank +# The -DOG family of models starts with a _multiscale spatial filtering_ frontend. +# This consists of a set of filters, $\mathbf{F}$ +# which span a range of spatial scales $S$ +# The _oriented_ -ODOG subfamily of models uses filters +# that also have one of several orientations $O$. +# Thus, filter $f_{o,s}$ is a single filter in the set $\mathbf{F}$ +# with orientation $o$ and scale $s$. +# Since each filter is 2D, it also has an implied $x,y$ pixels. +# +# As a result, we can also think of the 2D ($O\times S$) set $\mathbf{F}$ of filter(outputs), +# where each filter(output) $f_{o,s}$ is an image, +# as a 4D ($O \times S \times X \times Y$) set $\mathbf{I}$ of pixel intensities. +# $$ \mathbf{I}_{O \times S \times X \times Y} \equiv \mathbf{F}_{O \times S} $$ +# +# For the current topic, we use the default ODOG filterbank, +# which can be created by `multyscale.filterbanks.RHS2007()`, +# with 6 orientations, and 7 spatial scales. + +# %% Frontend filterbank of ODOG implementation by Robinson et al. (2007) +filterbank = multyscale.filterbanks.RHS2007(filtershape=stimulus.shape, visextent=visextent) + +# Get parameters +print(f"{filterbank.filters.shape[0]} orientations, {filterbank.filters.shape[1]} spatial scales") + +# Visualise filterbank +fig, axs = plt.subplots(*filterbank.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(*filterbank.shape[:2]): + axs[o, s].imshow(filterbank.filters[o, s, ...], cmap="coolwarm", extent=visextent) +fig.supxlabel("Spatial scale/freq. $s$") +fig.supylabel("Orientation $o$") +plt.show() + +# %% [markdown] +# In this visualisation of all filters, +# the rows differ in orientation of the filter +# and columns differ in the spatial scale of the filter. + +# %% [markdown] +# These filters are then convolved with the stimulus image. +# +# Filterbank-objects have an `apply(...)` method, +# which filters the input stimulus with the whole bank. +# The output is an $O \times S \times Y \times X$ tensor +# of channel responses. + +# %% Apply filterbank to (example) stimulus +filters_output = filterbank.apply(stimulus) + +# Visualise each filter output +fig, axs = plt.subplots(*filters_output.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(filters_output.shape[:2]): + axs[o, s].imshow( + filters_output[o, s], + cmap="coolwarm", + extent=visextent, + vmin=filters_output.min(), + vmax=filters_output.max(), + ) +fig.supxlabel("Spatial scale/freq. $s$") +fig.supylabel("Orientation $o$") +plt.show() + + +# %% [markdown] +# ### Recombination is not enough +# Simply recombining the outputs of these filters does not give rise to any effects; +# it merely gives a lossy reconstruction of the original stimulus, +# since the filters act as a decomposition. + +# %% Recombine +recombined_outputs = np.sum(filters_output, axis=(0, 1)) + +plt.subplot(1, 2, 1) +plt.imshow(recombined_outputs, cmap="gray", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(1, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], recombined_outputs.shape[1])[256:768], + recombined_outputs[512, 256:768], + color="black", +) +plt.show() + +# %% [markdown] +# In the horizontal cut, we can see that the model output +# is still approximately equal for the two gray target patches in the stimulus image. + + +# %% [markdown] +# ### Frontend: weighting filter(outputs) according to CSF +# In the -ODOG models, the filter outputs are weighted according to the CSF, +# that is, higher frequencies (smaller spatial scales), +# are weighted more strongly: +# "The seven spatial frequency filters [are weighted] across frequency +# using a power function with a slope of 0.1" +# +# The weights can be created using `multyscale.filterbank.scale_weights`, +# and applied using `multyscale.filterbanks.weight_oriented_multiscale_outputs`. + +# %% Weight individual filter(outputs) according to spatial size (frequency) +center_sigmas = [center[0] for (center, s) in filterbank.sigmas] +weights = multyscale.filterbanks.scale_weights(center_sigmas, slope=0.1) + +weighted_outputs = multyscale.filterbanks.weight_oriented_multiscale_outputs( + filters_output, weights +) + +# Visualise weighted filter outputs +fig, axs = plt.subplots(*weighted_outputs.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(weighted_outputs.shape[:2]): + axs[o, s].imshow( + weighted_outputs[o, s], + cmap="coolwarm", + extent=visextent, + vmin=weighted_outputs.min(), + vmax=weighted_outputs.max(), + ) +fig.supxlabel("Spatial scale/freq. $s$") +fig.supylabel("Orientation $o$") +plt.show() + +# %% [markdown] +# This does produce some effect, however, not the kind we wish to model: + +# %% Readout +recombined_outputs = np.sum(weighted_outputs, axis=(0, 1)) + +plt.subplot(1, 2, 1) +plt.imshow(recombined_outputs, cmap="gray", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(1, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], recombined_outputs.shape[1])[256:768], + recombined_outputs[512, 256:768], + color="black", +) +plt.show() + +# %% [markdown] +# In the horizontal cut, we can see that the model output +# is actually _higher_ for the right patch, compared to the left patch, +# which is _opposite_ from the perceived brightness difference! + + +# %% [markdown] +# ## Normalization +# The predictive power of the -ODOG models comes from their normalization step, +# in which information from all the filters regulates the activity of each filter output. +# +# Each filter(output) $f_{o',s'}$ gets divided by +# a _normalizing coefficient_ $n_{o',s'}$, +# i.e., for each filter $f_{o',s'}$, the normalized $f'$ is +# +# $$f'_{o',s'} := \frac{f_{o',s'}}{n_{o',s'}}$$ +# +# The normalizing coefficient $n_{o', s'}$ is made up of (a subset of) +# the responses in all $\mathbf{F}$ filter outputs. +# Thus, the tensor of normalizing coefficients $\mathbf{N}$ +# contains $6 \times 7$ 2D ($X \times Y$): +# one normalizing coefficient $n_{o',s'}$ per filter $f_{o',s'}$ to normalize. + +# %% [markdown] +# ### Combine filter outputs into normalizing coefficients +# +# In the original ODOG specification +# a filter only gets normalized by the filters with the same orientation. +# Thus, the normalizing coefficient $n_{o',s'}$ +# is a combination of only those $f_s$ with the same orientation ($o=o'$): +# +# $$n_{o',s', x, y} := \sum_{s=1}^{S} f_{o',s,x,y}$$ + +# %% Normalizing coefficients +norm_coeffs = np.zeros_like(weighted_outputs) +for o_prime, s_prime in np.ndindex(weighted_outputs.shape[:2]): # for each filter to normalize + for o, s in np.ndindex(weighted_outputs.shape[:2]): # loop over all filters + if o == o_prime: # same orientation + norm_coeffs[o_prime, s_prime] += weighted_outputs[ + o, s, : + ] # add this filter to normalizing coefficient + +# Plot each normalizing coefficient n_{o,s}, +# i.e., for each individual filter f_{o,s} +fig, axs = plt.subplots(*norm_coeffs.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(norm_coeffs.shape[:2]): + axs[o, s].imshow( + norm_coeffs[o, s], + cmap="coolwarm", + extent=visextent, + vmin=norm_coeffs.min(), + vmax=norm_coeffs.max(), + ) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + +# %% [markdown] +# NOTE that all normalizing coefficients within a row are identical, +# i.e., all filters of an orientation get the same normalizing coefficient in the ODOG model. +# This makes sense, +# since each filter $f_{o',...}$ gets normalized by all filters of that same orientation $o=o'$: +# +# $$n_{o',1} = n_{o',2} = ... = n_{o',7} = \sum_{s=1}^{S} f_{o',s,x,y}$$ + + +# %% [markdown] +# We can alternatively specify the combination +# as a weighted sum over all $\mathbf{F}$ such that +# $$n_{o',s',x,y}=\sum_{o=1}^{O}\sum_{s=1}^{S} w_{o',s',o,s}f_{o,s,x,y}$$ +# where the weight depends on whether $o$: +# $$w_{o',s',o,s} = \begin{cases} +# 1 & o = o' \\ +# 0 & else +# \end{cases}$$ +# +# Thus, $\mathbf{W}$ is a $O \times S$ set of $O \times S$ weights: +# for each $f_{o',s'}$ filter, we need to define $O \times S$ weights +# for whether each filter normalizes this one. + +# %% Define normalization weights +normalization_weights = np.ndarray(weighted_outputs.shape[:2] * 2) +for o_prime, s_prime in np.ndindex(weighted_outputs.shape[:2]): + for o, s in np.ndindex(weighted_outputs.shape[:2]): + if o == o_prime: + normalization_weights[o_prime, s_prime, o, s] = 1 # /filters_output.shape[1] + else: + normalization_weights[o_prime, s_prime, o, s] = 0 + +# Visualize weights +fig, axs = plt.subplots(*normalization_weights.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalization_weights.shape[:2]): + axs[o, s].pcolor( + normalization_weights[normalization_weights.shape[0] - o - 1, s], + cmap="Greens", + edgecolors="k", + linewidths=1, + vmin=0, + vmax=1, + ) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + +# %% [markdown] +# With a tensor (matrix) dot-product, +# these weights can be used to combine filter outputs into normalizing images + +# %% Normalizing images as weighted combination (tensor dot-product) of filter outputs +normalizing_coefficients = np.tensordot( + normalization_weights, weighted_outputs, axes=([0, 1], [0, 1]) +) + +# Visualize each normalizing coefficient n_{o,s}, +# i.e., for each individual filter f_{o,s} +fig, axs = plt.subplots(*normalizing_coefficients.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalizing_coefficients.shape[:2]): + axs[o, s].imshow(normalizing_coefficients[o, s], cmap="coolwarm", extent=visextent) + +# NOTE that these normalizing coefficients are identical the ones above: +assert np.allclose(normalizing_coefficients, norm_coeffs) + +# %% [markdown] +# In `multyscale`, such a matrix (tensor) of normalization weights +# can be easily and expressively created: +# by specifying the weights along each dimension (orientation, scale, etc.) +# and combining these into the single tensor. +# +# The function `multyscale.normalization.scale_norm_weights_equal()` +# defines how the $S$ spatial scales affect each other +# as a $(S \times S)$ matrix of weights: +# for each scale-to-be-normalized $s'$, +# it produces the weight given to each other scale $s$. +# +# In the ODOG model, all scales influence all (other) scales equally, +# thus this matrix is all $1$s. + +# %% Scale normalization weights +scale_norm_weights = multyscale.normalization.scale_norm_weights_equal(7) +plt.pcolor(scale_norm_weights, cmap="Greens", edgecolors="k", linewidths=1, vmin=0, vmax=1) +plt.xlabel("Spatial scale/freq. $s'$") +plt.ylabel("Spatial scale/freq. $s$") +plt.show() + +# %% [markdown] +# The function `multyscale.normalization.orientation_norm_weights()` similarly +# defines how the $O$ spatial scales affect each other, +# as a $(O \times O)$ matrix of weights: +# for each orientation-to-be-normalized $o'$, +# it produces the $O$ weights given to all other scales $o$. +# +# In the -ODOG models, filters only normalize other filters with the same orientation, +# i.e., when $o'=o$. Thus, this forms a diagonal matrix + +# %% Orientation normalization weights +orientation_norm_weights = multyscale.normalization.orientation_norm_weights(6) +plt.pcolor( + orientation_norm_weights[::-1, :], cmap="Greens", edgecolors="k", linewidths=1, vmin=0, vmax=1 +) +plt.xlabel("Orientation $o'$") +plt.ylabel("Orientation $o$") +plt.show() + +# %% [markdown] +# These normalization weights along each dimension +# are then combined into a single +# $(O' \times S' \times O \times S)$ matrix (tensor) of normalization weights. +# +# This tensor $w_{o',s',o,s}$ can be produced +# using the function `multyscale.normalization.create_normalization_weights()` +# from the separate sets of weights for orientations and scales. +# +# For the example where $(o'=3, s'=4)$, +# this means that all weights $w_{3,4,o,s}=1$ if $o==3$, regardless of $s$. + +# %% Normalization weights +norm_weights = multyscale.normalization.create_normalization_weights( + *weighted_outputs.shape[:2], scale_norm_weights, orientation_norm_weights +) +# NOTE that these are identical to the weights $w$ defined above. +assert np.array_equal(norm_weights, normalization_weights) + +# Visualize weights +fig, axs = plt.subplots(*norm_weights.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(norm_weights.shape[:2]): + axs[o, s].pcolor( + norm_weights[norm_weights.shape[0] - o - 1, s], + cmap="Greens", + edgecolors="k", + linewidths=1, + vmin=0, + vmax=1, + ) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + + +# %% [markdown] +# `multyscale` also provides convenience functionality +# for constructing the normalizing coefficients +# from such weights, and the filter outputs to be normalized. +# +# The function `multyscale.normalization.norm_coeffs()` creates these coefficients, +# and note that these are identical to the $N$ constructed above. + +# %% Identical normalizing coefficients +norm_coeffs = multyscale.normalization.norm_coeffs(weighted_outputs, normalization_weights) +assert np.allclose(norm_coeffs, normalizing_coefficients) + +# Visualize each normalizing coefficient n_{o,s}, +# i.e. for each individual filter f_{o,s} +fig, axs = plt.subplots(*normalizing_coefficients.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalizing_coefficients.shape[:2]): + axs[o, s].imshow(normalizing_coefficients[o, s], cmap="coolwarm", extent=visextent) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + +# %% [markdown] +# ### Normalize by energy +# Rather than normalizing by this weighted sum of all filter outputs at each pixel location, +# instead the -ODOG models normalize by +# the _energy_ of the normalizing coefficient. +# Energy here is expressed as the (spatial) root-mean-square of the signal. +# +# 1. Square each ($X \times Y = 1024 \times 1024$) pixel of the combined normalizing coefficient +# 2. Mean across all ($X \times Y = 1024 \times 1024$) pixels +# 3. Square-root of this mean +# +# $$ RMS(n_{o',s'}) = \sqrt{\frac{1}{XY}\sum_{x=1}^{X}\sum_{y=1}^{Y}(n_{o',s',x,y})^2} $$ +# +# NOTE: this is the _quadratic mean_ of the normalizing coefficient. +# +# This results in a single value for each $f_{o', s'}$ filter-to-be-normalized. + +# %% Global image RMS +normalization_RMSs = np.ndarray(normalizing_coefficients.shape[:2]) +for o, s in np.ndindex(normalizing_coefficients.shape[:2]): + normalization_RMSs[o, s] = np.sqrt((normalizing_coefficients[o, s] ** 2).mean()) + +# Visualise +plt.pcolor(normalization_RMSs[::-1, :], cmap="coolwarm", edgecolors="k", linewidths=1) +plt.ylabel("Orientation $o'$") +plt.xlabel("Spatial scale $s'$") +plt.show() + +# %% [markdown] +# This heatmap represents the RMS value of the normalizing coefficient +# that each filter gets normalized by. + +# %% [markdown] +# ### Divisive normalization +# These values, the energy (spatial RMS) of the normalizing coefficients, +# form the denominator of the divisive normalization. +# +# Thus, the normalized filter output $f'_{o', s'}$ +# is calculating by dividing each filter(output) $f_{o',s'}$ +# by the energy of the normalizing coefficient $n_{o',s'}$: +# $$f'_{o',s'} = \frac{f_{o',s'}}{RMS(n_{o',s'})}$$ + +# %% Divisive normalization +normalized_outputs = np.ndarray(weighted_outputs.shape) +for o, s in np.ndindex(weighted_outputs.shape[:2]): + f = weighted_outputs[o, s, ...] + n = normalization_RMSs[o, s] + normalized_outputs[o, s] = f / n + +# Visualize each normalized f'_{o',s'} +fig, axs = plt.subplots(*normalized_outputs.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalized_outputs.shape[:2]): + axs[o, s].imshow( + normalized_outputs[o, s], + cmap="coolwarm", + extent=visextent, + vmin=normalized_outputs.min(), + vmax=normalized_outputs.max(), + ) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + +# %% [markdown] +# In this visualisation of the normalized outputs, +# again, rows differ in orientation of the filter (being normalized) +# and columns differ in the spatial scale of the filter (being normalized). + +# %% [markdown] +# Thus, the full normalization schema of ODOG can be formulated as: +# $$ F' = +# \frac{f_{o',s',x,y}}{\sqrt{\frac{1}{XY}\sum_{y=1}^{Y} \sum_{x=1}^{X}(\sum_{o=1}^{O}\sum_{s=1}^{S} {w_{o',s',o,s} f_{o,s,x,y})^2}}} +# = \frac{f_{o',s',x,y}}{\sqrt{\mathbf{avg}((\mathbf{W} \cdot \mathbf{F})^2)}} +# $$ +# where $w_{o', s', o, s} = \begin{cases} +# 1 & o = o' \\ +# 0 & else +# \end{cases} +# $ + +# %% [markdown] +# ## Readout +# Finally, we readout a final model output +# by recombining the normalized filter outputs +# -- simply by summing them all up. + +# %% Recombine +recombined_outputs = np.zeros(normalized_outputs.shape[-2:]) +for o, s in np.ndindex(normalized_outputs.shape[:2]): + recombined_outputs += normalized_outputs[o, s] + +# Visualize model output +plt.subplot(1, 2, 1) +plt.imshow(recombined_outputs, cmap="coolwarm", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(1, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], recombined_outputs.shape[1])[256:768], + recombined_outputs[512, 256:768], + color="black", +) +plt.show() + +# %% [markdown] +# In the horizontal cut, we can see that the model output +# is now greater for the target patch on the left side of the stimulus, +# than for the patch on the right side +# -- in the same direction as the perceived brightness effect. + +# %% [markdown] +# Thus, the normalization step is crucial for the proper functioning of the ODOG model. +# Robinson, Hammon, & de Sa (2007) also demonstrate this, +# by comparing the full ODOG model with a version without normalization (UNODOG). +# The UNODOG model fails to predict the direction of effect more often than the full ODOG model +# (Table 2). diff --git a/docs/normalization/normalization_generalized.py b/docs/normalization/normalization_generalized.py new file mode 100644 index 0000000..889a32c --- /dev/null +++ b/docs/normalization/normalization_generalized.py @@ -0,0 +1,651 @@ +# %% [markdown] +# # Generalized -ODOG normalization +# Here we introduce a generalized formulation of the normalization step +# of the -ODOG family of models. +# In this generalized formulation, +# the F-, L-, and ODOG models differ only in parameterization of this step. +# We also show that this formulation is numerically equivalent to the original formulation. +# This is pointed to, and inspired by, a note and Figure 4e +# in Robinson, Hammon & de Sa (2007) + +# %% [markdown] +# ## Generalizing normalization steps +# Generally, the -ODOG normalization step consists of three parts: +# +# 1. The _normalizing coefficients_: weighted combinations of all filter outputs +# 2. Energy-calculating (as spatial averaging) of the normalizing coefficients +# 3. Divisive normalization, where a filter output is divided by the energy (2) +# of its normalizing coefficient (1) +# +# However, the different (F)(L)ODOG models differ in their exact implementation, +# particularly in steps (1) and (2). +# Initially, these differences may seem structural, +# different models using different calculations. +# Yet, as we show here, these can be expressed as parametric differences: +# the same structural components for each step, +# but with different parameter values. +# +# `multyscale` thus implements a _generalized_ version of the -ODOG normalization, +# such that the individual models are different parameterizations of this generalization. +# This formulation is intriguing because +# it exposes the possibility of other parameter values, +# but also because other forms of normalization +# (from other models of early vision) +# may map onto this or an even more generalized version. + +# %% Setup +# Third party libraries +import matplotlib.pyplot as plt +import numpy as np + +# Import local module +import multyscale + +# %% [markdown] +# ## Frontend +# The three models differ in their normalization step, +# but share the same filterbank frontend, +# so it's only necessary to apply this bank +# (and weight the filteroutputs) +# once. + +# %% [markdown] +# The example stimulus used for this exploration +# is a version of White's (1979) classic illusion, +# as also used by Robinson, Hammon, & de Sa (2007) as `WE_thick`. +# +# This stimulus is provided here +# as an NumPy `.npy` file, +# so it can be loaded in directly as a NumPy ndarray. +# +# The image of $1024 \times 1024$ pixels represent $32° \times 32°$ of the visual field; +# if centered, the visual extent of this stimulus subtends +# from $-16°$ on the left, to $16°$ on the right, +# and from $-16°$ on top, to $16°$ on the bottom. + +# %% Load example stimulus +stimulus = np.load("example_stimulus.npy") + +# visual extent, in degrees visual angle, +# same convention as pyplot (left, right, top, bottom): +# +# NOTE that Robinson, Hammon, & de Sa (2007) actually implement +# a visual extent slightly smaller: (1023/32) +visextent = tuple(np.asarray((-0.5, 0.5, -0.5, 0.5)) * (1023 / 32)) + +# Visualise +plt.subplot(1, 2, 1) +plt.imshow(stimulus, cmap="gray", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(1, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + stimulus[512, 256:768], + color="black", +) +plt.show() + +# %% [markdown] +# In the stimulus image on the left, the left gray patch appears brighter than the right gray patch. +# On the right, the pixel intensity/gray scale values along the horizontal cut +# (indicated by the dashed line in the image) are shown. +# These reveal that, in fact, the two gray patches are identical in their physical intensity. + +# %% Initialize models +ODOG = multyscale.models.ODOG_RHS2007(shape=stimulus.shape, visextent=visextent) +LODOG = multyscale.models.LODOG_RHS2007(shape=stimulus.shape, visextent=visextent) +FLODOG = multyscale.models.FLODOG_RHS2007(shape=stimulus.shape, visextent=visextent) +assert np.array_equal(LODOG.bank.filters, ODOG.bank.filters) +assert np.array_equal(FLODOG.bank.filters, LODOG.bank.filters) + +# %% Apply filterbank +# And weight outputs +filters_output = FLODOG.bank.apply(stimulus) +filters_output = FLODOG.weight_outputs(filters_output) + +# %% [markdown] +# ## Normalization +# Generally, the -ODOG normalization step consists of three parts: +# +# 1. The _normalizing coefficients_: weighted combinations of all filter outputs +# 2. Energy-calculating (as spatial averaging) of the normalizing coefficients +# 3. Divisive normalization, where a filter output is divided by the energy (2) +# of its normalizing coefficient (1) +# +# This can be formalized as: +# +# 1. $ n_{o', s', y, x} := w_{o', s'} \cdot \mathbf{F} $, where: +# - $\mathbf{F}$ is the whole set of filteroutputs, +# and each $f_{o, s, y, x}$ is a specific pixel ($y, x$) +# in the output of filter with specific orientation and spatial scale ($o, s$). +# Thus, $\mathbf{F}$ is a 4D tensor ($O, S, Y, X$) +# - $n_{o', s', y, x}$ is a pixel in the _normalizing coefficient_ for filter $o', s'$. +# - $\mathbf{w}$ is a set of interaction weights, indicating for each $(o', s')$ filter +# how all other $O, S$ filters combine. +# Thus, this is a 4D tensor ($O, S, O, S$). +# - $\cdot$ is a tensor dot-product operation +# +# 2. $ e_{o', s', y, x} := \sqrt{\mathrm{avg_{xy}}(n_{o, s}^2)} $, where: +# - $e_{o', s', y, x}$ is a pixel in the _energy estimate_ for filter $o', s'$ +# - $\mathrm{avg_{xy}}$ is a spatial averaging function, +# taking some average over pixels ($X, Y$) in the _normalizing coefficient_ $n_{o', s'}$. +# +# 3. $ f'_{o', s', y, x} := \frac{f_{o', s', y, x}}{e_{o', s', y, x}} $, where: +# - $\mathbf{F'}$ is the normalized set of filteroutputs; +# a 4D tensor of same dimensions and size as $\mathbf{F}$ +# +# Combined, this gives: +# +# $$ f'_{o, s, y, x} := +# \frac{f_{o, s, y, x}} +# {\sqrt{\mathrm{avg_{xy}}((\mathbf{w}\cdot\mathbf{F})^2)}} +# $$ +# +# All three (F)(L)ODOG models can be expressed in this form, +# by changing the implementation of parts (1) and (2), specifically. + + +# %% [markdown] +# ## Normalizing coefficients +# The first step in normalization is to define +# the normalizing coefficient ($n_{o', s'}$) for each filter ($f_{o, s}$). +# This normalizing coefficient is made up of (a subset of) +# the responses in all $\mathbf{F}$ filter outputs. +# Thus, the tensor of normalizing coefficients $\mathbf{N}$ +# contains $O \times S$ 2D ($Y \times X$): +# one normalizing coefficient $n_{o',s'}$ per filter $f_{o',s'}$ to normalize. +# +# In `multyscale`, we calculate these normalizing coefficients +# as a tensor dot-product beween a set of weights $\mathbf{w}$ +# and all filter outputs $\mathbf{F}$. +# The 4D tensor ($O, S, O, S$) of interaction weights $\mathbf{w}$ +# can be constructed by combined separately defined sets of weights +# for how different orientations interact, +# and how different spatial scale interact. + +# %% [markdown] +# In all -ODOG models, filters only normalize other filters with the same orientation, +# i.e., when $o'=o$. Thus, this forms a diagonal matrix of orientation interaction weights + +# %% Orientation normalization weights +orientation_norm_weights = multyscale.normalization.orientation_norm_weights(6) +plt.pcolor( + orientation_norm_weights[::-1, :], cmap="Greens", edgecolors="k", linewidths=1, vmin=0, vmax=1 +) +plt.xlabel("Orientation $o'$") +plt.ylabel("Orientation $o$") +plt.show() + +# %% [markdown] +# In the base ODOG model, all scales influence all (other) scales equally, +# thus the matrix of scale interaction weights is all $1$s. + +# %% Scale normalization weights +scale_norm_weights = multyscale.normalization.scale_norm_weights_equal(7) +plt.pcolor(scale_norm_weights, cmap="Greens", edgecolors="k", linewidths=1, vmin=0, vmax=1) +plt.xlabel("Spatial scale/freq. $s'$") +plt.ylabel("Spatial scale/freq. $s$") +plt.show() + +# %% [markdown] +# These normalization weights along each dimension +# are then combined into a single +# $(O' \times S' \times O \times S)$ matrix (tensor) of normalization weights. +# +# This tensor $w_{o',s',o,s}$ can be produced +# using the function `multyscale.normalization.create_normalization_weights()` +# from the separate sets of weights for orientations and scales. +# +# For the example where $(o'=3, s'=4)$, +# this means that all weights $w_{3,4,o,s}=1$ if $o=3$, regardless of $s$. + +# %% ODOG Normalization weights +interaction_weights = multyscale.normalization.create_normalization_weights( + *filters_output.shape[:2], scale_norm_weights, orientation_norm_weights +) + +# Visualize weights +fig, axs = plt.subplots(*interaction_weights.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(interaction_weights.shape[:2]): + axs[o, s].pcolor( + interaction_weights[interaction_weights.shape[0] - o - 1, s], + cmap="Greens", + edgecolors="k", + linewidths=1, + vmin=0, + vmax=1, + ) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + +# %% [markdown] +# These interaction weights then are used to combine all filter outputs $\mathbf{F}$ +# to create the normalizing coefficients $\mathbf{n}$ + +# %% Normalizing coefficients +normalizing_coefficients = multyscale.normalization.norm_coeffs(filters_output, interaction_weights) + +assert np.array_equal(normalizing_coefficients, ODOG.norm_coeffs(filters_output)) + + +# Visualize each normalizing coefficient n_{o,s}, i.e. +# the normalizer image for each individual filter f_{o,s} +fig, axs = plt.subplots(*normalizing_coefficients.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalizing_coefficients.shape[:2]): + axs[o, s].imshow(normalizing_coefficients[o, s], cmap="coolwarm", extent=visextent) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + +# %% [markdown] +# The LODOG model uses the same interaction weights +# (and thus same normalizing coefficients) +# as the base ODOG model. + +# %% +normalizing_coefficients_LODOG = LODOG.norm_coeffs(filters_output) +assert np.array_equal(normalizing_coefficients, normalizing_coefficients_LODOG) + +# %% [markdown] +# The FLODOG model uses a different set of interaction weights. +# Specifically, it does not weight all spatial scales equally. +# (It does weigh the orientations in the same manner as (L)ODOG). +# Instead of the equal weighting, +# the FLODOG model uses a 1D Gaussian as the weights profile: +# centered on the spatial scale of the filter being normalized +# and dropping off as a Gaussian function of the relative index the other spatial scales. + +# %% Define weights +scale_norm_weights_FLODOG = multyscale.normalization.scale_norm_weights_gaussian(7, sdmix=0.5) +assert np.array_equal(scale_norm_weights_FLODOG, FLODOG.scale_norm_weights) + +# %% +fig, axs = plt.subplots(2, 2, sharex="row", sharey="row") +axs[0, 0].pcolor( + LODOG.scale_norm_weights, + cmap="Greens", + edgecolors="k", + linewidths=1, + vmin=0, + vmax=1, +) +axs[0, 0].set_ylabel("scale of filter to normalize (idx)") +axs[0, 0].set_title("(L)ODOG weights") + +axs[0, 1].pcolor( + FLODOG.scale_norm_weights, + cmap="Greens", + edgecolors="k", + linewidths=1, + vmin=0, + vmax=1, +) +axs[0, 1].set_xlabel("scale of filter to normalize (idx)") +axs[0, 1].set_title("FLODOG weights") + +axs[1, 0].plot(LODOG.scale_norm_weights[3, :], color="black") +axs[1, 0].set_xlabel("scale of other filter (idx)") +axs[1, 0].set_ylabel("weight") + +axs[1, 1].plot(FLODOG.scale_norm_weights[3, :], color="black") +axs[1, 1].set_xlabel("scale of other filter (idx)") + +plt.show() + +# %% [markdown] +# Since these weights are strongly biased towards same/similar spatial scales, +# the resulting normalizing coefficients also more strongly resemble +# the filter outputs at these spatial scales. + +# %% Normalizing coefficients +normalizing_coefficients_FLODOG = FLODOG.norm_coeffs(filters_output) + +# Visualize each norm. coeff. +vmin = min(np.min(normalizing_coefficients_LODOG), np.min(normalizing_coefficients_FLODOG)) +vmax = max(np.max(normalizing_coefficients_LODOG), np.max(normalizing_coefficients_FLODOG)) + +fig, axs = plt.subplots(*normalizing_coefficients_LODOG.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalizing_coefficients_LODOG.shape[:2]): + axs[o, s].imshow( + normalizing_coefficients_LODOG[o, s], + cmap="coolwarm", + extent=visextent, + vmin=vmin, + vmax=vmax, + ) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +fig.suptitle("(L)ODOG") +plt.show() + +fig, axs = plt.subplots(*normalizing_coefficients_FLODOG.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalizing_coefficients_FLODOG.shape[:2]): + axs[o, s].imshow( + normalizing_coefficients_FLODOG[o, s], + cmap="coolwarm", + extent=visextent, + vmin=vmin, + vmax=vmax, + ) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +fig.suptitle("FLODOG") +plt.show() + +# %% [markdown] +# ### Summary +# In the first step of normalization, +# the construction of normalizing coefficients $\mathbf{n}$ +# as weighted combinations of all filter outputs $\mathbf{F}$, +# we can express the three models as using different sets of interaction weights $\mathbf{w}$. +# `multyscale` makes it easy and straightforward to implement these different weights, +# as well as to explore even further with different weights. + +# %% [markdown] +# ## Energy estimate through (localized) averaging +# The second step in -ODOG normalization, +# is to use an energy-estimate of, rather than the raw, normalizing coefficients. +# Rather than normalizing by this weighted sum of all filter outputs at each pixel location, +# instead the -ODOG models normalize by +# the _energy_ of the normalizing coefficient. +# Energy here is expressed as the (spatial) root-mean-square of the signal. +# +# 1. Square each ($X \times Y = 1024 \times 1024$) pixel of the normalizing coefficient $n_{o, s}$ +# 2. Average $\mathrm{avg_{yx}}$ across pixels +# 3. Square-root of this average +# +# $$ e_{o, s, y, x} := \sqrt{\mathrm{avg_{yx}}(n_{o, s}^2)} $$ +# +# NOTE: this is the _quadratic mean_ of the normalizing coefficient. + +# %% [markdown] +# The base ODOG normalization uses the _global image mean_ as the spatial average: +# +# $$ \mathrm{avg_{yx}}(n_{o', s', x, y}) = \frac{1}{YX} n_{o', s', x, y} $$ +# +# This results in a single energy estimate for each $(o', s')$. + +# %% Global image RMS +normalizing_coefficients_ODOG = normalizing_coefficients +ODOG_energies = np.sqrt((normalizing_coefficients_ODOG ** 2).mean(axis=(-1, -2))) +print(ODOG_energies.shape) + +# Visualise +plt.pcolor(ODOG_energies[::-1, :], cmap="coolwarm", edgecolors="k", linewidths=1) +plt.ylabel("Orientation $o'$") +plt.xlabel("Spatial scale $s'$") +plt.show() + +# %% [markdown] +# These energy estimates are then used as the denominator in the divisive normalization step (3) + +# %% Divisive normalization +ODOG_outputs = np.ndarray(filters_output.shape) +for o, s in np.ndindex(filters_output.shape[:2]): + f = filters_output[o, s, ...] + n = ODOG_energies[o, s] + ODOG_outputs[o, s] = f / n + +# Visualize each normalized f'_{o',s'} +fig, axs = plt.subplots(*ODOG_outputs.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(ODOG_outputs.shape[:2]): + axs[o, s].imshow( + ODOG_outputs[o, s], + cmap="coolwarm", + extent=visextent, + vmin=ODOG_outputs.min(), + vmax=ODOG_outputs.max(), + ) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + + +# %% [markdown] +# ### Energy estimates as matrices ("images") +# However, it makes conceptual more sense +# to have a denominator that is the same dimensions (Y, X) +# as the filter that is being normalized. + +# %% Energies as tensor +ODOG_energies_i = np.tile(ODOG_energies.reshape(6, 7, 1, 1), (1, 1, 1024, 1024)) +print(ODOG_energies_i.shape) + +# %% [markdown] +# This is equivalent to implementing the averaging in the denominator +# as a multiplication with a matrix where each component is the energy of the normalizer image. +# +# $$ f'_{o',s'} = \frac{f_{o',s'}}{e_{o', s', y,x}} $$ +# +# where +# +# $$ e_{o', s', y,x} = \sqrt{\frac{1}{YX}\sum_{y=1}^{Y} \sum_{x=1}^{X}n_{o',s'}^2} $$ +# +# This changes the dimensionality of the denominator, +# but not actually the result of the division, +# since image-by-image division is executed pixel-wise. + +# %% Divisive normalization +norm_i_outputs = filters_output / ODOG_energies_i + +assert np.array_equal(ODOG_outputs, norm_i_outputs) + + +# %% [markdown] +# ### Localized spatial averaging +# Instead of the global image mean, +# the (F)LODOG model uses a Gaussian window $G$ of some with $\sigma$ +# to average over pixels, +# giving the _local_ (estimate of) energy: +# +# $$ e_\mathrm{local}(n_{o',s'},\sigma) = \sqrt{G(\sigma) * (n_{o',s',x,y})^2} $$ +# +# Thus, difference between (F)LODOG and ODOG normalization is purely in the denominator $e$: +# +# $$ +# \begin{aligned} +# ODOG: e &= \sqrt{\frac{1}{YX}\sum_{y=1}^{Y} \sum_{x=1}^{X} n_{o',s'}^2} \\ +# (F)LODOG: e &= \sqrt{G(\sigma) * n_{o',s',x,y}^2} \end{aligned} +# $$ +# + +# %% Spatial Gaussian +spatial_kernels = np.ndarray(filters_output.shape) +for o, s in np.ndindex(LODOG.window_sigmas.shape[:2]): + spatial_kernels[o, s, :] = multyscale.normalization.spatial_kernel_gaussian( + LODOG.bank.x, LODOG.bank.y, LODOG.window_sigmas[o, s] + ) + +assert np.array_equal(spatial_kernels, LODOG.spatial_kernels()) + +# %% [markdown] +# Applying this Gaussian window gives the _local_ (estimate of) of energy + +# %% Local RMS +local_energies = np.ndarray(normalizing_coefficients_LODOG.shape) +for o, s in np.ndindex(normalizing_coefficients_LODOG.shape[:2]): + norm = normalizing_coefficients_LODOG[o, s] + norm = norm ** 2 + local_avg = multyscale.filters.apply( + norm, spatial_kernels[o, s], padval=0 + ) + local_energies[o, s] = np.sqrt(local_avg + 1e-6) # minor offset to avoid negatives/0's + +assert np.allclose(local_energies, LODOG.norm_energies(normalizing_coefficients_LODOG, eps=1e-6)) + +# Visualize each local RMS +fig, axs = plt.subplots(*local_energies.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(local_energies.shape[:2]): + axs[o, s].imshow(local_energies[o, s], cmap="coolwarm", extent=visextent) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + +# %% [markdown] +# Since these local energy estimates are not the same across the "image", +# here expressing the estimates as 2D matrices is essential. +# Therefore, it makes sense to also do this for the base ODOG model above, +# to make the comparison clearer. + +# %% Divisive normalization +# Since the local energies tensor is the same $(O, S, X, Y)$ shape as the filter outputs +# we can simply divide +LODOG_outputs = filters_output / (local_energies + 1e-6) + +# Visualize each normalized f'_{o',s'} +fig, axs = plt.subplots(*LODOG_outputs.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(LODOG_outputs.shape[:2]): + axs[o, s].imshow(LODOG_outputs[o, s], cmap="coolwarm", extent=visextent) +fig.supxlabel("Spatial scale/freq. $s'$") +fig.supylabel("Orientation $o'$") +plt.show() + +# %% [markdown] +# ### Implement global image averaging as a spatial filter + +# %% [markdown] +# The question then is whether global averaging +# +# $$ \frac{1}{YX}\sum_{y=1}^{Y}\sum_{x=1}^{X} \dots $$ +# +# can be reformulated as a convolution with 2D kernel +# +# $$ \mathbf{A}(..) * \dots $$ +# +# A convolution is essentially a repeated weighted sum, +# where the weight is the value in the filter +# and this sum is repeated for centering the filter +# on each pixel in the input image. +# If we can construct a kernel $\mathbf{A}$ ensures that for every pixel +# it weights all pixels in the input image by $\frac{1}{YX}$, +# then the output of the convolution is simply the global image average. + +# %% +# have doubly sized kernel so that pixel reaches each pixel in the average +spatial_kernel = np.ones((1024 * 2, 1024 * 2)) / 1024**2 + +img = filters_output[3, 4] +mean_filtered = multyscale.filters.apply(img, spatial_kernel, padval=0) + +assert np.allclose(mean_filtered, img.mean()) + +# %% [markdown] +# With this kind of spatial filtering, +# we run into the question of how to avoid edge artefects. +# For the pixels on the edges of the image, +# the kernel, when centered on one of those pixels, +# will extend beyond the edges of the image. +# In our case, this causes 2 problems: +# - if the kernel were the same size as the input image, +# then some pixels on the _other_ edge of the image +# will no longer fall in our image. +# Thus the spatial kernel $\mathbf{A}$ has $(2Y,2X)$ entries +# to assure that each pixel will take its average from the entire image. +# - what to do at the edges, to ensure that the filter has values to filter? +# We pad all the edges with the value $0$, +# so that no unnecessary information gets included into the averaging. + +# %% [markdown] +# The spatial averaging step in the ODOG normalization then becomes: +# +# $$ \mathbf{A} * (\dots) $$ +# +# and thus the energy step: +# +# $$ e = \sqrt{\mathbf{A} * n_{o',s'}^2} $$ +# + +# %% Global image averaging as filter +# calculate energy using the kernel +ODOG_energies_i2 = np.ndarray(normalizing_coefficients_ODOG.shape) +for o, s in np.ndindex(normalizing_coefficients_ODOG.shape[:2]): + norm_coeff = normalizing_coefficients_ODOG[o, s] + norm = norm_coeff ** 2 + spatial_average = multyscale.filters.apply(norm, spatial_kernel, padval=0) + energy = np.sqrt(spatial_average) + ODOG_energies_i2[o, s] = energy + +assert np.allclose(ODOG_energies_i2, ODOG_energies_i) + +# %% +ODOG_outputs_i2 = filters_output / ODOG_energies_i2 + +assert np.allclose(ODOG_outputs, ODOG_outputs_i2) + +# %% [markdown] +# ### Summary +# Thus, we can now reformulate the ODOG normalization as: +# +# $$ f'_{o',s'} = \frac{f_{o',s'}}{\sqrt{\mathbf{A} * (\mathbf{w} \cdot \mathbf{F})^2}} $$ +# +# where: +# - $w_{o', s', o, s} = \begin{cases} +# 1 & o = o' \\ +# 0 & else +# \end{cases}$ +# - $\cdot$ is a tensor dot-product +# - $\mathbf{A} * ...$ means the convolution with our 2D ($Y,X$) kernel +# that implements global averaging. +# + +# %% [markdown] +# ## Generalized formulation +# +# All three (F)(L)ODOG can thus be expressed as parameteric variations +# of the same overall divisive normalization structure: +# +# $$ f'_{o, s, y, x} := \frac{f_{o, s, y, x}}{\sqrt{\mathbf{A} * (\mathbf{w} \cdot \mathbf{F})^2}} $$ +# +# Both $\mathbf{w}$ and $\mathbf{A}$ depend on the specific model: +# - $\mathbf{w}$ is the same for LODOG and ODOG, where +# $ w_{o', s', o, s} = \begin{cases} +# 1 & o = o' \\ +# 0 & else +# \end{cases} $ +# For FLODOG, the $ w_{o', s'} $ also depends on the relative index of $ (s', s) $ +# - $ \mathbf{A} $ is Gaussian filter $\mathbf{G(\sigma)}$ for LODOG and FLODOG, +# where $\sigma = k$ and $\sigma = ks$, respectively. +# For ODOG, $ \mathbf{A} $ is a (larger) constant filter. +# However, this could even be understood as an infinite-width Gaussian $ \mathbf{G(\infty)} $ + +# %% [markdown] +# ### Pseudo-implementation + + +# %% +def divisive_normalization(filter_output, norm_energy): + return filter_output / norm_energy + + +def norm_energy(norm_coeff, spatial_kernel): + norm = norm_coeff**2 + spatial_average = multyscale.filters.apply(norm, spatial_kernel, padval=0) + energy = np.sqrt(spatial_average) + return energy + + +def norm_coeff(filter_outputs, normalization_weights): + coeffs = np.zeros_like(filter_outputs) + for o, s in np.ndindex(filter_outputs.shape[:2]): + weights = normalization_weights[o, s] + + # Tensor dot: multiply filters_output by weights, then sum over axes [0,1] + coeff = np.tensordot(filter_outputs, weights, axes=([0, 1], [0, 1])) + + # Accumulate + coeffs[o, s, ...] = coeff + + return coeffs + + +def spatial_kernel_ODOG(shape): + kernel = np.ones([dim * 2 for dim in shape]) + kernel /= kernel.sum() + return kernel + + +def spatial_kernel_LODOG(x, y, sigmas=[1.0, 1.0]): + kernel = multyscale.filters.gaussian2d(x, y, sigmas) + kernel /= kernel.sum() + return kernel diff --git a/docs/topics/topics.rst b/docs/normalization/topic_guides.md similarity index 69% rename from docs/topics/topics.rst rename to docs/normalization/topic_guides.md index 53f0418..513e170 100644 --- a/docs/topics/topics.rst +++ b/docs/normalization/topic_guides.md @@ -1,5 +1,4 @@ -Topic guides -============= +# Explanations Models of brightness perception are numerous and varied. One major differentiating factor, is whether a model includes @@ -10,12 +9,8 @@ that brightness perception phenomena emerge but are not explicitly modeled. A (super)family of models of the latter kind that have been particularly successful, are multiscale spatial filtering models, examplified in the -ODOG genus of models. -.. toctree:: - :maxdepth: 1 - :caption: Topic Guides: - - brightness_modeling - -.. image:: diagram.pdf - :width: 400 - :alt: Historical roadmap and modular overview of -ODOG genus of models \ No newline at end of file +```{toctree} +normalization_ODOG +normalization_LODOG +normalization_FLODOG +``` \ No newline at end of file diff --git a/docs/quickstart/example_stimulus.npy b/docs/quickstart/example_stimulus.npy new file mode 100644 index 0000000..a602b2d Binary files /dev/null and b/docs/quickstart/example_stimulus.npy differ diff --git a/docs/quickstart/example_stimulus.png b/docs/quickstart/example_stimulus.png new file mode 100644 index 0000000..5d4ddca Binary files /dev/null and b/docs/quickstart/example_stimulus.png differ diff --git a/docs/quickstart/example_stimulus_mask.npy b/docs/quickstart/example_stimulus_mask.npy new file mode 100644 index 0000000..0ea9b61 Binary files /dev/null and b/docs/quickstart/example_stimulus_mask.npy differ diff --git a/docs/quickstart/installation.md b/docs/quickstart/installation.md new file mode 100644 index 0000000..46f1b85 --- /dev/null +++ b/docs/quickstart/installation.md @@ -0,0 +1,90 @@ +# Install `multyscale` + +`multyscale` can be installed in several ways. +For most usecases, i.e., to _use_ the functions in `multyscale`, +we recommend installing from PyPI using `pip`. + +To adapt or contribute code, you will have to get the _source_ code from GitHub. + +:::::{tab-set} + +::::{tab-item} pip {fab}`python` +`pip` can install the latest version of `multyscale` directly from PyPI + +```python +pip install multyscale +``` + +:::{admonition} Install a different version + :class: dropdown + +`pip` by default install the latest version of package. +To install a different version, simply specify the version number, +either an exact version: +```python +pip install "multyscale==1.0.0" +``` +or a conditional version: +```python +pip install "multyscale<=1.0.0" +``` +(for any version before `1.0.0`). + +`multyscale` uses approximately [Semantic Versioning](https://semver.org/) / +[PEP440](https://peps.python.org/pep-0440/). +This means that versions with the same MAJOR version number are backwards compatible: +code written using version `1.N.x` will work under `1.N+1.x` +but is not guaranteed to work under version `2.x.x`. +Versions with higher MINOR version number (`x.N+1.x`) introduce new features. +Versions with higher PATCH number (`x.x.N+1`) fix bugs. + +::: +:::: + + +::::{tab-item} source {fab}`github` + +1. Clone the repository from GitHub: + + ```bash + git clone git@github.com:computational-psychology/multyscale.git + ``` + +2. `multyscale` can then be installed using pip. + From top-level directory run: + + ```python + pip install . + ``` + + to install to your local python library. + +:::{admonition} For developers + :class: dropdown + +```python +pip install -e ".[dev,docs]" +``` + +for an editable install (`-e`) which makes changes to files immediately usable, +rather than having to reinstall the package after every change; +and to install the development and documentation dependencies. +::: +:::: + +::::: + + +## Dependencies +Dependencies should be automatically installed (at least using `pip`). +`multyscale`s required dependencies are: +- [NumPy](numpy) +- [SciPy](scipy) + +This documentation contains interactive tutorials and demos, +in the form of Jupyter Notebooks. +To run this, additional dependencies are required: + - Jupyter Notebook (or JupyterLab) + - ipywidgets +These demo notebooks can also be opened on Binder, +which should install all the necessary dependencies automatically. \ No newline at end of file diff --git a/docs/quickstart/quickstart.md b/docs/quickstart/quickstart.md new file mode 100644 index 0000000..6677120 --- /dev/null +++ b/docs/quickstart/quickstart.md @@ -0,0 +1,14 @@ +# Quick start with `multyscale` + +The following pages serve as a quickstart quide +to working with the `multyscale` library. +They are meant to introduce the core functionalities of the library, +and assume knowledge of and familiarity with +the concepts of multiscale spatial filtering models. + +The rest of this "book" is a more detailed introduction to the topic. + +```{toctree} +run_(F)(L)ODOG +construct_new +``` \ No newline at end of file diff --git a/docs/quickstart/run_(F)(L)ODOG.py b/docs/quickstart/run_(F)(L)ODOG.py new file mode 100644 index 0000000..05114b0 --- /dev/null +++ b/docs/quickstart/run_(F)(L)ODOG.py @@ -0,0 +1,497 @@ +# %% [markdown] +# # Run an existing (-ODOG) model +# This Recipe describes how to run the existing -ODOG models +# (Blakeslee & McCourt, 1999; Robinson, Hammon, & de Sa, 2007) +# as implemented in the module. + +# %% Setup +# Third party libraries +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +# Import local module +import multyscale + +# %% [markdown] +# ## Example stimulus +# The example stimulus used for this exploration +# is a version of White's (1979) classic illusion, +# as also used by Robinson, Hammon, & de Sa (2007) as `WE_thick`. +# +# This stimulus is provided here +# as an NumPy `.npy` file, +# so it can be loaded in directly as a NumPy ndarray. +# +# The image of $1024 \times 1024$ pixels represent $32° \times 32°$ of the visual field; +# if centered, the visual extent of this stimulus subtends +# from $-16°$ on the left, to $16°$ on the right, +# and from $-16°$ on top, to $16°$ on the bottom. + +# %% Load example stimulus +stimulus = np.load("example_stimulus.npy") + +# visual extent, in degrees visual angle, +# same convention as pyplot (left, right, top, bottom): +# +# NOTE that Robinson, Hammon, & de Sa (2007) actually implement +# a visual extent slightly smaller: (1023/32) +visextent = tuple(np.asarray((-0.5, 0.5, -0.5, 0.5)) * (1023 / 32)) + +# Visualise +plt.subplot(1, 2, 1) +plt.imshow(stimulus, cmap="gray", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(1, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + stimulus[512, 256:768], + color="black", +) +plt.show() + +# %% [markdown] +# In the stimulus image on the left, the left gray patch appears brighter than the right gray patch. +# On the right, the pixel intensity/gray scale values along the horizontal cut +# (indicated by the dashed line in the image) are shown. +# These reveal that, in fact, the two gray patches are identical in their physical intensity. + + +# %% [markdown] +# ## (F)(L)ODOG models +# The ODOG model (Blakeslee & McCourt, 1997), +# and later derivations LODOG and FLODOG (Robinson, Hammon, & de Sa, 2007) +# are _image-computable model_s of brightness perception. +# As image computable models, they take any arbitrary image-array (2D) as input, +# and output another 2D array +# where each pixel is the predicted perceived brightness for the corresponding pixel in the input. +# +# This recipe demonstrates how to apply these models in one integrated run, +# as well as breaks it down into its constituent components +# and executes these step-wise. +# +# These models are implement in `multyscale.models`, +# where the ODOG model is implemented as the class `ODOG_RHS2007` +# (note that this implementation mimics that of Robinson, Hammon, & de Sa, 2007, +# and deviates slightly from the Blakeslee & McCourt original implementation). +# The specification of the model depends on the image resolution and visual extent of the model, +# so requires these as constructor arguments. + + +# %% Create model +model_ODOG = multyscale.models.ODOG_RHS2007(stimulus.shape, visextent) + +# %% [markdown] +# ## Running the ODOG model +# The most straightforward way of running the model, +# is by calling the `apply()` method + +# %% Apply model +output_ODOG = model_ODOG.apply(stimulus) + +# Visualise output +plt.subplot(1, 2, 1) +plt.imshow(output_ODOG, cmap="coolwarm", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(1, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + output_ODOG[512, 256:768], + color="black", +) +plt.show() + +# %% [markdown] +# In the horizontal cut on the right, we can see that the model output +# is greater for the target patch on the left side of the stimulus, +# than for the patch on the right side +# -- also indicated in the output image by the warmer color on the left than on the right. +# This is in the same direction as the perceived brightness effect. + + +# %% [markdown] +# ### Readout +# From this output 2D-array, +# which represents the (predicted) brightness at each pixel location in the original input image, +# we can readout predicted brightness for target regions. +# +# To do this, we use a _mask_-array: +# a 2D-array with the same $Y \times X$ shape as the input image +# where each pixel is assigned to a region of interest. +# At each pixel location, +# an integer index indicates which region of interest it belongs to. + +# %% Mask +mask = np.load("example_stimulus_mask.npy") + +fig, (ax_im, ax_mask) = plt.subplots(1, 2) +ax_im.imshow(stimulus, cmap="gray", extent=visextent) +ax_mask.imshow(mask, extent=visextent) +plt.show() + +# %% [markdown] +# By indexing only those pixels in the image +# belong to a given masked region, +# we can average (median) the intensity value in this region. + +# %% Extract targets intensities +target_intensities = [] +for idx in np.unique(mask.astype(int)): + if idx > 0: + target_intensities.append(np.median(stimulus[mask == idx])) + +# Visualize +plt.bar(x=["left", "right"], height=target_intensities, color="k") +plt.ylim([0, 1]) +plt.xlabel("Target region") +plt.ylabel("Intensity (median)") +plt.show() + +# %% [markdown] +# In the original stimulus, both target regions have the same median intensity. +# In the output array from the ODOG model, however, +# the two target regions now have a different median predicted brightness: + +# %% Extract target outputs +targets_ODOG = [] +for idx in np.unique(mask.astype(int)): + if idx > 0: + targets_ODOG.append(np.median(output_ODOG[mask == idx])) + +# Visualize +plt.bar(x=["left", "right"], height=targets_ODOG, color="k") +plt.axhline(y=0.0, linestyle="dashed", color="k") +plt.xlabel("Target region") +plt.ylabel("Brightness (ODOG; median)") +plt.show() + +# %% [markdown] +# Here we get a _quantitative_ prediction from the model output. +# This quantitative prediction is in the same direction. +# as the perceived brightness effect. + +# %% [markdown] +# ## Comparing (F)(L)ODOG models +# The `multyscale.models` module also implements the LODOG and FLODOG models +# (Robinson, Hammon, & de Sa, 2007), +# as `.LODOG_RHS2007` and `.FLODOG_RHS2007` respectively. +# Running these models works the same way as the base ODOG model. + +# %% Initialize all three models +model_LODOG = multyscale.models.LODOG_RHS2007(stimulus.shape, visextent) +model_FLODOG = multyscale.models.FLODOG_RHS2007(stimulus.shape, visextent) + +# %% Integrated runs of all three models +output_LODOG = model_LODOG.apply(stimulus) +output_FLODOG = model_FLODOG.apply(stimulus) + +# %% Compare outputs +# Stimulus +plt.subplot(4, 2, 1) +plt.imshow(stimulus, cmap="gray", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(4, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + stimulus[512, 256:768], + color="black", +) + +# ODOG +plt.subplot(4, 2, 3) +plt.imshow(output_ODOG, cmap="coolwarm", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(4, 2, 4) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + output_ODOG[512, 256:768], + color="black", +) + +# LODOG +plt.subplot(4, 2, 5) +plt.imshow(output_LODOG, cmap="coolwarm", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(4, 2, 6) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + output_LODOG[512, 256:768], + color="black", +) + +# FLODOG +plt.subplot(4, 2, 7) +plt.imshow(output_FLODOG, cmap="coolwarm", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(4, 2, 8) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + output_FLODOG[512, 256:768], + color="black", +) + +plt.show() + +# %% [markdown] +# From these cut througs, +# we see that the three models overall make similar predictions for this stimulus +# but that the brightness profile is locally quite different. + +# %% [markdown] +# Again, we can extract predictions for each target region +# from each model output. + +# %% Extract target outputs +targets_LODOG = [] +targets_FLODOG = [] +for idx in np.unique(mask.astype(int)): + if idx > 0: + targets_LODOG.append(np.median(output_LODOG[mask == idx])) + targets_FLODOG.append(np.median(output_FLODOG[mask == idx])) + +targets = pd.DataFrame( + { + "ODOG": targets_ODOG, + "LODOG": targets_LODOG, + "FLODOG": targets_FLODOG, + }, + index=["Left", "Right"], +) + +# Visualize +targets.plot(kind="bar") +plt.axhline(y=0.0, linestyle="dashed", color="k") +plt.xlabel("Target region") +plt.ylabel("Brightness (median)") +plt.show() + +# %% [markdown] +# Here we see that all three models +# _qualitative_ predict the same direction of effect, +# but make different _quantitative predictions +# for the magnitude of the brightness difference between target regions. + +# %% [markdown] +# ## Model components, and step-wise execution +# The (F)(L)ODOG models consist of several components, or steps: +# +# 1. The _multiscale spatial filering_ frontend +# _encodes_ the input (stimulus) image +# into a representation at multiple scales/spatial frequencies, and multiple orientations. +# 2. The normalization step regulates activity in each frequency-orientation channel, +# by normalizing it by the energy in all frequency bands +# 3. From the normalized, multichannel representation, +# a single 2D brightness "map" is readout, by linearly combining channels. +# From this, target predictions can be further decoded. +# +# +# The implementations in `multyscale` have the convenience `.apply()` method +# that runs an entire model in one go, +# but also provides access to each of the steps individually. + + +# %% [markdown] +# ### Frontend: filterbank +# _Multiscale spatial filtering_ models are defined by their multiscale spatial filtering frontend: +# A bank of filters $\mathbf{F}$ which span a range of spatial scales $S$ +# are convolved with the stimulus image. +# The general -DOG family of models uses +# *D*ifference-*o*f-*G*aussian filters, +# which act as intensity-difference detecters. +# The scale of the filter directly determines the spatial frequency selectivity. +# The -ODOG subfamily of models uses *O*riented DoG filters +# that also have one of several orientations $O$. +# Thus, filter $f_{o,s}$ is a single filter in the set $\mathbf{F}$ +# with orientation $o$ and scale $s$. +# +# Since each filter is 2D, it also has an implied $x,y$ pixels. +# As a result, we can also think of the 2D ($O\times S$) set $\mathbf{F}$ of filter(outputs), +# where each filter(output) $f_{o,s}$ is an image, +# as a 4D ($O \times S \times X \times Y$) set $\mathbf{I}$ of pixel intensities. +# $$ \mathbf{I}_{O \times S \times X \times Y} \equiv \mathbf{F}_{O \times S} $$ +# +# The filterbank object is stored the `model.bank` attribute of the model object. +# These, and other, filterbanks are created using the `multyscale.filterbanks`-module. +# All three models here use the same filterbank +# with 6 different orientation as 7 different spatial scales (spatial frequency selecitivity). + + +# %% Get parameters +print(f"{model_ODOG.bank.shape[0]} orientations, {model_ODOG.bank.shape[1]} spatial scales") + +# All three models have identical filters +assert np.array_equal(model_ODOG.bank.filters, model_LODOG.bank.filters) +assert np.array_equal(model_ODOG.bank.filters, model_FLODOG.bank.filters) +assert np.array_equal(model_LODOG.bank.filters, model_FLODOG.bank.filters) + +# Visualise filterbank +fig, axs = plt.subplots(*model_ODOG.bank.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(model_ODOG.bank.shape[:2]): + axs[o, s].imshow(model_ODOG.bank.filters[o, s, ...], cmap="coolwarm", extent=visextent) + +# %% [markdown] +# In this visualisation of all filters, +# the rows differ in orientation of the filter +# and columns differ in the spatial scale of the filter. + + +# %% [markdown] +# ### Frontend: filtering +# These filters are then convolved with the stimulus image. +# +# Filterbank-objects have an `apply(...)` method, +# which filters the input stimulus with the whole bank. +# The output is an $O \times S \times Y \times X$ tensor +# of channel responses. + +# %% Apply filterbank to example stimulus +filters_output = model_ODOG.bank.apply(stimulus) + +print(f"{filters_output.shape} channel responses") + + +# Visualise each filter output +fig, axs = plt.subplots(*filters_output.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(filters_output.shape[:2]): + axs[o, s].imshow(filters_output[o, s], cmap="coolwarm", extent=visextent) + +# %% [markdown] +# In this visualisation of filter output, +# again, rows differ in orientation of the filter +# and columns differ in the spatial scale of the filter. + + +# %% [markdown] +# ### Frontend: weighting filter(outputs) according to CSF +# In the -ODOG models, the filter outputs are weighted according to the CSF, +# that is, higher frequencies (smaller spatial scales), +# are weighted more strongly: +# "The seven spatial frequency filters [are weighted] across frequency +# using a power function with a slope of 0.1" +# +# This slope is an attribute of the `ODOG_RHS2007` object, +# which then determines the `scale_weights` attribute. +# The weights can be applied through the `.weight_outputs` method. + +# %% Weight individual filter(outputs) according to spatial size (frequency) +print(f"Slope of weights {model_ODOG.weights_slope}, gives weights:") +print(f"{model_ODOG.scale_weights}") + +weighted_outputs = model_ODOG.weight_outputs(filters_output) + +# Visualise weighted filter outputs +fig, axs = plt.subplots(*weighted_outputs.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(weighted_outputs.shape[:2]): + axs[o, s].imshow(weighted_outputs[o, s], cmap="coolwarm", extent=visextent) + +# Since this weighting just scales the output, +# it does not affect this visualisation of the filter outputs. + +# %% [markdown] +# ### Normalization +# Each filter output then gets normalized +# by a combination of the other filter outputs. + +# %% Normalize filter outputs +normalized_outputs = model_ODOG.normalize_outputs(weighted_outputs) + +# Visualise normalized filter outputs +fig, axs = plt.subplots(*normalized_outputs.shape[:2], sharex="all", sharey="all") +for o, s in np.ndindex(normalized_outputs.shape[:2]): + axs[o, s].imshow(normalized_outputs[o, s], cmap="coolwarm", extent=visextent) + +# %% [markdown] +# ### Readout +# To readout from this (normalized) multiscale representation, +# first we integrate over orientations and scales, +# resulting in a 2D $(Y \times X)$ array again + +# %% Sum normalized filter outputs into single array +output_stepwise = normalized_outputs.sum((0, 1)) + +# Visualise output +plt.subplot(1, 2, 1) +plt.imshow(output_stepwise, cmap="coolwarm", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(1, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + output_stepwise[512, 256:768], + color="black", +) +plt.show() + +# %% [markdown] +# In the horizontal cut on the right, we can see that the model output +# is greater for the target patch on the left side of the stimulus, +# than for the patch on the right side +# -- also indicated in the output image by the warmer color on the left than on the right. +# This is in the same direction as the perceived brightness effect. + +# %% [markdown] +# This stepwise running of the model +# is exactly what the `model.apply(...)` function does, +# thus the output is identical. + +# %% Compare runs +# Stimulus +plt.subplot(3, 2, 1) +plt.imshow(stimulus, cmap="gray", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(3, 2, 2) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + stimulus[512, 256:768], + color="black", +) + +# Integrated run +plt.subplot(3, 2, 3) +plt.imshow(output_ODOG, cmap="coolwarm", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(3, 2, 4) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + output_ODOG[512, 256:768], + color="black", +) + +# Stepwise +plt.subplot(3, 2, 5) +plt.imshow(output_stepwise, cmap="coolwarm", extent=visextent) +plt.axhline(y=0, color="black", dashes=(1, 1)) +plt.subplot(3, 2, 6) +plt.plot( + np.linspace(visextent[2], visextent[3], stimulus.shape[1])[256:768], + output_stepwise[512, 256:768], + color="black", +) + +# Check (assert) that the two runs are identical +assert np.array_equal(output_ODOG, output_stepwise) + + +# %% [markdown] +# ### Overlap and distinction between F-L-ODOG steps +# The three models-species are distinct, +# and their output is different, +# but they also share some components. +# +# All three models share the same frontend (filterbank, weighting), +# but their normalization steps are different. +# Thus, one can get the (weighted) filter output from only one model, +# and run only the distinct normalization steps from the other models +# to produce the three outputs. + +# %% Apply different normalizations +normalized_ODOG = model_ODOG.normalize_outputs(weighted_outputs) +normalized_LODOG = model_LODOG.normalize_outputs(weighted_outputs) +normalized_FLODOG = model_FLODOG.normalize_outputs(weighted_outputs) + +output_stepwise_ODOG = normalized_ODOG.sum((0, 1)) +output_stepwise_LODOG = normalized_LODOG.sum((0, 1)) +output_stepwise_FLODOG = normalized_FLODOG.sum((0, 1)) + +assert np.array_equal(output_ODOG, output_stepwise_ODOG) +assert np.array_equal(output_LODOG, output_stepwise_LODOG) +assert np.array_equal(output_FLODOG, output_stepwise_FLODOG) diff --git a/docs/reference/api.md b/docs/reference/api.md new file mode 100644 index 0000000..81bb049 --- /dev/null +++ b/docs/reference/api.md @@ -0,0 +1,11 @@ +# `multyscale` API +```{eval-rst} +.. autosummary:: + :toctree: _api + :recursive: + + multyscale.filters + multyscale.filterbanks + multyscale.normalization + multyscale.models +``` \ No newline at end of file diff --git a/docs/reference/multyscale.filterbank.rst b/docs/reference/multyscale.filterbank.rst deleted file mode 100644 index e24992b..0000000 --- a/docs/reference/multyscale.filterbank.rst +++ /dev/null @@ -1,7 +0,0 @@ -multyscale.filterbank module -============================ - -.. automodule:: multyscale.filterbank - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/reference/multyscale.filters.rst b/docs/reference/multyscale.filters.rst deleted file mode 100644 index 9dad7db..0000000 --- a/docs/reference/multyscale.filters.rst +++ /dev/null @@ -1,7 +0,0 @@ -multyscale.filters module -========================= - -.. automodule:: multyscale.filters - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/reference/multyscale.models.rst b/docs/reference/multyscale.models.rst deleted file mode 100644 index 16581ec..0000000 --- a/docs/reference/multyscale.models.rst +++ /dev/null @@ -1,7 +0,0 @@ -multyscale.models module -======================== - -.. automodule:: multyscale.models - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/reference/multyscale.normalization.rst b/docs/reference/multyscale.normalization.rst deleted file mode 100644 index 3e94ac4..0000000 --- a/docs/reference/multyscale.normalization.rst +++ /dev/null @@ -1,7 +0,0 @@ -multyscale.normalization module -=============================== - -.. automodule:: multyscale.normalization - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/reference/multyscale.rst b/docs/reference/multyscale.rst deleted file mode 100644 index 220c47a..0000000 --- a/docs/reference/multyscale.rst +++ /dev/null @@ -1,16 +0,0 @@ -API reference -========================= - -.. automodule:: multyscale - :members: - :undoc-members: - :show-inheritance: - -.. toctree:: - :maxdepth: 4 - - multyscale.filterbank - multyscale.filters - multyscale.models - multyscale.normalization - multyscale.utils diff --git a/docs/reference/multyscale.utils.rst b/docs/reference/multyscale.utils.rst deleted file mode 100644 index accfea1..0000000 --- a/docs/reference/multyscale.utils.rst +++ /dev/null @@ -1,7 +0,0 @@ -multyscale.utils module -======================= - -.. automodule:: multyscale.utils - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/topics/diagram.pdf b/docs/topic_guides/diagram.pdf similarity index 100% rename from docs/topics/diagram.pdf rename to docs/topic_guides/diagram.pdf diff --git a/docs/topic_guides/history.md b/docs/topic_guides/history.md new file mode 100644 index 0000000..2f0568f --- /dev/null +++ b/docs/topic_guides/history.md @@ -0,0 +1,9 @@ +# Selected history of multiscale filtering for brightness + +1. ... +2. Foley & McCourt (1985) - Spatial filtering of grating induction +3. Moulden & Kingdom (1989) - Spatial filtering White's effect +4. Moulden & Kingdom (1991) - Multiscale DoG filter model of White's effect +5. Blakeslee & McCourt (1997) - Multiscale DoG model of White's _and_ grating induction +6. Blakeslee & McCourt (1999) - (multiscale) Oriented DoG (ODOG) model of brightness perception +7. Robinson, Hammon, & de Sa (2007) - (F)(L)ODOG models of brightness perception \ No newline at end of file diff --git a/docs/topic_guides/overview.md b/docs/topic_guides/overview.md new file mode 100644 index 0000000..d30f186 --- /dev/null +++ b/docs/topic_guides/overview.md @@ -0,0 +1 @@ +# Overview of `multyscale` \ No newline at end of file diff --git a/docs/topics/brightness_modeling.rst b/docs/topics/brightness_modeling.rst deleted file mode 100644 index b6f77af..0000000 --- a/docs/topics/brightness_modeling.rst +++ /dev/null @@ -1,2 +0,0 @@ -Modeling brightness perception -=============================== diff --git a/docs/tutorials/demo_filters.nblink b/docs/tutorials/demo_filters.nblink deleted file mode 100644 index ca057e5..0000000 --- a/docs/tutorials/demo_filters.nblink +++ /dev/null @@ -1,3 +0,0 @@ -{ - "path": "../../demo/filters.ipynb" -} \ No newline at end of file diff --git a/docs/tutorials/tutorials.rst b/docs/tutorials/tutorials.rst deleted file mode 100644 index eba73e3..0000000 --- a/docs/tutorials/tutorials.rst +++ /dev/null @@ -1,5 +0,0 @@ -Tutorials -========== - -.. toctree:: - demo_filters.nblink \ No newline at end of file diff --git a/multyscale/__init__.py b/multyscale/__init__.py index 927d2b3..a31c0e3 100644 --- a/multyscale/__init__.py +++ b/multyscale/__init__.py @@ -1,5 +1,5 @@ __version__ = "0.2.0" -__all__ = ["filters", "filterbank", "models", "utils", "normalization"] +__all__ = ["filters", "filterbanks", "models", "utils", "normalization"] -from multyscale import filterbank, filters, models, normalization, utils +from multyscale import filterbanks, filters, models, normalization, utils diff --git a/multyscale/filterbank.py b/multyscale/filterbanks.py similarity index 88% rename from multyscale/filterbank.py rename to multyscale/filterbanks.py index 96f1e52..a32cd6e 100644 --- a/multyscale/filterbank.py +++ b/multyscale/filterbanks.py @@ -69,19 +69,29 @@ def __init__(self, sigmas: Sequence[Sequence[float]], x: np.ndarray, y: np.ndarr self.sigmas = sigmas self.x = x self.y = y + self.shape = ( + len(self.sigmas), + x.shape[0], + x.shape[1], + ) self.filters = np.empty((len(sigmas), x.shape[0], x.shape[1])) for i, sigma in enumerate(sigmas): dog = filters.dog(x, y, sigma) self.filters[i, :, :] = dog - def apply(self, image: np.ndarray) -> np.ndarray: + def apply(self, image: np.ndarray, padval=0.5) -> np.ndarray: """Apply filterbank to given image Parameters ---------- image : numpy.ndarray Image matrix to be filtered + padval : float, optional + whether to pad the input image, by default 0.5. + If truthy, image will be padded equally around all borders + with the constant value in here + to be the size of the filter Returns ------- @@ -92,7 +102,7 @@ def apply(self, image: np.ndarray) -> np.ndarray: """ filters_output = np.empty(self.filters.shape) for i in range(self.filters.shape[0]): - filters_output[i, ...] = filters.apply(image, self.filters[i, ...], pad=True) + filters_output[i, ...] = filters.apply(image, self.filters[i, ...], padval=padval) return filters_output @@ -163,6 +173,12 @@ def __init__( self.sigmas = sigmas self.x = x self.y = y + self.shape = ( + len(self.orientations), + len(self.sigmas), + x.shape[0], + x.shape[1], + ) self.filters = np.empty((len(orientations), len(sigmas), x.shape[0], x.shape[1])) @@ -171,13 +187,18 @@ def __init__( odog = filters.odog(x, y, sigma, (angle, angle)) self.filters[i, j, :, :] = odog - def apply(self, image: np.ndarray) -> np.ndarray: + def apply(self, image: np.ndarray, padval=0.5) -> np.ndarray: """Apply filterbank to given image Parameters ---------- image : numpy.ndarray Image matrix to be filtered + padval : float, optional + whether to pad the input image, by default 0.5. + If truthy, image will be padded equally around all borders + with the constant value in here + to be the size of the filter Returns ------- @@ -190,7 +211,9 @@ def apply(self, image: np.ndarray) -> np.ndarray: filters_output = np.empty(self.filters.shape) for i in range(self.filters.shape[0]): for j in range(self.filters.shape[1]): - filters_output[i, j, ...] = filters.apply(image, self.filters[i, j, ...], pad=True) + filters_output[i, j, ...] = filters.apply( + image, self.filters[i, j, ...], padval=padval + ) return filters_output diff --git a/multyscale/filters.py b/multyscale/filters.py index 74c2128..1014fd1 100644 --- a/multyscale/filters.py +++ b/multyscale/filters.py @@ -10,7 +10,7 @@ # TODO: (Abstract) base class Filter with apply-method,... -def apply(image: np.ndarray, filt: np.ndarray, pad: bool = False) -> np.ndarray: +def apply(image: np.ndarray, filt: np.ndarray, padval: float = 0.5) -> np.ndarray: """Apply filter to image, optionally pad input Parameters @@ -19,10 +19,10 @@ def apply(image: np.ndarray, filt: np.ndarray, pad: bool = False) -> np.ndarray: image to filter filt : numpy.ndarray filter to use - pad : bool, optional - whether to pad the input image, by default False. - If true, input will be padded equally around all borders - with the constant value 0.5 + padval : float, optional + whether to pad the input image, by default 0.5. + If truthy, input will be padded equally around all borders + with the constant value in here to be the size of the filter, Returns @@ -32,19 +32,19 @@ def apply(image: np.ndarray, filt: np.ndarray, pad: bool = False) -> np.ndarray: """ # TODO: make method - if pad: - pad_vertical, pad_horizontal = np.array(filt.shape) - padding = np.array( - [ - [pad_vertical / 2 - 1, pad_vertical / 2], - [pad_horizontal / 2 - 1, pad_horizontal / 2], - ], - dtype="int", - ) - pad_image = np.pad(image, padding, "constant", constant_values=0.5) - filtered_image = signal.fftconvolve(pad_image, filt, mode="valid") - else: - filtered_image = signal.fftconvolve(image, filt, mode="same") + # if padval: + pad_vertical, pad_horizontal = np.array(filt.shape) + padding = np.array( + [ + [pad_vertical / 2 - 1, pad_vertical / 2], + [pad_horizontal / 2 - 1, pad_horizontal / 2], + ], + dtype="int", + ) + pad_image = np.pad(image, padding, "constant", constant_values=padval) + filtered_image = signal.fftconvolve(pad_image, filt, mode="valid") + # else: + # filtered_image = signal.fftconvolve(image, filt, mode="same") return filtered_image diff --git a/multyscale/models.py b/multyscale/models.py index c5a2309..8ebe798 100644 --- a/multyscale/models.py +++ b/multyscale/models.py @@ -1,28 +1,94 @@ +"""End-to-End multiscale spatial filtering models + +This module implements several existing multiscale spatial filtering models. +The models here are primarily used to model brightness perception. + +Each model-type is implement as a (separate) class; +the parameters, attributes and methods available differ per class. +All models require a filter `shape` in pixels (X, Y), +and a `visextent` in degrees of visual angle (left, right, top, bottom) +All models have a method `.apply()`, +which takes in a (2D) image and returns the final model output (also 2D image). + +Currently implemented are: +- Difference-of-Gaussian (Blakeslee & McCourt, 1997), `DOG_BM1997` +- Oriented Difference-of-Gaussian (ODOG; Blakeslee & McCourt, 1999; Robinson, Hammon & de Sa, 2007) + `ODOG_RHS2007` +- LODOG (Robinson, Hammon & de Sa, 2007) `LODOG_RHS2007` +- FLODOG (Robinson, Hammon & de Sa, 2007) `FLODOG_RHS2007` + +""" + +from typing import Sequence + # Third party imports import numpy as np # Local application imports -from . import filterbank, filters, normalization - -# TODO: refactor filter-output datastructures +from . import filterbanks, normalization class DOG_BM1997: - def __init__(self, shape, visextent): + """Difference-of-Gaussian model, after Blakeslee & McCourt (1997) + + This model uses 7 unoriented (isotropic) Difference-of-Gaussian filters, + weighted according to an (approximate) contrast sensitivity function. + + Parameters + ---------- + shape : array-like[int] + pixel size (X, Y) of filters + visextent : array-like[float] + extent in degrees visual angle, (left, right, top, bottom) + + See also + -------- + multyscale.filterbanks.BM1997 + + """ + + def __init__(self, shape: Sequence[int], visextent: Sequence[float]): self.shape = shape self.visextent = visextent - self.bank = filterbank.BM1997(shape, visextent) + self.bank = filterbanks.BM1997(shape, visextent) self.center_sigmas = [sigma[0] for sigma in self.bank.sigmas] self.weights_slope = 0.1 - self.scale_weights = filterbank.scale_weights(self.center_sigmas, self.weights_slope) + self.scale_weights = filterbanks.scale_weights(self.center_sigmas, self.weights_slope) + + def weight_outputs(self, filters_output: np.ndarray) -> np.ndarray: + """Weight filter outputs according to spatial scale of filter + + Uses self.scale_weights for weighting + + Parameters + ---------- + filters_output : numpy.ndarray + output of whole filterbank, of shape (S, Y, X) + + Returns + ------- + numpy.ndarray + weighted output of whole filterbank, same shape is filters_output + """ + return filterbanks.weight_multiscale_outputs(filters_output, self.scale_weights) - def weight_outputs(self, filters_output): - return filterbank.weight_multiscale_outputs(filters_output, self.scale_weights) + def apply(self, image: np.ndarray) -> np.ndarray: + """Apply model to given image - def apply(self, image): - # TODO: docstring + Parameters + ---------- + image : numpy.ndarray + image, 2D in shape (Y, X) + eps : float, optional + precision offset, used to avoid floating point errors, by default 0.0 + + Returns + ------- + numpy.ndarray + matrix of brightness estimate, of same shape as image + """ # Sum over spatial scales filters_output = self.bank.apply(image) @@ -34,108 +100,306 @@ def apply(self, image): class ODOG_RHS2007: - # TODO: docstring + """Oriented Difference-of-Gaussian model, after Blakeslee & McCourt (1999) + + This model uses 7 Oriented Difference-of-Gaussian filters, + weighted according to an (approximate) contrast sensitivity function. + + After weighting, each filter output is normalized + by the global energy of a weighted combination of all other filter outputs. + + This implementation specifically corresponds to Robinson, Hammon and de Sa's (2007) + implementation, and exactly replicates the available MATLAB version. + + Parameters + ---------- + shape : array-like[int] + pixel size (X, Y) of filters + visextent : array-like[float] + extent in degrees visual angle, (left, right, top, bottom) - def __init__(self, shape, visextent): + See also + -------- + multyscale.filterbanks.RHS2007 + multyscale.normalizaton + + """ + + def __init__(self, shape: Sequence[int], visextent: Sequence[float]): self.shape = shape self.visextent = visextent - self.bank = filterbank.RHS2007(shape, visextent) + self.bank = filterbanks.RHS2007(shape, visextent) self.center_sigmas = [sigma[0][0] for sigma in self.bank.sigmas] self.weights_slope = 0.1 - self.scale_weights = filterbank.scale_weights(self.center_sigmas, self.weights_slope) + self.scale_weights = filterbanks.scale_weights(self.center_sigmas, self.weights_slope) self.scale_norm_weights = normalization.scale_norm_weights_equal(len(self.scale_weights)) - self.orientation_norm_weights = normalization.orientation_norm_weights(6) + self.orientation_norm_weights = normalization.orientation_norm_weights(self.bank.shape[0]) self.normalization_weights = normalization.create_normalization_weights( - 6, 7, self.scale_norm_weights, self.orientation_norm_weights + *self.bank.shape[:2], self.scale_norm_weights, self.orientation_norm_weights ) - def weight_outputs(self, filters_output): - return filterbank.weight_oriented_multiscale_outputs(filters_output, self.scale_weights) - - def normalizers(self, filters_output): - # Get normalizers - normalizers = normalization.normalizers(filters_output, self.normalization_weights) - return normalizers - - def normalizers_to_RMS(self, normalizers): - # Get RMS from each normalizer - spatial_avg_filters = normalization.spatial_avg_windows_globalmean(normalizers) - normalizers_RMS = normalizers.copy() - normalizers_RMS = np.square(normalizers_RMS) - normalizers_RMS = spatial_avg_filters * normalizers_RMS - normalizers_RMS = normalizers_RMS.sum(axis=(-1, -2)) - normalizers_RMS = np.sqrt(normalizers_RMS) - return normalizers_RMS - - def normalize_outputs(self, filters_output): - # TODO: docstring - normalizers = self.normalizers(filters_output) + def weight_outputs(self, filters_output: np.ndarray) -> np.ndarray: + """Weight filter outputs according to spatial scale of filter + + Uses self.scale_weights for weighting + + Parameters + ---------- + filters_output : numpy.ndarray + output of whole filterbank, of shape (O, S, Y, X) + + Returns + ------- + numpy.ndarray + weighted output of whole filterbank, same shape is filters_output + """ + return filterbanks.weight_oriented_multiscale_outputs(filters_output, self.scale_weights) + + def norm_coeffs(self, filters_output: np.ndarray) -> np.ndarray: + """Construct all normalizing coefficients for given filter outputs + + Uses model.normalization_weights + + Parameters + ---------- + filters_output : numpy.ndarray + output of whole filterbank, of shape (O, S, Y, X) + + Returns + ------- + numpy.ndarray + all (O', S', Y, X) normalizing coefficients: + a single 2D (Y, X) weighted combination of all filter outputs + per (O', S') filter-to-normalize + + See also + -------- + multyscale.normalization.norm_coeffs + """ + return normalization.norm_coeffs(filters_output, self.normalization_weights) + + def spatial_kernels(self) -> np.ndarray: + """Construct all spatial averaging kernels + + ODOG model uses a global image mean kernel + + Returns + ------- + numpy.ndarray + all (O, S, Y, X) spatial averaging kernels + + See also + -------- + multyscale.normalization.spatial_kernel_globalmean + """ + kernel = normalization.spatial_kernel_globalmean(self.bank.shape[2:]) + kernels = np.ndarray(self.bank.shape[:2] + kernel.shape) + for o, s in np.ndindex(kernels.shape[:2]): + kernels[o, s] = kernel + + return kernels + + def norm_energies(self, norm_coeffs: np.ndarray, eps=0.0) -> np.ndarray: + """Convert normalizing coefficients to energy (denominator for divisive normalization) + + Parameters + ---------- + norm_coeffs : numpy.ndarray + all (O, S, Y, X) normalizing coefficients (weighted combination of all filter outputs) + eps : float, optional + precision offset, used to avoid square-root of negative numbers, by default 0.0 + + Returns + ------- + numpy.ndarray + all (O, S, Y, X) normalizing energies (denominator for divisive normalization) + + See also + -------- + multyscale.models.ODOG_RHS2007.norm_coeffs + multyscale.normalization.norm_energy + """ + kernels = self.spatial_kernels() + + norm_energies = np.ndarray(norm_coeffs.shape) + for o_prime, s_prime in np.ndindex(norm_coeffs.shape[:2]): + norm_energies[o_prime, s_prime] = normalization.norm_energy( + norm_coeffs[o_prime, s_prime], kernels[o_prime, s_prime], eps=eps + ) + + return norm_energies + + def normalize_outputs(self, filters_output: np.ndarray, eps=0.0) -> np.ndarray: + """Apply divisive normalization to given filter outputs + + Parameters + ---------- + filters_output : numpy.ndarray + output of whole filterbank, of shape (O, S, Y, X) + eps : float, optional + precision offset, used to avoid square-root of negative numbers, by default 0.0 + + Returns + ------- + numpy.ndarray + all (O, S, Y, X) normalized filter outputs + """ + norm_coeffs = self.norm_coeffs(filters_output) + + norm_energies = self.norm_energies(norm_coeffs, eps=eps) + + normalized_outputs = normalization.divisive_normalization( + filters_output, norm_energies, eps=eps + ) - normalizer_RMS = self.normalizers_to_RMS(normalizers) + return normalized_outputs - normalized_outputs = np.ndarray(filters_output.shape) - for o, s in np.ndindex(filters_output.shape[:2]): - normalized_outputs[o, s] = filters_output[o, s] / normalizer_RMS[o, s] + def apply(self, image: np.ndarray, eps=0.0) -> np.ndarray: + """Apply model to given image - return normalized_outputs + Parameters + ---------- + image : numpy.ndarray + image, 2D in shape (Y, X) + eps : float, optional + precision offset, used to avoid floating point errors, by default 0.0 - def apply(self, image): - # TODO: docstring + Returns + ------- + numpy.ndarray + matrix of brightness estimate, of same shape as image + """ # Sum over spatial scales filters_output = self.bank.apply(image) weighted_outputs = self.weight_outputs(filters_output) # Normalize oriented multiscale outputs - normalized_outputs = self.normalize_outputs(weighted_outputs) + normalized_outputs = self.normalize_outputs(weighted_outputs, eps=eps) # Sum over orientations and scales output = normalized_outputs.sum((0, 1)) + return output class LODOG_RHS2007(ODOG_RHS2007): - # TODO: docstring + """LODOG model, after Robinson, Hammon, and de Sa (2007) + + This model uses 7 Oriented Difference-of-Gaussian filters, + weighted according to an (approximate) contrast sensitivity function. + + After weighting, each filter output is normalized + by the local energy of a weighted combination of all other filter outputs. - def __init__(self, shape, visextent): - self.window_sigma = 2 - self.window_sigmas = np.ones(shape=(6, 7, 2)) * self.window_sigma + This implementation specifically corresponds to Robinson, Hammon and de Sa's (2007) + implementation, and exactly replicates the available MATLAB version. + Parameters + ---------- + shape : array-like[int] + pixel size (X, Y) of filters + visextent : array-like[float] + extent in degrees visual angle, (left, right, top, bottom) + window_sigma : float + standard deviation (in degrees) of Gaussian spatial normalization kernel, by default 4.0 + + + See also + -------- + multyscale.filterbanks.RHS2007 + multyscale.normalization + multyscale.normalization.spatial_kernel_gaussian + + """ + + def __init__( + self, shape: Sequence[int], visextent: Sequence[float], window_sigma: float = 4.0 + ): super().__init__(shape, visextent) - def normalizers_to_RMS(self, normalizers): - # Expand sigmas - # Get RMS from each normalizer - spatial_avg_filters = normalization.spatial_avg_windows_gaussian( - self.bank.x, self.bank.y, self.window_sigmas - ) - normalizers_RMS = normalizers.copy() - normalizers_RMS = np.square(normalizers_RMS) - for o, s in np.ndindex(normalizers_RMS.shape[:2]): - normalizers_RMS[o, s] = filters.apply(spatial_avg_filters[o, s], normalizers_RMS[o, s]) - normalizers_RMS += 1e-6 - normalizers_RMS = np.sqrt(normalizers_RMS) - return normalizers_RMS + self.window_sigma = window_sigma + self.window_sigmas = np.ones(shape=(*self.bank.shape[:2], 2)) * self.window_sigma + def spatial_kernels(self) -> np.ndarray: + """Construct all spatial averaging kernels -class FLODOG_RHS2007(LODOG_RHS2007): - # TODO: docstring + (F)LODOG model uses a 2D Gaussian shape, + using the model.window_sigma(s) + + Returns + ------- + numpy.ndarray + spatial averaging kernel (Y, X) + + See also + -------- + multyscale.normalization.spatial_kernel_gaussian + """ + kernels = np.ndarray(self.bank.shape) + for o_prime, s_prime in np.ndindex(self.window_sigmas.shape[:2]): + kernels[o_prime, s_prime] = normalization.spatial_kernel_gaussian( + self.bank.x, + self.bank.y, + sigmas=self.window_sigmas[o_prime, s_prime], + ) + return kernels - def __init__(self, shape, visextent): + +class FLODOG_RHS2007(LODOG_RHS2007): + """LODOG model, after Robinson, Hammon, and de Sa (2007) + + This model uses 7 Oriented Difference-of-Gaussian filters, + weighted according to an (approximate) contrast sensitivity function. + + After weighting, each filter output is normalized + by the local energy of a weighted combination of all other filter outputs. + + This implementation specifically corresponds to Robinson, Hammon and de Sa's (2007) + implementation, and exactly replicates the available MATLAB version. + + Parameters + ---------- + shape : array-like[int] + pixel size (X, Y) of filters + visextent : array-like[float] + extent in degrees visual angle, (left, right, top, bottom) + sdmix : float + standard deviation of Gaussian weighting function, by default 0.5 + spatial_window_scalar : float + scaling factor of Gaussian spatial normalization kernel relative to filter size, + by default 4.0 + + + See also + -------- + multyscale.filterbanks.RHS2007 + multyscale.normalization + multyscale.normalization.spatial_kernel_gaussian + + """ + + def __init__( + self, + shape: Sequence[int], + visextent: Sequence[float], + sdmix: float = 0.5, + spatial_window_scalar: float = 4.0, + ): super().__init__(shape, visextent) - self.sdmix = 0.5 # stdev of Gaussian weights for scale mixing + self.sdmix = sdmix # stdev of Gaussian weights for scale mixing self.scale_norm_weights = normalization.scale_norm_weights_gaussian( len(self.scale_weights), self.sdmix ) self.normalization_weights = normalization.create_normalization_weights( - 6, 7, self.scale_norm_weights, self.orientation_norm_weights + *self.bank.shape[:2], self.scale_norm_weights, self.orientation_norm_weights ) - self.spatial_window_scalar = 2 - self.window_sigmas = np.broadcast_to( - np.array(self.center_sigmas)[None, ..., None], (6, 7, 2) + self.spatial_window_scalar = spatial_window_scalar + self.window_sigmas = self.spatial_window_scalar * np.broadcast_to( + np.array(self.center_sigmas)[None, ..., None], (*self.bank.shape[:2], 2) ) diff --git a/multyscale/normalization.py b/multyscale/normalization.py index 46650c5..5446edc 100644 --- a/multyscale/normalization.py +++ b/multyscale/normalization.py @@ -1,92 +1,254 @@ +from collections.abc import Sequence + # Third party imports import numpy as np # Local application imports from . import filters -# DOG_BM1997 normalization +# Generalized (F)(L)ODOG normalization + + +def norm_coeffs(multioutput: np.ndarray, normalization_weights: np.ndarray) -> np.ndarray: + """Construct all normalizing coefficients: weighted combination of all filter outputs, for each filter + + The multi(dimensional)output consists of all (O, S) filter outputs, + where each is of dimension (Y, X). + Thus, the multioutput has dimension (O, S, Y, X). + + The output will be one normalizing coefficient, a matrix of (Y, X) pixels, + for each filter-to-normalize f'(o', s'). + Thus, the norm_coeffs output also has dimension (O', S', Y, X). + + To create these, for each filter-to-normalize f'(o', s'), + there should be (O, S) weights provide: + one for how each filter is weighted in this normalizing coefficient. + Thus, the normalization weights are of dimension (O', S', O, S): + for each (o',s') filter-to-normalize, there are (O, S) weights. + + Parameters + ---------- + multioutput : numpy.ndarray + all (O, S, Y, X) filter outputs + normalization_weights : numpy.ndarray + full tensor (O', S', O, S) of normalization weights + + Returns + ------- + numpy.ndarray + all (O', S', Y, X) normalizing coefficients: + a single 2D (Y, X) weighted combination of all filter outputs + per (O', S') filter-to-normalize + """ + + # Create all normalizing coefficients from weighted combination of filter outputs + # This is done as a single tensor dot-product operation. + # Multiply the (O', S', O, S) weights, by the (O, S, Y, X) filter outputs, + # then sum over the (O, S) dimensions. + # In Einstein summation notation (and q := o', r := s'), that is: + # + # norms = np.einsum('qros, osyx -> qryx', normalization_weights, multioutput) + # + # which in tensordot syntax is: + norms = np.tensordot(normalization_weights, multioutput, axes=([2, 3], [0, 1])) + + return norms + + +def norm_energy(norm_coeff: np.ndarray, spatial_kernel: np.ndarray, eps=0.0) -> np.ndarray: + """Convert normalizing coefficient to energy (denominator for divisive normalization) + + Parameters + ---------- + norm_coeff : numpy.ndarray + single 2D normalizing coefficient; weighted combination of all filter outputs + spatial_kernel : numpy.ndarray + single kernel to spatially average (2D; over x,y) the normalizing coefficient + eps : float, optional + precision offset, used to avoid square-root of negative numbers, by default 0.0 + + Returns + ------- + numpy.ndarray + single normalizing energy: denominator for divisive normalization + """ + norm = norm_coeff**2 + spatial_average = filters.apply(norm, spatial_kernel, padval=0) + energy = np.sqrt(spatial_average + eps) + return energy + + +def divisive_normalization( + filter_output: np.ndarray, norm_coeff: np.ndarray, eps=0.0 +) -> np.ndarray: + """Apply divisive normalization to a single filter output + + Parameters + ---------- + filter_output : np.ndarray + output from either a single filter, 2D of shape (Y, X), + or a whole filterbank, N-Dimensional of shape (M, ..., N, Y, X) + norm_coeff : np.ndarray + normalizing coefficient(s): denominator for divisive normalization. + Must be of same shape as filter_output + eps : float, optional + precision offset, used to avoid DivideByZero errors, by default 0.0 + + Returns + ------- + np.ndarray + normalized filter output, 2D of shape (y, x) + """ + return filter_output / (norm_coeff + eps) + + +def spatial_kernel_globalmean(shape: Sequence[int]) -> np.ndarray: + """Create spatial averaging kernel, calculating the global image mean + + Parameters + ---------- + shape : (int, int) + shape in pixels (height, width) of image to average + + Returns + ------- + numpy.ndarray + spatial averaging kernel + """ + kernel = np.ones([dim * 2 for dim in shape]) + kernel /= kernel.sum() + kernel *= 4 + return kernel + + +def spatial_kernel_gaussian(x: np.ndarray, y: np.ndarray, sigmas: Sequence[float]) -> np.ndarray: + """Create spatial averaging kernel, in a 2D Gaussian shape + + Parameters + ---------- + x : numpy.ndarray + x coordinates of each pixel + y : numpy.ndarray + y coordinates of each pixel + sigmas : (float, float) + standard deviation along each axis (2-vector) or both axes (scalar) + + Returns + ------- + numpy.ndarray + spatial averaging kernel + """ + kernel = filters.gaussian2d(x, y, sigmas) + kernel /= kernel.sum() + return kernel -# ODOG_BM1999 normalization -# Each filter is normalized by other filters of same orientation, -# but not by filters at other orientations -# Different scales are weighted equally -# so for filter (o,s), all filters (O!=o, :) get weight 0, -# and all filters (O=o, S) get weight 1/len(S) def create_normalization_weights( - n_orientations, n_scales, scale_norm_weights, orientation_norm_weights -): - # Each filter can, in theory, normalized by every (other) filter - # But the weight for how much every filter normalizes, can vary - # For each filter, create a tensor with weights for each other filter - normalization_weights = np.ndarray(shape=(n_orientations, n_scales, n_orientations, n_scales)) - - for o, s in np.ndindex(n_orientations, n_scales): - normalization_weights[o, s, ...] = np.outer( - orientation_norm_weights[o], scale_norm_weights[s] + n_orientations: int, + n_scales: int, + scale_norm_weights: np.ndarray, + orientation_norm_weights: np.ndarray, +) -> np.ndarray: + """Combine normalization weights across dimensions into single tensor of weights + + Each filter can, in theory, normalized by every (other) filter, + but the weight for how much every filter normalizes, can vary. + For each filter, create a tensor with weights for each other filter. + + Parameters + ---------- + n_orientations : int + number of orientations to weight + n_scales : int + number of spatial scales/frequencies to weight + scale_norm_weights : numpy.ndarray + matrix of normalization weights across scales, of shape (n_scales, n_scales) + orientation_norm_weights : numpy.ndarray + matrix of normalization weights across orientations, of shape (n_orientations, n_orientations) + + Returns + ------- + numpy.ndarray + tensor of combined normalization weights, + of shape (n_orientations, n_scales, n_orientations, n_scales) -- + for each filter $(o', s')$, a matrix of $(O, S)$ normalization weights + """ + normalization_weights: np.ndarray = np.ndarray( + shape=(n_orientations, n_scales, n_orientations, n_scales) + ) + for o_prime, s_prime in np.ndindex(n_orientations, n_scales): + normalization_weights[o_prime, s_prime, ...] = np.outer( + orientation_norm_weights[o_prime], scale_norm_weights[s_prime] ) return normalization_weights -def scale_norm_weights_equal(n_scales): - scale_norm_weights = np.ones((n_scales, n_scales)) - # scale_norm_weights = scale_norm_weights / scale_norm_weights.sum((0)) - return scale_norm_weights +def scale_norm_weights_equal(n_scales: int) -> np.ndarray: + """Equal weighting for all scales + All filters $(o=o', s)$ get some weight; + in (L)ODOG, that weight is $1/len(S)$, -def scale_norm_weights_gaussian(n_scales, sdmix): - scale_norm_weights = np.ndarray((n_scales, n_scales)) - for s in range(n_scales): - rel_i = np.asarray(range(n_scales)) - s + Parameters + ---------- + n_scales : int + number of scales to create weights for - # Gaussian weights, based on relative index - scale_norm_weights[s, ...] = np.exp(-(rel_i**2) / (2 * sdmix**2)) / ( - sdmix * np.sqrt(2 * np.pi) - ) - # scale_norm_weights = scale_norm_weights / scale_norm_weights.sum((0)) - return scale_norm_weights + Returns + ------- + numpy.ndarray + matrix of normalization weights, of shape (n_scales, n_scales) + """ + return np.ones((n_scales, n_scales)) -def orientation_norm_weights(n_orientations): - orientation_norm_weights = np.eye(n_orientations) - orientation_norm_weights = orientation_norm_weights / orientation_norm_weights.sum(0) - return orientation_norm_weights +def scale_norm_weights_gaussian(n_scales: int, sdmix: float) -> np.ndarray: + """Gaussian weighting for all scales + All filters $(o=o', s)$ get some weight; + in FLODOG, that weight is Gaussian dependent on $s-s'$ -def normalizers(filters_output, normalization_weights): - # Create normalizing images from weighted combination of filter outputs - normalizers = np.ndarray(filters_output.shape) + Parameters + ---------- + n_scales : int + number of scales to create weights for + sdmix : float + standard deviation (SD) of Gaussian - for o, s in np.ndindex(filters_output.shape[:2]): - weights = normalization_weights[o, s] - - # Tensor dot: multiply filters_output by weights, then sum over axes [0,1] - normalizer = np.tensordot(filters_output, weights, axes=([0, 1], [0, 1])) - - # Normalize normalizer... - # area = weights.sum() - # normalizer = normalizer / area + Returns + ------- + numpy.ndarray + matrix of normalization weights, of shape (n_scales, n_scales) + """ + scale_norm_weights: np.ndarray = np.ndarray(shape=(n_scales, n_scales)) + for s in range(n_scales): + rel_i = np.asarray(range(n_scales)) - s - # Accumulate - normalizers[o, s, ...] = normalizer + # Gaussian weights, based on relative index + weights = np.exp(-(rel_i**2) / (2 * sdmix**2)) / (sdmix * np.sqrt(2 * np.pi)) + weights /= weights.sum() + scale_norm_weights[s, :] = weights - return normalizers + return scale_norm_weights -def spatial_avg_windows_globalmean(normalizers): - filts = np.ndarray(normalizers.shape) - for o, s in np.ndindex(filts.shape[:2]): - filts[o, s] = filters.global_avg(normalizers.shape[2:]) - return filts +def orientation_norm_weights(n_orientations: int) -> np.ndarray: + """Self-normalization weight for each orientation + Each filter is normalized by other filters of same orientation, + but not by filters at other orientations; + so for filter $(o',s')$, all filters $(o=o', ...)$ get weight $1$, else $0$. -def spatial_avg_windows_gaussian(x, y, sigmas): - filts = np.ndarray(sigmas.shape[:2] + x.shape) - for o, s in np.ndindex(filts.shape[:2]): - # Create Gaussian window - window = filters.gaussian2d(x, y, sigmas[o, s]) + Parameters + ---------- + n_orientations : int + number of orientations to create weights for - # Normalize window to unit-sum (== spatial averaging filter) - window = window / window.sum() - filts[o, s, ...] = window - return filts + Returns + ------- + numpy.ndarray + matrix of normalization weights, of shape (n_orientations, n_orientations) + """ + orientation_norm_weights = np.eye(n_orientations) + orientation_norm_weights /= orientation_norm_weights.sum(0) + return orientation_norm_weights diff --git a/pyproject.toml b/pyproject.toml index 8638142..6f3f8dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,34 +25,11 @@ dependencies = ["numpy", "scipy", "matplotlib", "Pillow"] [project.optional-dependencies] dev = ["pytest", "black", "pyupgrade", "flake8"] docs = [ - "Sphinx", - "sphinx-autodoc-typehints", - "sphinx-rtd-theme", - "sphinx-tabs", - "sphinxcontrib-applehelp", - "sphinxcontrib-bibtex", - "sphinxcontrib-devhelp", - "sphinxcontrib-htmlhelp", - "sphinxcontrib-jsmath", - "sphinxcontrib-napoleon", - "sphinxcontrib-qthelp", - "sphinxcontrib-serializinghtml", - "ipykernel", - "ipython", - "ipython-genutils", - "ipywidgets", - "jupyter-client", - "jupyter-core", - "jupyterlab-pygments", - "jupyterlab-widgets", - "nbclient", - "nbconvert", - "nbformat", - "nbsphinx", - "nbsphinx-link", - "notebook", - "rstcheck", - "widgetsnbextension", + "jupyter-book", + "jupytext>=1.13.3", + "jupyterlab_myst", + "myst_nb", + "sphinx-hoverxref", ] [project.urls] diff --git a/test/MATLAB_comparison.py b/test/MATLAB_comparison.py new file mode 100644 index 0000000..2f1d3ab --- /dev/null +++ b/test/MATLAB_comparison.py @@ -0,0 +1,67 @@ +import os + +import numpy as np +import pytest +from scipy import io + +filepath_MATLAB_output = os.path.abspath(__file__ + "../../odog_MATLAB.mat") + + +@pytest.fixture() +def MATLAB_visextent(): + # Visual extent, same convention as pyplot (Left, Right, Bottom, Top): + visextent = np.array([-0.5, 0.5, -0.5, 0.5]) * (1023 / 32) + # NOTE: RHS implementation doesn't actually use (-16,16,-16,16) + return visextent + + +@pytest.fixture() +def MATLAB_shape(): + # Shape (resolution) of image, filters (Y, X) + return (1024, 1024) + + +@pytest.fixture() +def MATLAB_bank(): + # Load RHS bank from MATLAB implementation + return np.array(io.loadmat(filepath_MATLAB_output)["filters"].tolist()) + + +@pytest.fixture() +def stimulus(): + return io.loadmat(filepath_MATLAB_output)["illusion"] + + +@pytest.fixture() +def MATLAB_filteroutput(): + # Load RHS bank from MATLAB implementation + return np.array(io.loadmat(filepath_MATLAB_output)["filter_response"].tolist()) + + +@pytest.fixture() +def output_ODOG_MATLAB(): + return io.loadmat(filepath_MATLAB_output)["odog_output"] + + +@pytest.fixture() +def output_LODOG_MATLAB(): + return io.loadmat(filepath_MATLAB_output)["lodog_output"] + + +@pytest.fixture() +def output_FLODOG_MATLAB(): + return io.loadmat(filepath_MATLAB_output)["flodog_output"] + + +@pytest.fixture() +def MATLAB_LODOG_params(): + """Normalization parameters used to produce the MATLAB output for LODOG""" + LODOG_params = {"sig1": 128, "sr": 1} + return LODOG_params + + +@pytest.fixture() +def MATLAB_FLODOG_params(): + """Normalization parameters used to produce the MATLAB output for FLODOG""" + FLODOG_params = {"sigx": 4, "sr": 1, "sdmix": 0.5} + return FLODOG_params diff --git a/test/RHS_filters.py b/test/RHS_filters.py deleted file mode 100644 index b99c4d2..0000000 --- a/test/RHS_filters.py +++ /dev/null @@ -1,148 +0,0 @@ -import numpy as np -from scipy import fft - -# %% Model params -orientations = np.linspace(0, 150, 6) # 6 steps of 30 degrees -freqs = np.arange(0, 7) # 7 different mechanisms - -# conversion factors -DEG_PER_PIXEL = 0.03125 -SPACE_CONST_TO_WIDTH = 2 * np.sqrt(np.log(2)) -SPACE_CONST_TO_STD = 1 / np.sqrt(2) -STD_TO_SPACE_CONST = 1 / SPACE_CONST_TO_STD - -space_const = 2**freqs * 1.5 # in pixels - -# matches Table 1 in BM(1999) -space_const_deg = space_const * DEG_PER_PIXEL # in deg. - -# compute the standard deviations of the different Gaussian in pixels -# space_const = 2.^freqs * 1.5; % space constant of Gaussians -stdev_pixels = space_const * SPACE_CONST_TO_STD # in pixels -std = space_const_deg * SPACE_CONST_TO_STD # in degrees - -# (almost matches) points along x-axis of Fig. 10 BM(1997) -cpd = 1 / (2 * space_const_deg * SPACE_CONST_TO_WIDTH) - -# (almost matches) points along y-axis of Fig. 10 BM(1997) -w_val = cpd**0.1 -w_val = w_val / w_val[int(np.ceil(w_val.size / 2)) - 1] -w_val = np.round(w_val, 5) - -# CSF as proposed by Manos and Sakrison -# J. L. Mannos, D. J. Sakrison, -# "The Effects of a Visual Fidelity Criterion on the Encoding of Images", -# IEEE Transactions on Information Theory, pp. 525-535, Vol. 20, No 4, (1974) -CSF = 2.6 * (0.0192 + 0.114 * cpd) * np.exp(-0.114 * cpd) ** 1.1 - -# model size -model_y = 1024 -model_x = 1024 - - -# %% -def gauss(x, std): - return np.exp(-(x**2) / (2 * std**2)) / (std * np.sqrt(2 * np.pi)) - - -def d2gauss(n1, std1, n2, std2, theta): - # rotation transformation - theta = np.deg2rad(90 - theta) - r = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]) - - # create X and Y grids - Xs = np.linspace(-((n1 - 1) / 2), (n1 - 1) / 2, n1) - Xs, Ys = np.meshgrid(Xs, Xs) - - # reshape into vectors - Xs = Xs.T.reshape(-1) - Ys = Ys.T.reshape(-1) - coor = r @ np.vstack((Xs, Ys)) - - # compute 1-D gaussians - gausX = gauss(coor[0, :], std1) - gausY = gauss(coor[1, :], std2) - - # element-wise multiplication creates 2-D gaussians - h = np.reshape(gausX * gausY, (n2, n1)) - h = h / h.sum() - - return h - - -def dog(rows, columns, std1, std2, sr, theta): - return d2gauss(rows, std1, columns, std2, theta) - d2gauss( - rows, std1, columns, std2 * sr, theta - ) - - -def odog(model_x, model_y, stdev_pixels, orientation): - return dog(model_y, model_x, stdev_pixels, stdev_pixels, 2, orientation) - - -# %% Filterbank -def filterbank(): - filters = np.empty((orientations.size, stdev_pixels.size, model_x, model_y)) - for i, orient in enumerate(orientations): - for j, stdev in enumerate(stdev_pixels): - filters[i, j, ...] = odog(model_x, model_y, stdev, orient) - return filters - - -# %% Convolution -def ourconv(image, filt): - # pad - padded_size = np.array(image.shape) + np.array(filt.shape) - pad_img = pad_RHS(image, padded_size, padval=0.5) - pad_filt = pad_RHS(filt, padded_size, padval=0) - - # Paul's slightly corrected version - temp = np.real(fft.ifft2(fft.fft2(pad_img) * fft.fft2(pad_filt))) - - # extract the appropriate portion of the filtered image - filtered = unpad_RHS(temp, image.shape) - - return filtered - - -def pad_RHS(image, shape, padval): - # pad the images - pad_img = np.ones(shape) * padval - pad_img[0 : image.shape[0], 0 : image.shape[1]] = image - return pad_img - - -def unpad_RHS(pad_image, shape): - image = pad_image[ - int(shape[0] / 2) : -int(shape[0] / 2), - int(shape[1] / 2) : -int(shape[1] / 2), - ] - return image - - -# %% Normalizations -def odog_normalize(filter_responses): - # to hold model output - modelOut = np.zeros(filter_responses.shape[-2:]) - - # loop over the orientations - for o in range(filter_responses.shape[0]): - this_norm = np.zeros(filter_responses.shape[-2:]) - # loop over spatial frequencies - for f in range(filter_responses.shape[1]): - # get the filtered response - filt_img = filter_responses[o, f] - - # create the proper weight - temp = filt_img * w_val[f] - - this_norm = temp + this_norm - # do normalization - this_norm = this_norm / np.sqrt(np.mean(this_norm * this_norm)) - - # add in normalized image - modelOut = modelOut + this_norm - return modelOut - - -# %% diff --git a/test/RHS_implementation.py b/test/RHS_implementation.py new file mode 100644 index 0000000..38281f5 --- /dev/null +++ b/test/RHS_implementation.py @@ -0,0 +1,307 @@ +import numpy as np +from scipy import fft + +# %% Model params +orientations = np.linspace(0, 150, 6) # 6 steps of 30 degrees +freqs = np.arange(0, 7) # 7 different mechanisms + +# conversion factors +DEG_PER_PIXEL = 0.03125 +SPACE_CONST_TO_WIDTH = 2 * np.sqrt(np.log(2)) +SPACE_CONST_TO_STD = 1 / np.sqrt(2) +STD_TO_SPACE_CONST = 1 / SPACE_CONST_TO_STD + +space_const = 2**freqs * 1.5 # in pixels + +# matches Table 1 in BM(1999) +space_const_deg = space_const * DEG_PER_PIXEL # in deg. + +# compute the standard deviations of the different Gaussian in pixels +# space_const = 2.^freqs * 1.5; % space constant of Gaussians +stdev_pixels = space_const * SPACE_CONST_TO_STD # in pixels +std = space_const_deg * SPACE_CONST_TO_STD # in degrees + +# (almost matches) points along x-axis of Fig. 10 BM(1997) +cpd = 1 / (2 * space_const_deg * SPACE_CONST_TO_WIDTH) + +# (almost matches) points along y-axis of Fig. 10 BM(1997) +w_val = cpd**0.1 +w_val = w_val / w_val[int(np.ceil(w_val.size / 2)) - 1] +w_val = np.round(w_val, 5) + +# CSF as proposed by Manos and Sakrison +# J. L. Mannos, D. J. Sakrison, +# "The Effects of a Visual Fidelity Criterion on the Encoding of Images", +# IEEE Transactions on Information Theory, pp. 525-535, Vol. 20, No 4, (1974) +CSF = 2.6 * (0.0192 + 0.114 * cpd) * np.exp(-0.114 * cpd) ** 1.1 + +# model size +model_y = 1024 +model_x = 1024 + + +# %% +def gauss(x, std): + return np.exp(-(x**2) / (2 * std**2)) / (std * np.sqrt(2 * np.pi)) + + +def d2gauss(n1, std1, n2, std2, theta): + # rotation transformation + theta = np.deg2rad(90 - theta) + r = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]) + + # create X and Y grids + Xs = np.linspace(-((n1 - 1) / 2), (n1 - 1) / 2, n1) + Xs, Ys = np.meshgrid(Xs, Xs) + + # reshape into vectors + Xs = Xs.T.reshape(-1) + Ys = Ys.T.reshape(-1) + coor = r @ np.vstack((Xs, Ys)) + + # compute 1-D gaussians + gausX = gauss(coor[0, :], std1) + gausY = gauss(coor[1, :], std2) + + # element-wise multiplication creates 2-D gaussians + h = np.reshape(gausX * gausY, (n2, n1)) + h = h / h.sum() + + return h + + +def dog(rows, columns, std1, std2, sr, theta): + return d2gauss(rows, std1, columns, std2, theta) - d2gauss( + rows, std1, columns, std2 * sr, theta + ) + + +def odog(model_x, model_y, stdev_pixels, orientation): + return dog(model_y, model_x, stdev_pixels, stdev_pixels, 2, orientation) + + +# %% Filterbank +def filterbank(): + filters = np.empty((orientations.size, stdev_pixels.size, model_x, model_y)) + for i, orient in enumerate(orientations): + for j, stdev in enumerate(stdev_pixels): + filters[i, j, ...] = odog(model_x, model_y, stdev, orient) + return filters + + +# %% Convolution +def ourconv(image, filt, pad=0.5): + # pad + padded_size = np.array(image.shape) + np.array(filt.shape) + pad_img = pad_RHS(image, padded_size, padval=pad) + pad_filt = pad_RHS(filt, padded_size, padval=0) + + # Paul's slightly corrected version + temp = np.real(fft.ifft2(fft.fft2(pad_img) * fft.fft2(pad_filt))) + + # extract the appropriate portion of the filtered image + filtered = unpad_RHS(temp, image.shape) + + return filtered + + +def pad_RHS(image, shape, padval): + # pad the images + pad_img = np.ones(shape) * padval + pad_img[0 : image.shape[0], 0 : image.shape[1]] = image + return pad_img + + +def unpad_RHS(pad_image, shape): + image = pad_image[ + int(shape[0] / 2) : -int(shape[0] / 2), + int(shape[1] / 2) : -int(shape[1] / 2), + ] + return image + + +def weight(filter_responses): + weighted_responses = np.ndarray(filter_responses.shape) + + # loop over the orientations + for o in range(filter_responses.shape[0]): + # loop over spatial frequencies + for f in range(filter_responses.shape[1]): + weighted_responses[o, f] = filter_responses[o, f] * w_val[f] + return weighted_responses + + +# %% Normalizations +def odog_normalize(filter_responses): + # to hold model output + modelOut = np.zeros(filter_responses.shape[-2:]) + + # loop over the orientations + for o in range(filter_responses.shape[0]): + this_norm = np.zeros(filter_responses.shape[-2:]) + # loop over spatial frequencies + for f in range(filter_responses.shape[1]): + # get the filtered response + filt_img = filter_responses[o, f] + + # create the proper weight + temp = filt_img * w_val[f] + + this_norm = temp + this_norm + # do normalization + this_norm = this_norm / np.sqrt(np.mean(this_norm * this_norm)) + + # add in normalized image + modelOut = modelOut + this_norm + return modelOut + + +# %% LODOG +def LODOG_RMS(this_norm, sig1, sr): + # square + img_sqr = this_norm**2 + + # create Gaussian mask + mask = LODOG_mask(sig1, sr) + + # filter the image (using unit-sum mask --> mean) + filter_out = ourconv(img_sqr, mask, pad=0) + + # make sure there are no negative numbers due to fft inaccuracies. + filter_out = filter_out + 1e-6 + + # take the square root, last part of doing RMS + filter_out = np.sqrt(filter_out) + + filter_out += 1e-6 + return filter_out + + +def LODOG_mask(sig1, sr=1, o=0): + # sig1= size of gaussian window in the direction of the filter + # sig2= size of gaussian window perpendicular to filter + sig2 = sig1 * sr + + # directed along main axis of filter + rot = orientations[o] * np.pi / 180 + + # create a unit volume gaussian for filtering + mask = d2gauss(model_x, sig1, model_y, sig2, rot) + mask = mask / mask.sum() + return mask + + +def LODOG_normalize(filter_responses, sig1, sr=1): + # normalizers + norms = LODOG_normalizers(filter_responses) + + # lRMS + RMSs = LODOG_RMSs(norms, sig1, sr) + + # loop over the orientations + normed_resps = np.ndarray(filter_responses.shape) + for o in range(filter_responses.shape[0]): + # loop over spatial frequencies + for f in range(filter_responses.shape[1]): + filter_out = RMSs[o, f] + normed_resps[o, f] = filter_responses[o, f] / filter_out + return normed_resps + + +def LODOG_normalizers(filter_responses): + norms = np.zeros(filter_responses.shape) + + # loop over the orientations + for o in range(filter_responses.shape[0]): + this_norm = np.zeros(filter_responses.shape[-2:]) + + # loop over spatial frequencies to accumulate + for f in range(filter_responses.shape[1]): + this_norm += filter_responses[o, f] # * w_val[f] + + for f in range(filter_responses.shape[1]): + norms[o, f] = this_norm + return norms + + +def LODOG_RMSs(norms, sig1, sr=1): + RMSs = np.ndarray(norms.shape) + # loop over the orientations + for o in range(norms.shape[0]): + # loop over spatial frequencies to accumulate + for f in range(norms.shape[1]): + RMSs[o, f] = LODOG_RMS(norms[o, f], sig1, sr) + return RMSs + + +# %% FLODOG +def FLODOG_normalize(filter_responses, sigx, sdmix, sr=1): + # Normalizers are combination of filters, weighted by scale + norms = FLODOG_normalizers(filter_responses, sdmix) + + # local RMS of each normalizer through spatial filtering + RMSs = FLODOG_RMSs(norms, sigx, sr) + + # Divisively normalize + # loop over the orientations + normed_resps = np.ndarray(filter_responses.shape) + for o in range(filter_responses.shape[0]): + # loop over spatial frequencies + for f in range(filter_responses.shape[1]): + filter_out = RMSs[o, f] + normed_resps[o, f] = filter_responses[o, f] / filter_out + + return normed_resps + + +def FLODOG_normweights(sdmix): + weights = np.ndarray((len(stdev_pixels), len(stdev_pixels))) + for f in range(len(stdev_pixels)): + for wf in range(len(stdev_pixels)): + weights[f, wf] = gauss(f - wf, sdmix) + weights[f, :] /= weights[f, :].sum() + return weights + + +def FLODOG_normalizers(filter_responses, sdmix): + """Build weighted normalizers""" + + norms = np.zeros(filter_responses.shape) + weights = FLODOG_normweights(sdmix) + + # loop over the orientations + for o in range(filter_responses.shape[0]): + # loop over spatial frequencies to accumulate + for f in range(filter_responses.shape[1]): + normalizer = 0 + for wf in range(len(stdev_pixels)): + normalizer += filter_responses[o, wf] * weights[f, wf] + + norms[o, f, ...] = normalizer + + return norms + + +def FLODOG_masks(sigx, sr=1): + masks = np.ndarray((len(orientations), len(stdev_pixels), model_y, model_x)) + + for o in range(len(orientations)): + for f in range(len(stdev_pixels)): + sig1 = sigx * stdev_pixels[f] + + masks[o, f] = LODOG_mask(sig1, sr) + + return masks + + +def FLODOG_RMSs(norms, sigx, sr=1): + RMSs = np.ndarray(norms.shape) + # loop over the orientations + for o in range(norms.shape[0]): + # loop over spatial frequencies to accumulate + for f in range(norms.shape[1]): + # localization extent along direction of filter - function of frequency + sig1 = sigx * stdev_pixels[f] + + RMSs[o, f] = LODOG_RMS(norms[o, f], sig1, sr) + return RMSs diff --git a/test/conftest.py b/test/conftest.py index c23ed1e..f6f15e9 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,36 +1,9 @@ -import os - -import numpy as np import pytest -import RHS_filters -from scipy import io - -filepath_matlab_output = os.path.abspath(__file__ + "../../odog_matlab.mat") +import RHS_implementation +from MATLAB_comparison import * @pytest.fixture() def rhs_bank(): # Create RHS filterbank from Python transplation - return RHS_filters.filterbank() - - -@pytest.fixture() -def matlab_bank(): - # Load RHS bank from MATLAB implementation - return np.array(io.loadmat(filepath_matlab_output)["filters"].tolist()) - - -@pytest.fixture() -def stimulus(): - return io.loadmat(filepath_matlab_output)["illusion"] - - -@pytest.fixture() -def matlab_filteroutput(): - # Load RHS bank from MATLAB implementation - return np.array(io.loadmat(filepath_matlab_output)["filter_response"].tolist()) - - -@pytest.fixture() -def output_odog_matlab(): - return io.loadmat(filepath_matlab_output)["odog_output"] + return RHS_implementation.filterbank() diff --git a/test/test_FLODOG.py b/test/test_FLODOG.py new file mode 100644 index 0000000..d31c8f1 --- /dev/null +++ b/test/test_FLODOG.py @@ -0,0 +1,75 @@ +# %% Imports +import numpy as np +import pytest +import RHS_implementation + +import multyscale + +# %% Parameters of image +# visual extent, same convention as pyplot: +visextent = np.array([-0.5, 0.5, -0.5, 0.5]) * (1023 / 32) +# NOTE: RHS implementation doesn't actually use (-16,16,-16,16) + + +# %% Model +@pytest.fixture +def model(stimulus, MATLAB_FLODOG_params): + return multyscale.models.FLODOG_RHS2007( + stimulus.shape, + visextent, + spatial_window_scalar=MATLAB_FLODOG_params["sigx"], + sdmix=MATLAB_FLODOG_params["sdmix"], + ) + + +# %% Tests +def test_filters(MATLAB_filteroutput, model, stimulus): + filter_output = model.bank.apply(stimulus) + + assert np.allclose(MATLAB_filteroutput, filter_output) + + +def test_weights(model): + assert np.allclose(model.scale_weights, RHS_implementation.w_val) + + +def test_scale_norm_weights(model, MATLAB_FLODOG_params): + RHS_weights = RHS_implementation.FLODOG_normweights(sdmix=MATLAB_FLODOG_params["sdmix"]) + assert np.allclose(RHS_weights.shape, model.scale_norm_weights.shape) + assert np.allclose(RHS_weights, model.scale_norm_weights) + + +def test_norm_coeffs(model, MATLAB_filteroutput, MATLAB_FLODOG_params): + weighted_outputs = model.weight_outputs(MATLAB_filteroutput) + norm_coeffs = model.norm_coeffs(weighted_outputs) + RHS_norms = RHS_implementation.FLODOG_normalizers( + weighted_outputs, sdmix=MATLAB_FLODOG_params["sdmix"] + ) + assert np.allclose(norm_coeffs.shape, RHS_norms.shape) + assert np.allclose(norm_coeffs, RHS_norms) + + +def test_spatial_masks(model, MATLAB_FLODOG_params): + # Is the spatial (Gaussian) averaging window the same? + RHS_kernels = RHS_implementation.FLODOG_masks(sigx=MATLAB_FLODOG_params["sigx"]) + spatial_kernels = model.spatial_kernels() + for o, s in np.ndindex(spatial_kernels.shape[:2]): + assert np.allclose(spatial_kernels[o, s], RHS_kernels[o, s]) + + +def test_normalized_outputs(model, MATLAB_filteroutput, MATLAB_FLODOG_params): + weighted_outputs = RHS_implementation.weight(MATLAB_filteroutput) + RHS_normalized_outputs = RHS_implementation.FLODOG_normalize( + weighted_outputs, + sigx=MATLAB_FLODOG_params["sigx"], + sdmix=MATLAB_FLODOG_params["sdmix"], + ) + + normed_outputs = model.normalize_outputs(weighted_outputs, eps=1e-6) + + assert np.allclose(normed_outputs, RHS_normalized_outputs) + + +def test_model_output(output_FLODOG_MATLAB, model, stimulus): + output = model.apply(stimulus, eps=1e-6) + assert np.allclose(output, output_FLODOG_MATLAB) diff --git a/test/test_LODOG.py b/test/test_LODOG.py new file mode 100644 index 0000000..6a450d2 --- /dev/null +++ b/test/test_LODOG.py @@ -0,0 +1,64 @@ +# %% Imports +import numpy as np +import pytest +import RHS_implementation + +import multyscale + +# %% Parameters of image +# visual extent, same convention as pyplot: +visextent = np.array([-0.5, 0.5, -0.5, 0.5]) * (1023 / 32) +# NOTE: RHS implementation doesn't actually use (-16,16,-16,16) + + +# %% Model +@pytest.fixture +def model(stimulus, MATLAB_LODOG_params): + return multyscale.models.LODOG_RHS2007( + stimulus.shape, + visextent, + window_sigma=MATLAB_LODOG_params["sig1"] / 32, + ) + + +# %% Tests +def test_filters(MATLAB_filteroutput, model, stimulus): + filter_output = model.bank.apply(stimulus) + + assert np.allclose(MATLAB_filteroutput, filter_output) + + +def test_weights(model): + assert np.allclose(model.scale_weights, RHS_implementation.w_val) + + +def test_norm_coeffs(model, MATLAB_filteroutput): + weighted_outputs = model.weight_outputs(MATLAB_filteroutput) + norm_coeffs = model.norm_coeffs(weighted_outputs) + RHS_norms = RHS_implementation.LODOG_normalizers(weighted_outputs) + assert np.allclose(norm_coeffs.shape, RHS_norms.shape) + assert np.allclose(norm_coeffs, RHS_norms) + + +def test_spatial_mask(model, MATLAB_LODOG_params): + # Is the spatial (Gaussian) averaging window the same? + RHS_kernel = RHS_implementation.LODOG_mask(sig1=MATLAB_LODOG_params["sig1"]) + spatial_kernels = model.spatial_kernels() + for o, s in np.ndindex(spatial_kernels.shape[:2]): + assert np.allclose(spatial_kernels[o, s], RHS_kernel) + + +def test_normalized_outputs(model, MATLAB_filteroutput, MATLAB_LODOG_params): + weighted_outputs = model.weight_outputs(MATLAB_filteroutput) + + normed_outputs = model.normalize_outputs(weighted_outputs, eps=1e-6) + + RHS_normalized_outputs = RHS_implementation.LODOG_normalize( + weighted_outputs, sig1=MATLAB_LODOG_params["sig1"] + ) + assert np.allclose(normed_outputs, RHS_normalized_outputs) + + +def test_model_output(output_LODOG_MATLAB, model, stimulus): + output = model.apply(stimulus, eps=1e-6) + assert np.allclose(output, output_LODOG_MATLAB) diff --git a/test/test_ODOG.py b/test/test_ODOG.py new file mode 100644 index 0000000..0f8cfd7 --- /dev/null +++ b/test/test_ODOG.py @@ -0,0 +1,42 @@ +# %% Imports +import numpy as np +import pytest +import RHS_implementation + +import multyscale + +# %% Parameters of image +# visual extent, same convention as pyplot: +visextent = np.array([-0.5, 0.5, -0.5, 0.5]) * (1023 / 32) +# NOTE: RHS implementation doesn't actually use (-16,16,-16,16) + + +# %% Model +@pytest.fixture +def model(stimulus): + return multyscale.models.ODOG_RHS2007(stimulus.shape, visextent) + + +# %% Tests +def test_filters(MATLAB_filteroutput, model, stimulus): + filter_output = model.bank.apply(stimulus) + + assert np.allclose(MATLAB_filteroutput, filter_output) + + +def test_weights(model): + assert np.allclose(model.scale_weights, RHS_implementation.w_val) + + +def test_normalize(output_ODOG_MATLAB, model, MATLAB_filteroutput): + weighted_outputs = model.weight_outputs(MATLAB_filteroutput) + normed_outputs = model.normalize_outputs(weighted_outputs) + + output = np.sum(normed_outputs, (0, 1)) + + assert np.allclose(output, output_ODOG_MATLAB) + + +def test_model_output(output_ODOG_MATLAB, model, stimulus): + output = model.apply(stimulus) + assert np.allclose(output, output_ODOG_MATLAB) diff --git a/test/test_RHS_implementation.py b/test/test_RHS_implementation.py new file mode 100644 index 0000000..cd97494 --- /dev/null +++ b/test/test_RHS_implementation.py @@ -0,0 +1,93 @@ +""" Test that Python transplementation matches original MATLAB output + +The original Robinson, Hammon, de Sa (2007) implementation of (F)(L)ODOG is in MATLAB. +The multyscale testsuite provides a Python "transplementation" of the algorithms of this +original MATLAB code. + +This module tests that the model output (and some in between steps) produced by this +Python transplementation matches numerically with the comparable output from original +MATLAB implementation + +The "ground-truth" MATLAB output should be provided in file output_MATLAB.mat, +which is accessed by the pytest fixtures in MATLAB_comparison.py. + +Note that this is selected output, for just a single stimulus. +""" + +import numpy as np +import RHS_implementation + + +def test_filterbank(rhs_bank, MATLAB_bank): + """Python filterbank matches RHS MATLAB filterbank""" + assert np.allclose(rhs_bank, MATLAB_bank) + + +def test_RHSconv_MATLAB(MATLAB_filteroutput, MATLAB_bank, stimulus): + """Python convolution with RHS MATLAB filters matches RHS MATLAB filters output""" + filters_output = np.empty(MATLAB_bank.shape) + for o, s in np.ndindex(MATLAB_bank.shape[:2]): + filters_output[o, s, ...] = RHS_implementation.ourconv( + stimulus, MATLAB_bank[o, s, ...], pad=0.5 + ) + + assert np.allclose(MATLAB_filteroutput, filters_output) + + +def test_RHSconv_RHS(MATLAB_filteroutput, stimulus, rhs_bank): + """Python convolution with Python filters matches RHS MATLAB filters output""" + filters_output = np.empty(rhs_bank.shape) + for o, s in np.ndindex(rhs_bank.shape[:2]): + filters_output[o, s, ...] = RHS_implementation.ourconv( + stimulus, rhs_bank[o, s, ...], pad=0.5 + ) + + assert np.allclose(MATLAB_filteroutput, filters_output) + + +def test_ODOG(output_ODOG_MATLAB, MATLAB_filteroutput): + """Python ODOG normalization & output matches RHS MATLAB ODOG output""" + + # Normalize and read out + output = RHS_implementation.odog_normalize(MATLAB_filteroutput) + + # Compare + assert np.allclose(output, output_ODOG_MATLAB) + + +def test_LODOG(output_LODOG_MATLAB, MATLAB_filteroutput, MATLAB_LODOG_params): + """Python LODOG normalization & output matches RHS MATLAB LODOG output""" + + # Weight filteroutput by scale + filters_output = RHS_implementation.weight(MATLAB_filteroutput) + + # Normalize + normed_multi_responses = RHS_implementation.LODOG_normalize( + filters_output, + **MATLAB_LODOG_params, + ) + + # Readout model output by summing normalized channel outputs + output = np.sum(normed_multi_responses, (0, 1)) + + # Compare to MATLAB output + assert np.allclose(output, output_LODOG_MATLAB) + + +def test_FLODOG(output_FLODOG_MATLAB, MATLAB_filteroutput, MATLAB_FLODOG_params): + """Python FLODOG normalization & output matches RHS MATLAB FLODOG output""" + + # Weight filteroutput by scale + filters_output = RHS_implementation.weight(MATLAB_filteroutput) + + # Normalize + normed_multi_responses = RHS_implementation.FLODOG_normalize( + filters_output, + **MATLAB_FLODOG_params, + ) + + # Sum normalized channel outputs, to read out model output + output = np.sum(normed_multi_responses, (0, 1)) + + # Compare to MATLAB output + assert np.allclose(output, output_FLODOG_MATLAB) diff --git a/test/test_RHS_matlab.py b/test/test_RHS_matlab.py deleted file mode 100644 index fe96729..0000000 --- a/test/test_RHS_matlab.py +++ /dev/null @@ -1,46 +0,0 @@ -# %% -import matplotlib.pyplot as plt -import numpy as np -import RHS_filters - - -def test_filterbank(rhs_bank, matlab_bank): - # %% Visualise - for i in range(rhs_bank.shape[0]): - plt.subplot(rhs_bank.shape[0], 2, i * 2 + 1) - plt.imshow(rhs_bank[i, 6, ...]) - plt.subplot(rhs_bank.shape[0], 2, i * 2 + 2) - plt.imshow(rhs_bank[i, 6, ...]) - - assert np.allclose(rhs_bank, matlab_bank) - - -def test_RHSconv_matlab(matlab_filteroutput, matlab_bank, stimulus): - # RHS convolution with matlab filters matches matlab output - filters_output = np.empty(matlab_bank.shape) - for i in range(matlab_bank.shape[0]): - for j in range(matlab_bank.shape[1]): - filters_output[i, j, ...] = RHS_filters.ourconv(stimulus, matlab_bank[i, j, ...]) - - assert np.allclose(matlab_filteroutput, filters_output) - - -def test_RHSconv_RHS(matlab_filteroutput, stimulus, rhs_bank): - # RHS convolution with python RHS filters matches matlab output - filters_output = np.empty(rhs_bank.shape) - for i in range(rhs_bank.shape[0]): - for j in range(rhs_bank.shape[1]): - filters_output[i, j, ...] = RHS_filters.ourconv(stimulus, rhs_bank[i, j, ...]) - - assert np.allclose(matlab_filteroutput, filters_output) - - -def test_ODOG(stimulus, rhs_bank, output_odog_matlab): - # RHS convolution with python RHS filters matches matlab output - filters_output = np.empty(rhs_bank.shape) - for i in range(rhs_bank.shape[0]): - for j in range(rhs_bank.shape[1]): - filters_output[i, j, ...] = RHS_filters.ourconv(stimulus, rhs_bank[i, j, ...]) - - output = RHS_filters.odog_normalize(filters_output) - assert np.allclose(output, output_odog_matlab) diff --git a/test/test_apply.py b/test/test_apply.py index 19c5b70..29f4d0f 100644 --- a/test/test_apply.py +++ b/test/test_apply.py @@ -1,27 +1,27 @@ import numpy as np -import RHS_filters +import RHS_implementation from multyscale import filters -def test_apply_matlab(matlab_filteroutput, matlab_bank, stimulus): - # multyscale apply with matlab filters matches matlab output - filters_output = np.empty(matlab_bank.shape) - for i in range(matlab_bank.shape[0]): - for j in range(matlab_bank.shape[1]): - filters_output[i, j, ...] = filters.apply(stimulus, matlab_bank[i, j, ...], pad=True) +def test_apply_MATLAB(MATLAB_filteroutput, MATLAB_bank, stimulus): + # multyscale apply with MATLAB filters matches MATLAB output + filters_output = np.empty(MATLAB_bank.shape) + for i in range(MATLAB_bank.shape[0]): + for j in range(MATLAB_bank.shape[1]): + filters_output[i, j, ...] = filters.apply(stimulus, MATLAB_bank[i, j, ...], padval=0.5) - assert np.allclose(matlab_filteroutput, filters_output) + assert np.allclose(MATLAB_filteroutput, filters_output) -def test_apply_RHS(matlab_filteroutput, rhs_bank, stimulus): - # multyscale apply with RHS filters matches matlab output +def test_apply_RHS(MATLAB_filteroutput, rhs_bank, stimulus): + # multyscale apply with RHS filters matches MATLAB output filters_output = np.empty(rhs_bank.shape) for i in range(rhs_bank.shape[0]): for j in range(rhs_bank.shape[1]): - filters_output[i, j, ...] = filters.apply(stimulus, rhs_bank[i, j, ...], pad=True) + filters_output[i, j, ...] = filters.apply(stimulus, rhs_bank[i, j, ...], padval=0.5) - assert np.allclose(matlab_filteroutput, filters_output) + assert np.allclose(MATLAB_filteroutput, filters_output) def test_conv_apply(rhs_bank, stimulus): @@ -30,7 +30,7 @@ def test_conv_apply(rhs_bank, stimulus): for i in range(rhs_bank.shape[0]): for j in range(rhs_bank.shape[1]): f = rhs_bank[i, j] - o_conv[i, j, ...] = RHS_filters.ourconv(stimulus, f) - o_apply[i, j, ...] = filters.apply(stimulus, f, pad=True) + o_conv[i, j, ...] = RHS_implementation.ourconv(stimulus, f, pad=0.5) + o_apply[i, j, ...] = filters.apply(stimulus, f, padval=0.5) assert np.allclose(o_conv, o_apply) diff --git a/test/test_filterbanks.py b/test/test_filterbanks.py index 8dbdfa1..50e18a0 100644 --- a/test/test_filterbanks.py +++ b/test/test_filterbanks.py @@ -1,8 +1,8 @@ import matplotlib.pyplot as plt import numpy as np -import RHS_filters +import RHS_implementation -from multyscale import filterbank +from multyscale import filterbanks # %% Parameters of image shape = (1024, 1024) # filtershape in pixels @@ -11,28 +11,21 @@ # NOTE: RHS implementation doesn't actually use (-16,16,-16,16) -# %% Filterbank -def test_filterbank(matlab_bank): - multy_bank = filterbank.RHS2007((1024, 1024), visextent) - # Visualise filterbank - for i in range(multy_bank.filters.shape[0]): - plt.subplot(multy_bank.filters.shape[0], 2, i * 2 + 1) - plt.imshow(multy_bank.filters[i, 6, ...], extent=visextent) - plt.subplot(multy_bank.filters.shape[0], 2, i * 2 + 2) - plt.imshow(matlab_bank[i, 6, ...]) +def test_filterbank(MATLAB_bank): + multy_bank = filterbanks.RHS2007((1024, 1024), visextent) - assert np.allclose(matlab_bank, multy_bank.filters) + assert np.allclose(MATLAB_bank, multy_bank.filters) -def test_filterbank_apply(stimulus, matlab_filteroutput): - multy_bank = filterbank.RHS2007((1024, 1024), visextent) +def test_filterbank_apply(stimulus, MATLAB_filteroutput): + multy_bank = filterbanks.RHS2007((1024, 1024), visextent) multy_output = multy_bank.apply(stimulus) - assert np.allclose(matlab_filteroutput, multy_output) + assert np.allclose(MATLAB_filteroutput, multy_output) def test_scale_weights(): - bank = filterbank.RHS2007(shape, visextent) + bank = filterbanks.RHS2007(shape, visextent) center_sigmas = np.array(bank.sigmas)[:, 0, 0] - scale_weights = filterbank.scale_weights(center_sigmas, 0.1) - assert np.allclose(scale_weights, RHS_filters.w_val) + scale_weights = filterbanks.scale_weights(center_sigmas, 0.1) + assert np.allclose(scale_weights, RHS_implementation.w_val) diff --git a/test/test_filters.py b/test/test_filters.py index d6e5a51..79c84a3 100644 --- a/test/test_filters.py +++ b/test/test_filters.py @@ -1,12 +1,10 @@ -# %% -import matplotlib.pyplot as plt import numpy as np -import RHS_filters +import RHS_implementation import multyscale # %% RHS bank -rhs_bank = RHS_filters.filterbank() +rhs_bank = RHS_implementation.filterbank() # %% Parameters of image shape = (1024, 1024) # filtershape in pixels @@ -26,16 +24,7 @@ def test_circular_Gaussian(): sigmas = np.array([1, 1]) * sigma1 f = multyscale.filters.gaussian2d(x, y, (sigmas[0], sigmas[1])) f = f / f.sum() - f_2 = RHS_filters.d2gauss(shape[0], sigmas[0] * 32, shape[1], sigmas[0] * 32, 0) - - plt.subplot(2, 2, 1) - plt.imshow(f) - plt.subplot(2, 2, 2) - plt.imshow(f_2) - plt.subplot(2, 2, 3) - plt.plot(f[512, :]) - plt.subplot(2, 2, 4) - plt.plot(f_2[512, :]) + f_2 = RHS_implementation.d2gauss(shape[0], sigmas[0] * 32, shape[1], sigmas[0] * 32, 0) assert np.allclose(f, f_2) @@ -48,16 +37,9 @@ def test_elliptical_Gaussian(): sigmas = np.array([1, 1]) * np.array([sigma1, sigma2]) f = multyscale.filters.gaussian2d(x, y, (sigmas[0], sigmas[1]), orientation=orientation) f = f / f.sum() - f_2 = RHS_filters.d2gauss(shape[0], sigmas[0] * 32, shape[1], sigmas[1] * 32, orientation) - - plt.subplot(2, 2, 1) - plt.imshow(f) - plt.subplot(2, 2, 2) - plt.imshow(f_2) - plt.subplot(2, 2, 3) - plt.plot(f[512, :]) - plt.subplot(2, 2, 4) - plt.plot(f_2[512, :]) + f_2 = RHS_implementation.d2gauss( + shape[0], sigmas[0] * 32, shape[1], sigmas[1] * 32, orientation + ) assert np.allclose(f, f_2) @@ -67,16 +49,7 @@ def test_ODOG(): orientation = 150 sigma3 = 2 sigmas = np.array([[1, 1], [1, 2]]) * sigma3 - rhs_odog = RHS_filters.odog(shape[0], shape[1], sigma3 * 32, orientation=orientation) + rhs_odog = RHS_implementation.odog(shape[0], shape[1], sigma3 * 32, orientation=orientation) multy_odog = multyscale.filters.odog(x, y, sigmas, orientation=(orientation, orientation)) - plt.subplot(2, 2, 1) - plt.imshow(rhs_odog) - plt.subplot(2, 2, 2) - plt.imshow(multy_odog) - plt.subplot(2, 2, 3) - plt.plot(rhs_odog[512, :]) - plt.subplot(2, 2, 4) - plt.plot(multy_odog[512, :]) - assert np.allclose(rhs_odog, multy_odog) diff --git a/test/test_models.py b/test/test_models.py index abbcb0b..84cf393 100644 --- a/test/test_models.py +++ b/test/test_models.py @@ -10,24 +10,28 @@ # %% Tests -def test_odog_output(output_odog_matlab, stimulus): +def test_odog_output(output_ODOG_MATLAB, stimulus): model = models.ODOG_RHS2007(stimulus.shape, visextent) output = model.apply(stimulus) - assert np.allclose(output, output_odog_matlab) - - -def test_lodog_output(stimulus, output_lodog_matlab): - window_sigma = 128 / 32 - - model = models.LODOG_RHS2007(stimulus.shape, visextent, window_sigma=window_sigma) - output = model.apply(stimulus) - assert np.allclose(output, output_lodog_matlab) - - -def test_flodog_output(stimulus, output_flodog_matlab): - windowSizeScalar = 4 - m = 0.5 - - model = models.FLODOG_RHS2007(stimulus.shape, visextent) - output = model.apply(stimulus) - assert np.allclose(output, output_flodog_matlab) + assert np.allclose(output, output_ODOG_MATLAB) + + +def test_lodog_output(stimulus, output_LODOG_MATLAB, MATLAB_LODOG_params): + model = models.LODOG_RHS2007( + stimulus.shape, + visextent, + window_sigma=MATLAB_LODOG_params["sig1"] / 32, + ) + output = model.apply(stimulus, eps=1e-6) + assert np.allclose(output, output_LODOG_MATLAB) + + +def test_flodog_output(stimulus, output_FLODOG_MATLAB, MATLAB_FLODOG_params): + model = models.FLODOG_RHS2007( + stimulus.shape, + visextent, + sdmix=MATLAB_FLODOG_params["sdmix"], + spatial_window_scalar=MATLAB_FLODOG_params["sigx"], + ) + output = model.apply(stimulus, eps=1e-6) + assert np.allclose(output, output_FLODOG_MATLAB) diff --git a/test/test_normalization.py b/test/test_normalization.py new file mode 100644 index 0000000..e35cf5e --- /dev/null +++ b/test/test_normalization.py @@ -0,0 +1,80 @@ +import numpy as np +import pytest + +from multyscale import filters, normalization + +shape = (1024, 1024) +visextent = (-16, 16, -16, 16) +O, S = (6, 7) + + +def test_scale_norm_weights_equal(): + norm_weights = normalization.scale_norm_weights_equal(S) + + assert np.all(norm_weights == np.ones((S, S))) + + +@pytest.mark.xfail +def test_scale_norm_weights_gaussian(): + raise NotImplementedError() + + +def test_orientation_norm_weights(): + norm_weights = normalization.orientation_norm_weights(O) + + assert np.all(norm_weights == np.identity(O)) + + +def test_norm_weights_combine(): + orientation_norm_weights = normalization.orientation_norm_weights(O) + scale_norm_weights = normalization.scale_norm_weights_equal(S) + norm_weights = normalization.create_normalization_weights( + O, + S, + scale_norm_weights, + orientation_norm_weights, + ) + + ground_truth = np.zeros((O, S, O, S)) + for o_prime, s_prime in np.ndindex(ground_truth.shape[:2]): + ground_truth[o_prime, s_prime, o_prime, :] = np.ones((1, S)) + + assert np.all(norm_weights == ground_truth) + + +@pytest.mark.xfail +def test_norm_coeffs(): + raise NotImplementedError() + + +def test_spatial_avg_kernel(): + ODOG_kernel = normalization.spatial_kernel_globalmean(shape) + + img = np.random.rand(shape[0], shape[1]) + + filtered = filters.apply(img, ODOG_kernel, padval=0) + + assert np.allclose(filtered, img.mean()) + + +def test_norm_energy(): + filters_output = np.random.rand(O, S, shape[0], shape[1]) + + scale_norm_weights = normalization.scale_norm_weights_equal(S) + orientation_norm_weights = normalization.orientation_norm_weights(O) + normalization_weights = normalization.create_normalization_weights( + O, S, scale_norm_weights, orientation_norm_weights + ) + + norm_coeffs = normalization.norm_coeffs(filters_output, normalization_weights) + + energies = np.ndarray(filters_output.shape) + kernel = normalization.spatial_kernel_globalmean(shape) + + for o, s in np.ndindex(filters_output.shape[:2]): + energies[o, s, ...] = normalization.norm_energy(norm_coeffs[o, s, ...], kernel) + + ground_truth = np.sqrt((norm_coeffs**2).mean(axis=(2, 3)) + 1e-6) + ground_truth = np.tile(np.expand_dims(ground_truth, [2, 3]), (1, 1, shape[0], shape[1])) + + assert np.allclose(energies, ground_truth)