Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- {
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## scipy generic_filter like feature in pure numpy\n",
- "\n",
- "The idea is to create a single array from an image that provides the elements of a window for each pixel in the input image (function filtergrid2d).\n",
- "\n",
- "For now it is only done for 2D arrays, but it should be possible to extend to nD arrays.\n",
- "\n",
- "The initial purpose was to do an image median filter which ignores NaNs, but this can be used for any kind of filter based on window."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {},
- "outputs": [],
- "source": [
- "import numpy\n",
- "from numpy.lib import stride_tricks\n",
- "\n",
- "\n",
- "def filtergrid2d(data, size):\n",
- " \"\"\"Create a 3D array of all filter windows of an image\n",
- " \n",
- " :param numpy.ndarray data: The array for which to create the grid\n",
- " :param Union[int,List[int]] size:\n",
- " Size of the window area for each dimensions or a\n",
- " single int for window area with same size on each dimension\n",
- " :return: Array of windows corresponding to each input array elements\n",
- " (initial dimensions, window_size)\n",
- " :rtype: numpy.ndarray\n",
- " \"\"\"\n",
- " data = numpy.ascontiguousarray(data)\n",
- " itemsize = data.itemsize\n",
- " height, width = data.shape\n",
- "\n",
- " if isinstance(size, int):\n",
- " size = size, size\n",
- " kernel_height = min(size[0], height)\n",
- " kernel_width = min(size[1], width)\n",
- "\n",
- " # Create a 4D view of the data that is convenient to apply the filter at once\n",
- " # view uses memory overrides and overflows allocated memory...\n",
- " view = stride_tricks.as_strided(\n",
- " data,\n",
- " shape=(height + 1 - kernel_height, width, kernel_height, width),\n",
- " strides=(itemsize * width, itemsize, itemsize * width, itemsize))\n",
- " #numpy >=1.12 , writeable=False)\n",
- "\n",
- " # Remove access to data that is not needed to apply filter\n",
- " grid = view[:, :(width + 1 - kernel_width), :, :kernel_width]\n",
- " # Reshape array to (output height, output width, kernel size)\n",
- " grid = grid.reshape(height + 1 - kernel_height,\n",
- " width + 1 - kernel_width,\n",
- " kernel_height * kernel_width) # alternative kernel_height, kernel_width\n",
- " return grid"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Example of use"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "metadata": {},
- "outputs": [],
- "source": [
- "def nanmedfilt2d(image, size=(3, 3)):\n",
- " return numpy.nanmedian(filtergrid2d(image, size), axis=-1)\n",
- "\n",
- "def medfilt2d(image, size=(3, 3)):\n",
- " return numpy.median(filtergrid2d(image, size), axis=-1)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Benchmark"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {},
- "outputs": [],
- "source": [
- "# benchmark images\n",
- "images = [numpy.random.random(500**2).reshape(500, 500), numpy.random.random(2048**2).reshape(2048, 2048)]\n",
- "\n",
- "# benchmark window size\n",
- "sizes = [(5, 5), (21, 21)]\n",
- "\n",
- "# benchmark conditions\n",
- "tests = [(image, size) for size in sizes for image in images]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 28,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "image shape (500, 500) window (5, 5) :\n",
- "120 ms ± 1.12 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
- "image shape (2048, 2048) window (5, 5) :\n",
- "1.98 s ± 9.85 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
- "image shape (500, 500) window (21, 21) :\n",
- "1.48 s ± 863 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
- "image shape (2048, 2048) window (21, 21) :\n",
- "26.6 s ± 76.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
- ]
- }
- ],
- "source": [
- "# filtergrid2d\n",
- "\n",
- "from scipy.signal import medfilt2d as scipy_medfilt2d\n",
- "\n",
- "for image, size in tests:\n",
- " print('image shape', image.shape, 'window', size, ':')\n",
- " # Do not compare borders\n",
- " assert(numpy.abs(scipy_medfilt2d(image, size)[size[0]//2:-(size[0]//2), size[1]//2:-(size[1]//2)] -\n",
- " medfilt2d(image, size)).max() == 0.)\n",
- "\n",
- " %timeit numpy.median(filtergrid2d(image, size), axis=-1)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 29,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "image shape (500, 500) window (5, 5) :\n",
- "75.1 ms ± 434 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
- "image shape (2048, 2048) window (5, 5) :\n",
- "1.27 s ± 6.88 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
- "image shape (500, 500) window (21, 21) :\n",
- "1.08 s ± 9.87 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
- "image shape (2048, 2048) window (21, 21) :\n",
- "18.5 s ± 223 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
- ]
- }
- ],
- "source": [
- "# scipy.signal.medfilt2d benchmark\n",
- "from scipy.signal import medfilt2d as scipy_medfilt2d\n",
- "\n",
- "for image, size in tests:\n",
- " print('image shape', image.shape, 'window', size, ':')\n",
- " %timeit scipy_medfilt2d(image, size)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "image shape (500, 500) window (5, 5) :\n",
- "85.4 ms ± 202 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
- "image shape (2048, 2048) window (5, 5) :\n",
- "1.43 s ± 1.46 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
- "image shape (500, 500) window (21, 21) :\n",
- "1.11 s ± 897 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
- "image shape (2048, 2048) window (21, 21) :\n",
- "19 s ± 95.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
- ]
- }
- ],
- "source": [
- "# scipy.ndimage.filters.median_filter\n",
- "from scipy.ndimage.filters import median_filter\n",
- "\n",
- "for image, size in tests:\n",
- " print('image shape', image.shape, 'window', size, ':')\n",
- " %timeit median_filter(image, size, mode='nearest')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 31,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "image shape (500, 500) window (5, 5) :\n",
- "Duration of a single run 10.746914625167847 s\n",
- "image shape (2048, 2048) window (5, 5) :\n",
- "Duration of a single run 173.32792830467224 s\n",
- "image shape (500, 500) window (21, 21) :\n",
- "Duration of a single run 11.813754081726074 s\n",
- "image shape (2048, 2048) window (21, 21) :\n",
- "Duration of a single run 199.75266814231873 s\n"
- ]
- }
- ],
- "source": [
- "# scipy generic filter + nanmedian\n",
- "# !!! slow !!!\n",
- "from scipy.ndimage.filters import generic_filter\n",
- "import time\n",
- "\n",
- "for image, size in tests:\n",
- " print('image shape', image.shape, 'window', size, ':')\n",
- " st = time.time()\n",
- " generic_filter(image, numpy.nanmedian, size)\n",
- " print('Duration of a single run', time.time() - st, 's')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.4.2"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
- }
Add Comment
Please, Sign In to add comment