From ea2de97fd7f50c60a387486b2bc8a849b402a9d0 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Mon, 29 Jul 2019 17:16:06 +0200 Subject: [PATCH] Reimplement NonUniformImage, PcolorImage in Python, not C. It's much shorter... Perf check: ```python from timeit import Timer from matplotlib import pyplot as plt from matplotlib.image import NonUniformImage, PcolorImage import numpy as np N = 100 fig, (ax_nn, ax_nb, ax_pc) = plt.subplots(3) ax_nn.set(xlim=(-.5, .75), ylim=(-.5, .75)) nn = NonUniformImage(ax_nn) nn.set_data(np.linspace(0, 1, 2 * N) ** 2, np.linspace(0, 1, N) ** 2, np.arange(2 * N**2).reshape((N, 2 * N))) ax_nn.images.append(nn) ax_nb.set(xlim=(-.5, .75), ylim=(-.5, .75)) nb = NonUniformImage(ax_nb, interpolation="bilinear") nb.set_data(np.linspace(0, 1, 2 * N) ** 2, np.linspace(0, 1, N) ** 2, np.arange(2 * N**2).reshape((N, 2 * N))) ax_nb.images.append(nb) ax_pc.set(xlim=(-.5, .75), ylim=(-.5, .75)) pc = PcolorImage(ax_pc) pc.set_data(np.linspace(0, 1, 2 * N + 1) ** 2, np.linspace(0, 1, N + 1) ** 2, np.arange(2 * N**2).reshape((N, 2 * N))) ax_pc.images.append(pc) fig.canvas.draw() n, t = Timer("nn.make_image(fig._cachedRenderer)", globals=globals()).autorange() print(f"NN: {1000*t/n:.4f}ms") n, t = Timer("nb.make_image(fig._cachedRenderer)", globals=globals()).autorange() print(f"NB: {1000*t/n:.4f}ms") n, t = Timer("pc.make_image(fig._cachedRenderer)", globals=globals()).autorange() print(f"PC: {1000*t/n:.4f}ms") plt.show() ``` --- .../next_api_changes/behavior/14913-AL.rst | 9 + lib/matplotlib/image.py | 89 ++++++-- .../test_image/nonuniform_and_pcolor.png | Bin 0 -> 3431 bytes lib/matplotlib/tests/test_image.py | 16 ++ setupext.py | 1 - src/_image.cpp | 118 ----------- src/_image.h | 198 ------------------ src/_image_wrapper.cpp | 91 -------- 8 files changed, 91 insertions(+), 431 deletions(-) create mode 100644 doc/api/next_api_changes/behavior/14913-AL.rst create mode 100644 lib/matplotlib/tests/baseline_images/test_image/nonuniform_and_pcolor.png delete mode 100644 src/_image.cpp delete mode 100644 src/_image.h diff --git a/doc/api/next_api_changes/behavior/14913-AL.rst b/doc/api/next_api_changes/behavior/14913-AL.rst new file mode 100644 index 000000000000..9e98b7e6c8ec --- /dev/null +++ b/doc/api/next_api_changes/behavior/14913-AL.rst @@ -0,0 +1,9 @@ +The output of ``NonUniformImage`` and ``PcolorImage`` has changed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Pixel-level differences may be observed in images generated using +`.NonUniformImage` or `.PcolorImage`, typically for pixels exactly at the +boundary between two data cells (no user-facing axes method currently generates +`.NonUniformImage`\s, and only `.pcolorfast` can generate `.PcolorImage`\s). +These artists are also now slower, normally by ~1.5x but sometimes more (in +particular for ``NonUniformImage(interpolation="bilinear")``. This slowdown +arises from fixing occasional floating point inaccuracies. diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index e755b204ab9d..bdbeba43cb15 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -1055,14 +1055,51 @@ def make_image(self, renderer, magnification=1.0, unsampled=False): self._is_grayscale = False vl = self.axes.viewLim l, b, r, t = self.axes.bbox.extents - width = (round(r) + 0.5) - (round(l) - 0.5) - height = (round(t) + 0.5) - (round(b) - 0.5) - width *= magnification - height *= magnification - im = _image.pcolor(self._Ax, self._Ay, A, - int(height), int(width), - (vl.x0, vl.x1, vl.y0, vl.y1), - _interpd_[self._interpolation]) + width = int(((round(r) + 0.5) - (round(l) - 0.5)) * magnification) + height = int(((round(t) + 0.5) - (round(b) - 0.5)) * magnification) + x_pix = np.linspace(vl.x0, vl.x1, width) + y_pix = np.linspace(vl.y0, vl.y1, height) + if self._interpolation == "nearest": + x_mid = (self._Ax[:-1] + self._Ax[1:]) / 2 + y_mid = (self._Ay[:-1] + self._Ay[1:]) / 2 + x_int = x_mid.searchsorted(x_pix) + y_int = y_mid.searchsorted(y_pix) + # The following is equal to `A[y_int[:, None], x_int[None, :]]`, + # but many times faster. Both casting to uint32 (to have an + # effectively 1D array) and manual index flattening matter. + im = ( + np.ascontiguousarray(A).view(np.uint32).ravel()[ + np.add.outer(y_int * A.shape[1], x_int)] + .view(np.uint8).reshape((height, width, 4))) + else: # self._interpolation == "bilinear" + # Use np.interp to compute x_int/x_float has similar speed. + x_int = np.clip( + self._Ax.searchsorted(x_pix) - 1, 0, len(self._Ax) - 2) + y_int = np.clip( + self._Ay.searchsorted(y_pix) - 1, 0, len(self._Ay) - 2) + idx_int = np.add.outer(y_int * A.shape[1], x_int) + x_frac = np.clip( + np.divide(x_pix - self._Ax[x_int], np.diff(self._Ax)[x_int], + dtype=np.float32), # Downcasting helps with speed. + 0, 1) + y_frac = np.clip( + np.divide(y_pix - self._Ay[y_int], np.diff(self._Ay)[y_int], + dtype=np.float32), + 0, 1) + f00 = np.outer(1 - y_frac, 1 - x_frac) + f10 = np.outer(y_frac, 1 - x_frac) + f01 = np.outer(1 - y_frac, x_frac) + f11 = np.outer(y_frac, x_frac) + im = np.empty((height, width, 4), np.uint8) + for chan in range(4): + ac = A[:, :, chan].reshape(-1) # reshape(-1) avoids a copy. + # Shifting the buffer start (`ac[offset:]`) avoids an array + # addition (`ac[idx_int + offset]`). + buf = f00 * ac[idx_int] + buf += f10 * ac[A.shape[1]:][idx_int] + buf += f01 * ac[1:][idx_int] + buf += f11 * ac[A.shape[1] + 1:][idx_int] + im[:, :, chan] = buf # Implicitly casts to uint8. return im, l, b, IdentityTransform() def set_data(self, x, y, A): @@ -1186,27 +1223,33 @@ def make_image(self, renderer, magnification=1.0, unsampled=False): raise RuntimeError('You must first set the image array') if unsampled: raise ValueError('unsampled not supported on PColorImage') - fc = self.axes.patch.get_facecolor() - bg = mcolors.to_rgba(fc, 0) - bg = (np.array(bg)*255).astype(np.uint8) + + if self._rgbacache is None: + A = self.to_rgba(self._A, bytes=True) + self._rgbacache = np.pad(A, [(1, 1), (1, 1), (0, 0)], "constant") + if self._A.ndim == 2: + self._is_grayscale = self.cmap.is_gray() + padded_A = self._rgbacache + bg = mcolors.to_rgba(self.axes.patch.get_facecolor(), 0) + bg = (np.array(bg) * 255).astype(np.uint8) + if (padded_A[0, 0] != bg).all(): + padded_A[[0, -1], :] = padded_A[:, [0, -1]] = bg + l, b, r, t = self.axes.bbox.extents width = (round(r) + 0.5) - (round(l) - 0.5) height = (round(t) + 0.5) - (round(b) - 0.5) width = int(round(width * magnification)) height = int(round(height * magnification)) - if self._rgbacache is None: - A = self.to_rgba(self._A, bytes=True) - self._rgbacache = A - if self._A.ndim == 2: - self._is_grayscale = self.cmap.is_gray() - else: - A = self._rgbacache vl = self.axes.viewLim - im = _image.pcolor2(self._Ax, self._Ay, A, - height, - width, - (vl.x0, vl.x1, vl.y0, vl.y1), - bg) + + x_pix = np.linspace(vl.x0, vl.x1, width) + y_pix = np.linspace(vl.y0, vl.y1, height) + x_int = self._Ax.searchsorted(x_pix) + y_int = self._Ay.searchsorted(y_pix) + im = ( # See comment in NonUniformImage.make_image re: performance. + padded_A.view(np.uint32).ravel()[ + np.add.outer(y_int * padded_A.shape[1], x_int)] + .view(np.uint8).reshape((height, width, 4))) return im, l, b, IdentityTransform() def _check_unsampled_image(self): diff --git a/lib/matplotlib/tests/baseline_images/test_image/nonuniform_and_pcolor.png b/lib/matplotlib/tests/baseline_images/test_image/nonuniform_and_pcolor.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0aa4815c4e850e335d4823f6803fcc336cd3ba GIT binary patch literal 3431 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 - -// utilities for irregular grids -void _bin_indices_middle( - unsigned int *irows, int nrows, const float *ys1, unsigned long ny, float dy, float y_min) -{ - int i, j, j_last; - unsigned int *rowstart = irows; - const float *ys2 = ys1 + 1; - const float *yl = ys1 + ny; - float yo = y_min + dy / 2.0f; - float ym = 0.5f * (*ys1 + *ys2); - // y/rows - j = 0; - j_last = j; - for (i = 0; i < nrows; i++, yo += dy, rowstart++) { - while (ys2 != yl && yo > ym) { - ys1 = ys2; - ys2 = ys1 + 1; - ym = 0.5f * (*ys1 + *ys2); - j++; - } - *rowstart = j - j_last; - j_last = j; - } -} - -void _bin_indices_middle_linear(float *arows, - unsigned int *irows, - int nrows, - const float *y, - unsigned long ny, - float dy, - float y_min) -{ - int i; - int ii = 0; - int iilast = (int)ny - 1; - float sc = 1 / dy; - int iy0 = (int)floor(sc * (y[ii] - y_min)); - int iy1 = (int)floor(sc * (y[ii + 1] - y_min)); - float invgap = 1.0f / (iy1 - iy0); - for (i = 0; i < nrows && i <= iy0; i++) { - irows[i] = 0; - arows[i] = 1.0; - } - for (; i < nrows; i++) { - while (i > iy1 && ii < iilast) { - ii++; - iy0 = iy1; - iy1 = (int)floor(sc * (y[ii + 1] - y_min)); - invgap = 1.0f / (iy1 - iy0); - } - if (i >= iy0 && i <= iy1) { - irows[i] = ii; - arows[i] = (iy1 - i) * invgap; - } else - break; - } - for (; i < nrows; i++) { - irows[i] = iilast - 1; - arows[i] = 0.0; - } -} - -void _bin_indices(int *irows, int nrows, const double *y, unsigned long ny, double sc, double offs) -{ - int i; - if (sc * (y[ny - 1] - y[0]) > 0) { - int ii = 0; - int iilast = (int)ny - 1; - int iy0 = (int)floor(sc * (y[ii] - offs)); - int iy1 = (int)floor(sc * (y[ii + 1] - offs)); - for (i = 0; i < nrows && i < iy0; i++) { - irows[i] = -1; - } - for (; i < nrows; i++) { - while (i > iy1 && ii < iilast) { - ii++; - iy0 = iy1; - iy1 = (int)floor(sc * (y[ii + 1] - offs)); - } - if (i >= iy0 && i <= iy1) - irows[i] = ii; - else - break; - } - for (; i < nrows; i++) { - irows[i] = -1; - } - } else { - int iilast = (int)ny - 1; - int ii = iilast; - int iy0 = (int)floor(sc * (y[ii] - offs)); - int iy1 = (int)floor(sc * (y[ii - 1] - offs)); - for (i = 0; i < nrows && i < iy0; i++) { - irows[i] = -1; - } - for (; i < nrows; i++) { - while (i > iy1 && ii > 1) { - ii--; - iy0 = iy1; - iy1 = (int)floor(sc * (y[ii - 1] - offs)); - } - if (i >= iy0 && i <= iy1) - irows[i] = ii - 1; - else - break; - } - for (; i < nrows; i++) { - irows[i] = -1; - } - } -} diff --git a/src/_image.h b/src/_image.h deleted file mode 100644 index 37a080fff1d4..000000000000 --- a/src/_image.h +++ /dev/null @@ -1,198 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -/* image.h - * - */ - -#ifndef MPL_IMAGE_H -#define MPL_IMAGE_H - -#include - - -// utilities for irregular grids -void _bin_indices_middle( - unsigned int *irows, int nrows, const float *ys1, unsigned long ny, float dy, float y_min); -void _bin_indices_middle_linear(float *arows, - unsigned int *irows, - int nrows, - const float *y, - unsigned long ny, - float dy, - float y_min); -void _bin_indices(int *irows, int nrows, const double *y, unsigned long ny, double sc, double offs); - -template -void pcolor(CoordinateArray &x, - CoordinateArray &y, - ColorArray &d, - unsigned int rows, - unsigned int cols, - float bounds[4], - int interpolation, - OutputArray &out) -{ - if (rows >= 32768 || cols >= 32768) { - throw std::runtime_error("rows and cols must both be less than 32768"); - } - - float x_min = bounds[0]; - float x_max = bounds[1]; - float y_min = bounds[2]; - float y_max = bounds[3]; - float width = x_max - x_min; - float height = y_max - y_min; - float dx = width / ((float)cols); - float dy = height / ((float)rows); - - // Check we have something to output to - if (rows == 0 || cols == 0) { - throw std::runtime_error("Cannot scale to zero size"); - } - - if (d.dim(2) != 4) { - throw std::runtime_error("data must be in RGBA format"); - } - - // Check dimensions match - unsigned long nx = x.dim(0); - unsigned long ny = y.dim(0); - if (nx != (unsigned long)d.dim(1) || ny != (unsigned long)d.dim(0)) { - throw std::runtime_error("data and axis dimensions do not match"); - } - - // Allocate memory for pointer arrays - std::vector rowstarts(rows); - std::vector colstarts(cols); - - // Calculate the pointer arrays to map input x to output x - unsigned int i, j; - unsigned int *colstart = &colstarts[0]; - unsigned int *rowstart = &rowstarts[0]; - const float *xs1 = x.data(); - const float *ys1 = y.data(); - - // Copy data to output buffer - const unsigned char *start; - const unsigned char *inposition; - size_t inrowsize = nx * 4; - size_t rowsize = cols * 4; - unsigned char *position = (unsigned char *)out.data(); - unsigned char *oldposition = NULL; - start = d.data(); - - if (interpolation == NEAREST) { - _bin_indices_middle(colstart, cols, xs1, nx, dx, x_min); - _bin_indices_middle(rowstart, rows, ys1, ny, dy, y_min); - for (i = 0; i < rows; i++, rowstart++) { - if (i > 0 && *rowstart == 0) { - memcpy(position, oldposition, rowsize * sizeof(unsigned char)); - oldposition = position; - position += rowsize; - } else { - oldposition = position; - start += *rowstart * inrowsize; - inposition = start; - for (j = 0, colstart = &colstarts[0]; j < cols; j++, position += 4, colstart++) { - inposition += *colstart * 4; - memcpy(position, inposition, 4 * sizeof(unsigned char)); - } - } - } - } else if (interpolation == BILINEAR) { - std::vector acols(cols); - std::vector arows(rows); - - _bin_indices_middle_linear(&acols[0], colstart, cols, xs1, nx, dx, x_min); - _bin_indices_middle_linear(&arows[0], rowstart, rows, ys1, ny, dy, y_min); - double a00, a01, a10, a11, alpha, beta; - - // Copy data to output buffer - for (i = 0; i < rows; i++) { - for (j = 0; j < cols; j++) { - alpha = arows[i]; - beta = acols[j]; - - a00 = alpha * beta; - a01 = alpha * (1.0 - beta); - a10 = (1.0 - alpha) * beta; - a11 = 1.0 - a00 - a01 - a10; - - for (size_t k = 0; k < 4; ++k) { - position[k] = - d(rowstart[i], colstart[j], k) * a00 + - d(rowstart[i], colstart[j] + 1, k) * a01 + - d(rowstart[i] + 1, colstart[j], k) * a10 + - d(rowstart[i] + 1, colstart[j] + 1, k) * a11; - } - position += 4; - } - } - } -} - -template -void pcolor2(CoordinateArray &x, - CoordinateArray &y, - ColorArray &d, - unsigned int rows, - unsigned int cols, - float bounds[4], - Color &bg, - OutputArray &out) -{ - double x_left = bounds[0]; - double x_right = bounds[1]; - double y_bot = bounds[2]; - double y_top = bounds[3]; - - // Check we have something to output to - if (rows == 0 || cols == 0) { - throw std::runtime_error("rows or cols is zero; there are no pixels"); - } - - if (d.dim(2) != 4) { - throw std::runtime_error("data must be in RGBA format"); - } - - // Check dimensions match - unsigned long nx = x.dim(0); - unsigned long ny = y.dim(0); - if (nx != (unsigned long)d.dim(1) + 1 || ny != (unsigned long)d.dim(0) + 1) { - throw std::runtime_error("data and axis bin boundary dimensions are incompatible"); - } - - if (bg.dim(0) != 4) { - throw std::runtime_error("bg must be in RGBA format"); - } - - std::vector irows(rows); - std::vector jcols(cols); - - // Calculate the pointer arrays to map input x to output x - size_t i, j; - const double *x0 = x.data(); - const double *y0 = y.data(); - double sx = cols / (x_right - x_left); - double sy = rows / (y_top - y_bot); - _bin_indices(&jcols[0], cols, x0, nx, sx, x_left); - _bin_indices(&irows[0], rows, y0, ny, sy, y_bot); - - // Copy data to output buffer - unsigned char *position = (unsigned char *)out.data(); - - for (i = 0; i < rows; i++) { - for (j = 0; j < cols; j++) { - if (irows[i] == -1 || jcols[j] == -1) { - memcpy(position, (const unsigned char *)bg.data(), 4 * sizeof(unsigned char)); - } else { - for (size_t k = 0; k < 4; ++k) { - position[k] = d(irows[i], jcols[j], k); - } - } - position += 4; - } - } -} - -#endif diff --git a/src/_image_wrapper.cpp b/src/_image_wrapper.cpp index 73d093aa3b18..a15e67c373e8 100644 --- a/src/_image_wrapper.cpp +++ b/src/_image_wrapper.cpp @@ -1,6 +1,5 @@ #include "mplutils.h" #include "_image_resample.h" -#include "_image.h" #include "numpy_cpp.h" #include "py_converters.h" @@ -290,98 +289,8 @@ image_resample(PyObject *self, PyObject* args, PyObject *kwargs) return NULL; } - -const char *image_pcolor__doc__ = - "pcolor(x, y, data, rows, cols, bounds)\n" - "\n" - "Generate a pseudo-color image from data on a non-uniform grid using\n" - "nearest neighbour or linear interpolation.\n" - "bounds = (x_min, x_max, y_min, y_max)\n" - "interpolation = NEAREST or BILINEAR \n"; - -static PyObject *image_pcolor(PyObject *self, PyObject *args, PyObject *kwds) -{ - numpy::array_view x; - numpy::array_view y; - numpy::array_view d; - npy_intp rows, cols; - float bounds[4]; - int interpolation; - - if (!PyArg_ParseTuple(args, - "O&O&O&nn(ffff)i:pcolor", - &x.converter, - &x, - &y.converter, - &y, - &d.converter_contiguous, - &d, - &rows, - &cols, - &bounds[0], - &bounds[1], - &bounds[2], - &bounds[3], - &interpolation)) { - return NULL; - } - - npy_intp dim[3] = {rows, cols, 4}; - numpy::array_view output(dim); - - CALL_CPP("pcolor", (pcolor(x, y, d, rows, cols, bounds, interpolation, output))); - - return output.pyobj(); -} - -const char *image_pcolor2__doc__ = - "pcolor2(x, y, data, rows, cols, bounds, bg)\n" - "\n" - "Generate a pseudo-color image from data on a non-uniform grid\n" - "specified by its cell boundaries.\n" - "bounds = (x_left, x_right, y_bot, y_top)\n" - "bg = ndarray of 4 uint8 representing background rgba\n"; - -static PyObject *image_pcolor2(PyObject *self, PyObject *args, PyObject *kwds) -{ - numpy::array_view x; - numpy::array_view y; - numpy::array_view d; - npy_intp rows, cols; - float bounds[4]; - numpy::array_view bg; - - if (!PyArg_ParseTuple(args, - "O&O&O&nn(ffff)O&:pcolor2", - &x.converter_contiguous, - &x, - &y.converter_contiguous, - &y, - &d.converter_contiguous, - &d, - &rows, - &cols, - &bounds[0], - &bounds[1], - &bounds[2], - &bounds[3], - &bg.converter, - &bg)) { - return NULL; - } - - npy_intp dim[3] = {rows, cols, 4}; - numpy::array_view output(dim); - - CALL_CPP("pcolor2", (pcolor2(x, y, d, rows, cols, bounds, bg, output))); - - return output.pyobj(); -} - static PyMethodDef module_functions[] = { {"resample", (PyCFunction)image_resample, METH_VARARGS|METH_KEYWORDS, image_resample__doc__}, - {"pcolor", (PyCFunction)image_pcolor, METH_VARARGS, image_pcolor__doc__}, - {"pcolor2", (PyCFunction)image_pcolor2, METH_VARARGS, image_pcolor2__doc__}, {NULL} };