code: def peaks_from_model(
      model,
      data,
      sphere,
      relative_peak_threshold,
      min_separation_angle,
      mask=None,
      return_odf=False,
      return_sh=True,
      gfa_thr=0,
      normalize_peaks=False,
      sh_order=8,
      sh_basis_type=None,
      npeaks=5,
      B=None,
      invB=None,
      parallel=False,
      num_processes=None,
  ):
      """Fit the model to data and computes peaks and metrics
  
      Parameters
      ----------
      model : a model instance
          `model` will be used to fit the data.
      data : ndarray
          Diffusion data.
      sphere : Sphere
          The Sphere providing discrete directions for evaluation.
      relative_peak_threshold : float
          Only return peaks greater than ``relative_peak_threshold * m`` where m
          is the largest peak.
      min_separation_angle : float in [0, 90] The minimum distance between
          directions. If two peaks are too close only the larger of the two is
          returned.
      mask : array, optional
          If `mask` is provided, voxels that are False in `mask` are skipped and
          no peaks are returned.
      return_odf : bool
          If True, the odfs are returned.
      return_sh : bool
          If True, the odf as spherical harmonics coefficients is returned
      gfa_thr : float
          Voxels with gfa less than `gfa_thr` are skipped, no peaks are returned.
      normalize_peaks : bool
          If true, all peak values are calculated relative to `max(odf)`.
      sh_order : int, optional
          Maximum SH order in the SH fit.  For `sh_order`, there will be
          ``(sh_order + 1) * (sh_order + 2) / 2`` SH coefficients (default 8).
      sh_basis_type : {None, 'tournier07', 'descoteaux07'}
          ``None`` for the default DIPY basis,
          ``tournier07`` for the Tournier 2007 [2]_ basis, and
          ``descoteaux07`` for the Descoteaux 2007 [1]_ basis
          (``None`` defaults to ``descoteaux07``).
      npeaks : int
          Maximum number of peaks found (default 5 peaks).
      B : ndarray, optional
          Matrix that transforms spherical harmonics to spherical function
          ``sf = np.dot(sh, B)``.
      invB : ndarray, optional
          Inverse of B.
      parallel: bool
          If True, use multiprocessing to compute peaks and metric
          (default False). Temporary files are saved in the default temporary
          directory of the system. It can be changed using ``import tempfile``
          and ``tempfile.tempdir = '/path/to/tempdir'``.
      num_processes: int, optional
          If `parallel` is True, the number of subprocesses to use
          (default multiprocessing.cpu_count()). If < 0 the maximal number of
          cores minus ``num_processes + 1`` is used (enter -1 to use as many
          cores as possible). 0 raises an error.
  
      Returns
      -------
      pam : PeaksAndMetrics
          An object with ``gfa``, ``peak_directions``, ``peak_values``,
          ``peak_indices``, ``odf``, ``shm_coeffs`` as attributes
  
      References
      ----------
      .. [1] Descoteaux, M., Angelino, E., Fitzgibbons, S. and Deriche, R.
             Regularized, Fast, and Robust Analytical Q-ball Imaging.
             Magn. Reson. Med. 2007;58:497-510.
      .. [2] Tournier J.D., Calamante F. and Connelly A. Robust determination
             of the fibre orientation distribution in diffusion MRI:
             Non-negativity constrained super-resolved spherical deconvolution.
             NeuroImage. 2007;35(4):1459-1472.
  
      """
      if return_sh and (B is None or invB is None):
          B, invB = sh_to_sf_matrix(
              sphere, sh_order, sh_basis_type, return_inv=True
          )
  
      num_processes = determine_num_processes(num_processes)
  
      if parallel and num_processes > 1:
          # It is mandatory to provide B and invB to the parallel function.
          # Otherwise, a call to np.linalg.pinv is made in a subprocess and
          # makes it timeout on some system.
          # see https://github.com/dipy/dipy/issues/253 for details
          return _peaks_from_model_parallel(
              model,
              data,
              sphere,
              relative_peak_threshold,
              min_separation_angle,
              mask,
              return_odf,
              return_sh,
              gfa_thr,
              normalize_peaks,
              sh_order,
              sh_basis_type,
              npeaks,
              B,
              invB,
              num_processes,
          )
  
      shape = data.shape[:-1]
      if mask is None:
          mask = np.ones(shape, dtype="bool")
      else:
          if mask.shape != shape:
              raise ValueError("Mask is not the same shape as data.")
  
      gfa_array = np.zeros(shape)
      qa_array = np.zeros((shape + (npeaks,)))
  
      peak_dirs = np.zeros((shape + (npeaks, 3)))
      peak_values = np.zeros((shape + (npeaks,)))
      peak_indices = np.zeros((shape + (npeaks,)), dtype=np.int32)
      peak_indices.fill(-1)
  
      if return_sh:
          n_shm_coeff = (sh_order + 2) * (sh_order + 1) // 2
          shm_coeff = np.zeros((shape + (n_shm_coeff,)))
  
      if return_odf:
          odf_array = np.zeros((shape + (len(sphere.vertices),)))
  
      global_max = -np.inf
      for idx in ndindex(shape):
          if not mask[idx]:
              continue
  
          odf = model.fit(data[idx]).odf(sphere)
  
          if return_sh:
              shm_coeff[idx] = np.dot(odf, invB)
  
          if return_odf:
              odf_array[idx] = odf
  
          gfa_array[idx] = gfa(odf)
          if gfa_array[idx] < gfa_thr:
              global_max = max(global_max, odf.max())
              continue
  
          # Get peaks of odf
          direction, pk, ind = peak_directions(
              odf, sphere, relative_peak_threshold, min_separation_angle
          )
  
          # Calculate peak metrics
          if pk.shape[0] != 0:
              global_max = max(global_max, pk[0])
  
              n = min(npeaks, pk.shape[0])
              qa_array[idx][:n] = pk[:n] - odf.min()
  
              peak_dirs[idx][:n] = direction[:n]
              peak_indices[idx][:n] = ind[:n]
              peak_values[idx][:n] = pk[:n]
  
              if normalize_peaks:
                  peak_values[idx][:n] /= pk[0]
                  peak_dirs[idx] *= peak_values[idx][:, None]
  
      qa_array /= global_max
  
      return _pam_from_attrs(
          PeaksAndMetrics,
          sphere,
          peak_indices,
          peak_values,
          peak_dirs,
          gfa_array,
          qa_array,
          shm_coeff if return_sh else None,
          B if return_sh else None,
          odf_array if return_odf else None,
      )
  
imports: import tempfile
  from multiprocessing import Pool
  from itertools import repeat
  from os import path
  import numpy
  import scipy.optimize
  from dipy.reconst.odf import gfa
  from dipy.reconst.recspeed import local_maxima, remove_similar_vertices, search_descending
  from dipy.core.sphere import Sphere
  from dipy.data import default_sphere
  from dipy.core.ndindex import ndindex
  from dipy.reconst.shm import sh_to_sf_matrix
  from dipy.reconst.eudx_direction_getter import EuDXDirectionGetter
  from dipy.utils.multiproc import determine_num_processes
  from dipy.utils.deprecator import deprecated_params
label_desc: Use image node_id 6 for calling a global function with example usage: peaks_from_model(model, data, sphere, relative_peak_threshold, min_separation_angle, mask, return_odf, return_sh, gfa_thr, normalize_peaks, sh_order, sh_basis_type, npeaks, B, invB, parallel, num_processes) and returns: _pam_from_attrs, _peaks_from_model_parallel