Source code for fluidimage.data_objects.display_piv

"""Display a PIV field (:mod:`fluidimage.data_objects.display_piv`)
===================================================================

.. autoclass:: DisplayPIV
   :members:
   :private-members:

"""

import matplotlib.pyplot as plt
import numpy as np

from ..calcul.correl import compute_indices_from_displacement

legend_text = """

- Click on a vector to show information.
- Press alt+s to switch between images.
- Press alt+left or alt+right to change vector.

# Legend correlation

- "or": position corresponding to no displacement,
- "xr": displacement found from correlation,
- "ow": displacement final (after interpolation),
- "+r": other peaks,
- "vr": bad peak removed.

"""


[docs]class DisplayPIV: """Display a piv result object.""" def __init__( self, im0, im1, piv_results=None, show_interp=False, scale=0.2, show_error=True, pourcent_histo=99, hist=False, show_correl=True, xlim=None, ylim=None, ): self.piv_results = piv_results if ( show_correl and hasattr(piv_results, "correls") and piv_results.correls ): self.show_correl = True else: self.show_correl = False if self.show_correl: fig, (ax0, ax1) = plt.subplots(ncols=2) self.ax1 = ax1 else: fig, ax0 = plt.subplots() self.fig = fig self.ax0 = ax0 fig.event_handler = self if im0 is not None: p0 = np.percentile( np.reshape(im0, (1, np.prod(im0.shape))).transpose(), pourcent_histo, ) p1 = np.percentile( np.reshape(im1, (1, np.prod(im1.shape))).transpose(), pourcent_histo, ) im0 = im0.copy() im1 = im1.copy() im0[im0 > p0] = p0 im1[im1 > p1] = p1 self.image0 = ax0.imshow( im0, interpolation="nearest", cmap=plt.cm.gray, origin="upper", extent=[0, im0.shape[1], im0.shape[0], 0], vmin=0, vmax=0.99 * im0.max(), ) self.image1 = ax0.imshow( im1, interpolation="nearest", cmap=plt.cm.gray, origin="upper", extent=[0, im0.shape[1], im0.shape[0], 0], vmin=0, vmax=0.99 * im1.max(), ) self.image1.set_visible(False) else: self.image0 = None (point,) = ax0.plot(0, 0, "oy") point.set_visible(False) ax0.set_title("im 0 (alt+s to switch)") self._text = fig.text(0.1, 0.05, "") self._point = point if im0 is not None: if xlim is None: xlim = (0, im0.shape[1]) if ylim is None: ylim = (im0.shape[0], 0) if xlim is not None: ax0.set_xlim(xlim) if ylim is not None: ax0.set_ylim(ylim) ax0.set_xlabel("pixels") ax0.set_ylabel("pixels") if piv_results is not None: if show_interp: if hasattr(piv_results, "deltaxs_approx"): deltaxs = piv_results.deltaxs_approx deltays = piv_results.deltays_approx xs = piv_results.ixvecs_approx ys = piv_results.iyvecs_approx else: deltaxs = piv_results.deltaxs_final deltays = piv_results.deltays_final xs = piv_results.ixvecs_final ys = piv_results.iyvecs_final else: deltaxs = piv_results.deltaxs deltays = piv_results.deltays xs = piv_results.xs ys = piv_results.ys if im0 is None: deltays *= -1 self.q = ax0.quiver( xs, ys, deltaxs, -deltays, width=0.004, picker=10, color="c", scale_units="xy", scale=scale, ) if show_error and not hasattr(piv_results, "deltays_wrong"): show_error = False if show_error: self.inds_error = inds_error = np.array( list(piv_results.deltays_wrong.keys()), dtype=int ) xs_wrong = xs[inds_error] ys_wrong = ys[inds_error] dxs_wrong = np.array( [piv_results.deltaxs_wrong[i] for i in inds_error] ) dys_wrong = np.array( [piv_results.deltays_wrong[i] for i in inds_error] ) if im0 is None: dys_wrong *= -1 self.q_wrong = ax0.quiver( xs_wrong, ys_wrong, dxs_wrong, -dys_wrong, picker=10, color="r", scale_units="xy", scale=scale, ) inds_isnan = inds_error[np.isnan(dxs_wrong)] self.inds_isnan = inds_isnan xs_isnan = xs[inds_isnan] ys_isnan = ys[inds_isnan] zeros = np.zeros_like(xs_isnan) self.q_isnan = ax0.quiver( xs_isnan, ys_isnan, zeros, zeros, minshaft=4, picker=10, color="r", scale_units="xy", scale=scale, ) if hist: fig2, axes = plt.subplots(ncols=2) ax3, ax4 = axes.ravel() ind = ( np.isnan(deltaxs) + np.isnan(deltays) + np.isinf(deltaxs) + np.isinf(deltays) ) deltaxs2 = deltaxs[~ind] deltays2 = deltays[~ind] ax3.hist(deltaxs2, "fd", color="b", label=r"$\Delta x_s$") ax3.hist(deltays2, "fd", color="r", label=r"$\Delta y_s$") ax3.set_xlabel("displacement x (blue) and y (red) (pixels)") ax3.set_ylabel("histogram") ax3.legend() ax4.hist(piv_results.correls_max, "fd", color="g") ax4.set_xlabel("Maximum pixel correlation") ax4.set_ylabel("histogram") plt.show() self.ind = 0 fig.canvas.mpl_connect("pick_event", self.onpick) fig.canvas.mpl_connect("key_press_event", self.onclick) print("press alt+h for help and legend") plt.show() def onclick(self, event): if event.key == "alt+h": print(legend_text) if event.inaxes != self.ax0: return if event.key == "alt+s": self.switch() if event.key == "alt+left": self.select_arrow(self.ind - 1) if event.key == "alt+right": self.select_arrow(self.ind + 1) def onpick(self, event): if not ( event.artist == self.q or event.artist == self.q_wrong or event.artist == self.q_isnan ): return True # the click locations # x = event.mouseevent.xdata # y = event.mouseevent.ydata ind = event.ind self.select_arrow(ind, event.artist) def select_arrow(self, ind, artist=None): try: ind = ind[0] except (TypeError, IndexError): return if artist is None: print("artist is None") return # if ind in self.piv_results.errors.keys(): # artist = self.q_wrong # ind = self.inds_error.index(ind) # else: # artist = self.q if artist == self.q: ind_all = ind q = self.q elif artist == self.q_wrong: ind_all = self.inds_error[ind] q = self.q_wrong elif artist == self.q_isnan: ind_all = self.inds_isnan[ind] q = self.q_isnan else: raise NotImplementedError("other artist" + str(artist)) if ind >= len(q.X) or ind < 0 or self.ind == ind_all: return self.ind = ind_all result = self.piv_results ix = q.X[ind] iy = q.Y[ind] deltax = result.deltaxs[ind_all] deltay = result.deltays[ind_all] if np.isnan(deltax): deltax = result.deltaxs_wrong[ind_all] deltay = result.deltays_wrong[ind_all] self._point.set_visible(True) self._point.set_data(ix, iy) text = ( f"vector {ind_all} at ix = {ix} : iy = {iy}" f" ; U = {deltax:.3f} ; V = {deltay:.3f}" ) if result.correls_max is not None: correl_max = result.correls_max[ind_all] text += f", C = {correl_max:.3f}" if ( self.piv_results.errors is not None and ind_all in self.piv_results.errors ): text += ", error: " + self.piv_results.errors[ind_all] self._text.set_text(text) if self.show_correl: ax2 = self.ax1 ax2.cla() alphac = result.correls[ind_all] alphac_max = alphac.max() correl = correl_max / alphac_max * alphac ax2.imshow(correl, origin="lower", interpolation="none", vmin=0) ax2.plot( result.indices_no_displacement[0], result.indices_no_displacement[1], "or", mfc="none", ) try: deltax -= result.deltaxs_input[ind_all] deltay -= result.deltays_input[ind_all] except AttributeError: pass i1, i0 = compute_indices_from_displacement( deltax, deltay, result.indices_no_displacement ) ax2.plot(i1, i0, "xr") if hasattr(result, "deltaxs_final"): deltax = result.deltaxs_final[ind_all] deltay = result.deltays_final[ind_all] try: deltax -= result.deltaxs_input[ind_all] deltay -= result.deltays_input[ind_all] except AttributeError: pass i1, i0 = compute_indices_from_displacement( deltax, deltay, result.indices_no_displacement ) ax2.plot(i1, i0, "ow") if hasattr(result, "replaced_vectors"): try: (dx_bad, dy_bad, _) = result.replaced_vectors[ind_all] except KeyError: pass else: try: dx_bad -= result.deltaxs_input[ind_all] dy_bad -= result.deltays_input[ind_all] except AttributeError: pass i1_bad, i0_bad = compute_indices_from_displacement( dx_bad, dy_bad, result.indices_no_displacement ) ax2.plot(i1_bad, i0_bad, "vr") params = self.piv_results.params if params.piv0.nb_peaks_to_search > 1: other_peaks = result.secondary_peaks[ind_all] if other_peaks is not None: s = text + "\n" if not other_peaks: s += " no other peak" elif len(other_peaks) == 1: s += " 1 other peak" else: s += f" {len(other_peaks)} other peaks" ax2.set_title(s) print(s) for dx, dy, cmax in other_peaks: i1, i0 = compute_indices_from_displacement( dx, dy, result.indices_no_displacement ) ax2.plot(i1, i0, "+r") print(f" {(dx, dy, cmax) = }") if params.piv0.displacement_max is not None: circle = plt.Circle( result.indices_no_displacement, result.displacement_max, color="b", fill=False, ) ax2.add_artist(circle) ax2.axis("scaled") ax2.set_xlim(-0.5, correl.shape[1] - 0.5) ax2.set_ylim(-0.5, correl.shape[0] - 0.5) self.fig.canvas.draw() def switch(self): if self.image0 is not None: self.image0.set_visible(not self.image0.get_visible()) self.image1.set_visible(not self.image1.get_visible()) self.ax0.set_title( "im {} (alt+s to switch)".format(int(self.image1.get_visible())) ) self.fig.canvas.draw()