def iabel_basex_transform(Q0): # basex requires a whole image IM = put_image_quadrants((Q0, Q0, Q0, Q0), odd_size=True) print ("basex uses whole image reconstructed from Q0 shape ",IM.shape) rows, cols = IM.shape center = (rows//2+rows%2, cols//2+cols%2) AIM = BASEX (IM, center, n=rows, verbose=True) return get_image_quadrants(AIM)[0] # only return Q0
def iabel_basex_transform(Q0): # basex requires a whole image IM = put_image_quadrants((Q0, Q0, Q0, Q0), odd_size=True) print("basex uses whole image reconstructed from Q0 shape ", IM.shape) rows, cols = IM.shape center = (rows // 2 + rows % 2, cols // 2 + cols % 2) AIM = BASEX(IM, center, n=rows, verbose=True) return get_image_quadrants(AIM)[0] # only return Q0
def rbasex_transform(IM, origin='center', rmax='MIN', order=2, odd=False, weights=None, direction='inverse', reg=None, out='same', basis_dir=None, verbose=False): r""" This function takes the input image and outputs its forward or inverse Abel transform as an image and its radial distributions. The **origin**, **rmax**, **order**, **odd** and **weights** parameters are passed to :class:`abel.tools.vmi.Distributions`, so see its documentation for their detailed descriptions. Parameters ---------- IM : m × n numpy array the image to be transformed origin : tuple of int or str image origin, explicit in the (row, column) format, or as a location string (by default, the image center) rmax : int or string largest radius to include in the transform (by default, the largest radius with at least one full quadrant of data) order : int highest angular order present in the data, ≥ 0 (by default, 2) odd : bool include odd angular orders (by default is `False`, but is enabled automatically if **order** is odd) weights : m × n numpy array, optional weighting factors for each pixel. The array shape must match the image shape. Parts of the image can be excluded from analysis by assigning zero weights to their pixels. By default is `None`, which applies equal weight to all pixels. direction : str: ``'forward'`` or ``'inverse'`` type of Abel transform to be performed (by default, inverse) reg : None or str or tuple (str, float), optional regularization to use for inverse Abel transform. ``None`` (default) means no regularization, a string selects a non-parameterized regularization method, and parameterized methods are selected by a tuple (`method`, `strength`). Available methods are: ``('L2', strength)``: Tikhonov :math:`L_2` regularization with `strength` as the square of the Tikhonov factor. This is the same as “Tikhonov regularization” used in BASEX, with almost identical effects on the radial distributions. ``('diff', strength)``: Tikhonov regularization with the difference operator (approximation of the derivative) multiplied by the square root of `strength` as the Tikhonov matrix. This tends to produce less blurring, but more negative overshoots than ``'L2'``. ``('SVD', strength)``: truncated SVD (singular value decomposition) with N = `strength` × **rmax** largest singular values removed for each angular order. This mimics the approach proposed (but in fact not used) in pBasex. `Not recommended` due to generally poor results. ``'pos'``: non-parameterized method, finds the best (in the least-squares sense) solution with non-negative :math:`\cos^n\theta \sin^m\theta` terms (see :meth:`~abel.tools.vmi.Distributions.Results.cossin`). For **order** = 0, 1, and 2 (with **odd** = `False`) this is equivalent to :math:`I(r, \theta) \geqslant 0`; for higher orders this assumption is stronger than :math:`I \geqslant 0` and corresponds to no interference between different multiphoton channels. Not implemented for odd orders > 1. Notice that this method is nonlinear, which also means that it is considerably slower than the linear methods and the transform operator cannot be cached. In all cases, `strength` = 0 provides no regularization. For the Tikhonov methods, `strength` ~ 100 is a reasonable value for megapixel images. For truncated SVD, `strength` must be < 1; `strength` ~ 0.1 is a reasonable value; `strength` ~ 0.5 can produce noticeable ringing artifacts. See the :ref:`full description <rBasexmathreg>` and examples there. out : str or None shape of the output image: ``'same'`` (default): same shape and origin as the input ``'fold'`` (fastest): Q0 (upper right) quadrant (for ``odd=False``) or right half (for ``odd=True``) up to **rmax**, but limited to the largest input-image quadrant (or half) ``'unfold'``: like ``'fold'``, but symmetrically “unfolded” to all 4 quadrants ``'full'``: all pixels with radii up to **rmax** ``'full-unique'``: the unique part of ``'full'``: Q0 (upper right) quadrant for ``odd=False``, right half for ``odd=True`` ``None``: no image (**recon** will be ``None``). Can be useful to avoid unnecessary calculations when only the transformed radial distributions (**distr**) are needed. basis_dir : str, optional path to the directory for saving / loading the basis set (useful only for the inverse transform without regularization; time savings in other cases are small and might be negated by the disk-access overhead). If ``None`` (default), the basis set will not be loaded from or saved to disk. verbose : bool print information about processing (for debugging), disabled by default Returns ------- recon : 2D numpy array or None the transformed image. Is centered and might have different dimensions than the input image. distr : Distributions.Results object the object from which various distributions for the transformed image can be retrieved, see :class:`abel.tools.vmi.Distributions.Results` """ if order == 0: odd = False # (to eliminate additional checks) elif order % 2: odd = True # enable automatically for odd orders # extract radial profiles from input image p = _profiles(IM, origin, rmax, order, odd, weights, verbose) # (caches Distributions as _dst) Rmax = _dst.rmax # get appropriate transform matrices A = get_bs_cached(Rmax, order, odd, direction, reg, _dst.valid, basis_dir, verbose) # transform radial profiles if reg == 'pos': if verbose: print('Solving NNLS equations...') N = len(p) p = np.hstack(p) cs = nnls(A, p)[0] cs = np.split(cs, N) if odd: # (1 ± cos) / 2 → cos^0, cos^1 c = [cs[0] + cs[1], cs[0] - cs[1]] else: # cossin → cos transform C = np.flip(invpascal(N, 'upper')) c = C.dot(cs) else: if verbose: print('Applying radial transforms...') c = [An.dot(pn) for An, pn in zip(A, p)] # construct output (transformed) distributions distr = Distributions.Results(np.arange(Rmax + 1), np.array(c), order, odd, _dst.valid) if out is None: return None, distr # output size if out == 'same': height = _dst.shape[0] if odd else _dst.VER + 1 width = _dst.HOR + 1 row = _dst.row if odd else 0 elif out in ['fold', 'unfold']: height = _dst.Qheight width = _dst.Qwidth row = _dst.row if odd else 0 elif out in ['full', 'full-unique']: height = 2 * Rmax + 1 if odd else Rmax + 1 width = Rmax + 1 row = Rmax if odd else 0 else: raise ValueError('Wrong output shape "{}"'.format(out)) # construct output image from transformed radial profiles if verbose: print('Constructing output image...') # bottom right quadrant or right half recon = _image(height, width, row, c, verbose) if odd: if out not in ['fold', 'full-unique']: # combine with left half (mirrored without central column) recon = np.hstack((recon[:, :0:-1], recon)) else: # even only recon = recon[::-1] # flip to Q0 if out not in ['fold', 'full-unique']: # assemble full image recon = put_image_quadrants((recon, recon, recon, recon), (2 * height - 1, 2 * width - 1)) if out == 'same': # crop as needed row = 0 if odd else _dst.VER - _dst.row col = _dst.HOR - _dst.col H, W = IM.shape recon = recon[row:row + H, col:col + W] return recon, distr
plt.plot (radial, speed, label=method) # reassemble image, each quadrant a different method # for < 4 images pad using a blank quadrant blank = np.zeros(IAQ0.shape) for q in range(ntrans, 4): iabelQ.append(blank) # more than 4, split quadrant if ntrans == 5: # split last quadrant into 2 = upper and lower triangles tmp_img = np.tril(np.flipud(iabelQ[-2])) +\ np.triu(np.flipud(iabelQ[-1])) iabelQ[3] = np.flipud(tmp_img) # Fix me when > 5 images im = put_image_quadrants ((iabelQ[0],iabelQ[1],iabelQ[2],iabelQ[3]), odd_size=True) plt.subplot(121) plt.imshow(im,vmin=0,vmax=0.8) plt.subplot(122) plt.axis(ymin=-0.05,ymax=1.1,xmin=50,xmax=450) plt.legend(loc=0,labelspacing=0.1) plt.tight_layout() plt.savefig('example_all_O2.png',dpi=100) plt.show()
plt.subplot(122) plt.plot(radial, speed, label=method) # reassemble image, each quadrant a different method # for < 4 images pad using a blank quadrant blank = np.zeros(IAQ0.shape) for q in range(ntrans, 4): iabelQ.append(blank) # more than 4, split quadrant if ntrans == 5: # split last quadrant into 2 = upper and lower triangles tmp_img = np.tril(np.flipud(iabelQ[-2])) +\ np.triu(np.flipud(iabelQ[-1])) iabelQ[3] = np.flipud(tmp_img) # Fix me when > 5 images im = put_image_quadrants((iabelQ[0], iabelQ[1], iabelQ[2], iabelQ[3]), odd_size=True) plt.subplot(121) plt.imshow(im, vmin=0, vmax=0.8) plt.subplot(122) plt.axis(ymin=-0.05, ymax=1.1, xmin=50, xmax=450) plt.legend(loc=0, labelspacing=0.1) plt.tight_layout() plt.savefig('example_all_O2.png', dpi=100) plt.show()