Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b383663

Browse files
committed
NonUniformImage and PcolorImage containerization
1 parent 56a0a09 commit b383663

1 file changed

Lines changed: 96 additions & 41 deletions

File tree

lib/matplotlib/image.py

Lines changed: 96 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,32 @@ def query(self, graph, parent_coordinates="axes"):
260260
# TODO hash
261261

262262

263+
@dataclass
264+
class NonUniformImageContainer(ImageContainer):
265+
def describe(self):
266+
imshape = list(self.image.shape)
267+
imshape[:2] = ("M", "N")
268+
269+
return {
270+
"x": Desc(("M",), "data"),
271+
"y": Desc(("N",), "data"),
272+
"image": Desc(tuple(imshape), "data"),
273+
}
274+
275+
276+
@dataclass
277+
class PcolorImageContainer(ImageContainer):
278+
def describe(self):
279+
imshape = list(self.image.shape)
280+
imshape[:2] = ("M", "N")
281+
282+
return {
283+
"x": Desc(("M+1",), "data"),
284+
"y": Desc(("N+1",), "data"),
285+
"image": Desc(tuple(imshape), "data"),
286+
}
287+
288+
263289
class _ImageBase(mcolorizer.ColorizingArtist):
264290
"""
265291
Base class for images.
@@ -333,6 +359,8 @@ def get_container(self):
333359
def _get_graph(self):
334360
# TODO see about getting rid of self.axes
335361
ax = self.axes
362+
if ax is None:
363+
return Graph([])
336364
desc: Desc = Desc(("N",), coordinates="data")
337365
xy: dict[str, Desc] = {"x": desc, "y": desc}
338366
implicit_graph = Graph(
@@ -1038,7 +1066,7 @@ def set_extent(self, extent, **kwargs):
10381066
will redo the autoscaling in accord with `~.Axes.dataLim`.
10391067
"""
10401068
if not isinstance(self._container, ImageContainer):
1041-
raise TypeError("Cannot use 'set_data' on custom container types")
1069+
raise TypeError("Cannot use 'set_extent' on custom container types")
10421070

10431071
if extent is None:
10441072
sz = self.get_size()
@@ -1145,18 +1173,27 @@ def __init__(self, ax, *, interpolation='nearest', **kwargs):
11451173
)
11461174
super().__init__(ax, **kwargs)
11471175
self.set_interpolation(interpolation)
1176+
self._container = NonUniformImageContainer(
1177+
np.array([0.,1.]),
1178+
np.array([0.,1.]),
1179+
np.array([[np.nan]]),
1180+
)
11481181

11491182
def _check_unsampled_image(self):
11501183
"""Return False. Do not use unsampled image."""
11511184
return False
11521185

11531186
def make_image(self, renderer, magnification=1.0, unsampled=False):
11541187
# docstring inherited
1155-
if self._A is None:
1156-
raise RuntimeError('You must first set the image array')
11571188
if unsampled:
11581189
raise ValueError('unsampled not supported on NonUniformImage')
1159-
A = self._A
1190+
1191+
q, _ = self._container.query(self._get_graph())
1192+
Ax = q["x"]
1193+
Ay = q["y"]
1194+
1195+
A = q["image"]
1196+
11601197
if A.ndim == 2:
11611198
if A.dtype != np.uint8:
11621199
A = self.to_rgba(A, bytes=True)
@@ -1182,8 +1219,8 @@ def make_image(self, renderer, magnification=1.0, unsampled=False):
11821219
[(l, y) for y in np.linspace(b, t, height)])[:, 1]
11831220

11841221
if self._interpolation == "nearest":
1185-
x_mid = (self._Ax[:-1] + self._Ax[1:]) / 2
1186-
y_mid = (self._Ay[:-1] + self._Ay[1:]) / 2
1222+
x_mid = (Ax[:-1] + Ax[1:]) / 2
1223+
y_mid = (Ay[:-1] + Ay[1:]) / 2
11871224
x_int = x_mid.searchsorted(x_pix)
11881225
y_int = y_mid.searchsorted(y_pix)
11891226
# The following is equal to `A[y_int[:, None], x_int[None, :]]`,
@@ -1196,16 +1233,16 @@ def make_image(self, renderer, magnification=1.0, unsampled=False):
11961233
else: # self._interpolation == "bilinear"
11971234
# Use np.interp to compute x_int/x_float has similar speed.
11981235
x_int = np.clip(
1199-
self._Ax.searchsorted(x_pix) - 1, 0, len(self._Ax) - 2)
1236+
Ax.searchsorted(x_pix) - 1, 0, len(Ax) - 2)
12001237
y_int = np.clip(
1201-
self._Ay.searchsorted(y_pix) - 1, 0, len(self._Ay) - 2)
1238+
Ay.searchsorted(y_pix) - 1, 0, len(Ay) - 2)
12021239
idx_int = np.add.outer(y_int * A.shape[1], x_int)
12031240
x_frac = np.clip(
1204-
np.divide(x_pix - self._Ax[x_int], np.diff(self._Ax)[x_int],
1241+
np.divide(x_pix - Ax[x_int], np.diff(Ax)[x_int],
12051242
dtype=np.float32), # Downcasting helps with speed.
12061243
0, 1)
12071244
y_frac = np.clip(
1208-
np.divide(y_pix - self._Ay[y_int], np.diff(self._Ay)[y_int],
1245+
np.divide(y_pix - Ay[y_int], np.diff(Ay)[y_int],
12091246
dtype=np.float32),
12101247
0, 1)
12111248
f00 = np.outer(1 - y_frac, 1 - x_frac)
@@ -1237,14 +1274,16 @@ def set_data(self, x, y, A):
12371274
(M, N) `~numpy.ndarray` or masked array of values to be
12381275
colormapped, or (M, N, 3) RGB array, or (M, N, 4) RGBA array.
12391276
"""
1277+
if not isinstance(self._container, NonUniformImageContainer):
1278+
raise TypeError("Cannot use 'set_data' on custom container types")
12401279
A = self._normalize_image_array(A)
12411280
x = np.array(x, np.float32)
12421281
y = np.array(y, np.float32)
12431282
if not (x.ndim == y.ndim == 1 and A.shape[:2] == y.shape + x.shape):
12441283
raise TypeError("Axes don't match array shape")
1245-
self._A = A
1246-
self._Ax = x
1247-
self._Ay = y
1284+
self._container.image = A
1285+
self._container.x = x
1286+
self._container.y = y
12481287
self._imcache = None
12491288
self.stale = True
12501289

@@ -1263,36 +1302,36 @@ def set_interpolation(self, s):
12631302
'bilinear interpolations are supported')
12641303
super().set_interpolation(s)
12651304

1266-
def get_extent(self):
1267-
if self._A is None:
1268-
raise RuntimeError('Must set data first')
1269-
return self._Ax[0], self._Ax[-1], self._Ay[0], self._Ay[-1]
1270-
12711305
def set_filternorm(self, filternorm):
12721306
pass
12731307

12741308
def set_filterrad(self, filterrad):
12751309
pass
12761310

12771311
def set_norm(self, norm):
1278-
if self._A is not None:
1279-
raise RuntimeError('Cannot change colors after loading data')
1312+
#if self._A is not None:
1313+
# raise RuntimeError('Cannot change colors after loading data')
12801314
super().set_norm(norm)
12811315

12821316
def set_cmap(self, cmap):
1283-
if self._A is not None:
1284-
raise RuntimeError('Cannot change colors after loading data')
1317+
#if self._A is not None:
1318+
# raise RuntimeError('Cannot change colors after loading data')
12851319
super().set_cmap(cmap)
12861320

12871321
def get_cursor_data(self, event):
12881322
# docstring inherited
1323+
q, _ = self._container.query(self._get_graph())
1324+
Ax = q["x"]
1325+
Ay = q["y"]
1326+
A = q["image"]
1327+
12891328
x, y = event.xdata, event.ydata
1290-
if (x < self._Ax[0] or x > self._Ax[-1] or
1291-
y < self._Ay[0] or y > self._Ay[-1]):
1329+
if (x < Ax[0] or x > Ax[-1] or
1330+
y < Ay[0] or y > Ay[-1]):
12921331
return None
1293-
j = np.searchsorted(self._Ax, x) - 1
1294-
i = np.searchsorted(self._Ay, y) - 1
1295-
return self._A[i, j]
1332+
j = np.searchsorted(Ax, x) - 1
1333+
i = np.searchsorted(Ay, y) - 1
1334+
return A[i, j]
12961335

12971336

12981337
class PcolorImage(AxesImage):
@@ -1339,18 +1378,27 @@ def __init__(self, ax,
13391378
"""
13401379
super().__init__(ax, norm=norm, cmap=cmap, colorizer=colorizer)
13411380
self._internal_update(kwargs)
1381+
self._container = PcolorImageContainer(
1382+
np.array([0.,1.]),
1383+
np.array([0.,1.]),
1384+
np.array([[np.nan]]),
1385+
)
13421386
if A is not None:
13431387
self.set_data(x, y, A)
13441388

13451389
def make_image(self, renderer, magnification=1.0, unsampled=False):
13461390
# docstring inherited
1347-
if self._A is None:
1348-
raise RuntimeError('You must first set the image array')
13491391
if unsampled:
1350-
raise ValueError('unsampled not supported on PColorImage')
1392+
raise ValueError('unsampled not supported on PcolorImage')
1393+
1394+
q, _ = self._container.query(self._get_graph())
1395+
Ax = q["x"]
1396+
Ay = q["y"]
1397+
1398+
A = q["image"]
13511399

13521400
if self._imcache is None:
1353-
A = self.to_rgba(self._A, bytes=True)
1401+
A = self.to_rgba(A, bytes=True)
13541402
self._imcache = np.pad(A, [(1, 1), (1, 1), (0, 0)], "constant")
13551403
padded_A = self._imcache
13561404
bg = mcolors.to_rgba(self.axes.patch.get_facecolor(), 0)
@@ -1367,8 +1415,8 @@ def make_image(self, renderer, magnification=1.0, unsampled=False):
13671415

13681416
x_pix = np.linspace(vl.x0, vl.x1, width)
13691417
y_pix = np.linspace(vl.y0, vl.y1, height)
1370-
x_int = self._Ax.searchsorted(x_pix)
1371-
y_int = self._Ay.searchsorted(y_pix)
1418+
x_int = Ax.searchsorted(x_pix)
1419+
y_int = Ay.searchsorted(y_pix)
13721420
im = ( # See comment in NonUniformImage.make_image re: performance.
13731421
padded_A.view(np.uint32).ravel()[
13741422
np.add.outer(y_int * padded_A.shape[1], x_int)]
@@ -1396,6 +1444,8 @@ def set_data(self, x, y, A):
13961444
- (M, N, 3): RGB array
13971445
- (M, N, 4): RGBA array
13981446
"""
1447+
if not isinstance(self._container, PcolorImageContainer):
1448+
raise TypeError("Cannot use 'set_data' on custom container types")
13991449
A = self._normalize_image_array(A)
14001450
x = np.arange(0., A.shape[1] + 1) if x is None else np.array(x, float).ravel()
14011451
y = np.arange(0., A.shape[0] + 1) if y is None else np.array(y, float).ravel()
@@ -1410,9 +1460,9 @@ def set_data(self, x, y, A):
14101460
if y[-1] < y[0]:
14111461
y = y[::-1]
14121462
A = A[::-1]
1413-
self._A = A
1414-
self._Ax = x
1415-
self._Ay = y
1463+
self._container.image = A
1464+
self._container.x = x
1465+
self._container.y = y
14161466
self._imcache = None
14171467
self.stale = True
14181468

@@ -1421,13 +1471,18 @@ def set_array(self, *args):
14211471

14221472
def get_cursor_data(self, event):
14231473
# docstring inherited
1474+
q, _ = self._container.query(self._get_graph())
1475+
Ax = q["x"]
1476+
Ay = q["y"]
1477+
A = q["image"]
1478+
14241479
x, y = event.xdata, event.ydata
1425-
if (x < self._Ax[0] or x > self._Ax[-1] or
1426-
y < self._Ay[0] or y > self._Ay[-1]):
1480+
if (x < Ax[0] or x > Ax[-1] or
1481+
y < Ay[0] or y > Ay[-1]):
14271482
return None
1428-
j = np.searchsorted(self._Ax, x) - 1
1429-
i = np.searchsorted(self._Ay, y) - 1
1430-
return self._A[i, j]
1483+
j = np.searchsorted(Ax, x) - 1
1484+
i = np.searchsorted(Ay, y) - 1
1485+
return A[i, j]
14311486

14321487

14331488
class FigureImage(_ImageBase):

0 commit comments

Comments
 (0)