code: def generate_kernel(
      gtab, sphere, wm_response, gm_response, csf_response
  ):
      """
      Generate deconvolution kernel
  
      Compute kernel mapping orientation densities of white matter fiber
      populations (along each vertex of the sphere) and isotropic volume
      fractions to a diffusion weighted signal.
  
      Parameters
      ----------
      gtab : GradientTable
      sphere : Sphere
          Sphere with which to sample discrete fiber orientations in order to
          construct kernel
      wm_response : 1d ndarray or 2d ndarray or AxSymShResponse, optional
          Tensor eigenvalues as a (3,) ndarray, multishell eigenvalues as
          a (len(unique_bvals_tolerance(gtab.bvals))-1, 3) ndarray in
          order of smallest to largest b-value, or an AxSymShResponse.
      gm_response : float, optional
          Mean diffusivity for GM compartment. If `None`, then grey
          matter compartment set to all zeros.
      csf_response : float, optional
          Mean diffusivity for CSF compartment. If `None`, then CSF
          compartment set to all zeros.
  
      Returns
      -------
      kernel : 2d ndarray (N, M)
          Computed kernel; can be multiplied with a vector consisting of volume
          fractions for each of M-2 fiber populations as well as GM and CSF
          fractions to produce a diffusion weighted signal.
      """
  
      # Coordinates of sphere vertices
      sticks = sphere.vertices
  
      n_grad = len(gtab.gradients)  # number of gradient directions
      n_wm_comp = sticks.shape[0]  # number of fiber populations
      n_comp = n_wm_comp + 2  # plus isotropic compartments
  
      kernel = np.zeros((n_grad, n_comp))
  
      # White matter compartments
      list_bvals = unique_bvals_tolerance(gtab.bvals)
      n_bvals = len(list_bvals) - 1  # number of unique b-values
  
      if isinstance(wm_response, AxSymShResponse):
          # Data-driven response
          where_dwi = lazy_index(~gtab.b0s_mask)
          gradients = gtab.gradients[where_dwi]
          gradients = (
              gradients / np.linalg.norm(gradients, axis=1)[..., None]
          )
          S0 = wm_response.S0
          for i in range(n_wm_comp):
              # Response oriented along [0, 0, 1], so must rotate sticks[i]
              rot_mat = vec2vec_rotmat(sticks[i], np.array([0, 0, 1]))
              rot_gradients = np.dot(rot_mat, gradients.T).T
              rot_sphere = Sphere(xyz=rot_gradients)
              # Project onto rotated sphere and scale
              rot_response = wm_response.on_sphere(rot_sphere) / S0
              kernel[where_dwi, i] = rot_response
  
          # Set b0 components
          kernel[gtab.b0s_mask, :] = 1
  
      elif wm_response.shape == (n_bvals, 3):
          # Multi-shell response
          bvals = gtab.bvals
          bvecs = gtab.bvecs
          for n, bval in enumerate(list_bvals[1:]):
              indices = get_bval_indices(bvals, bval)
              with warnings.catch_warnings():  # extract relevant b-value
                  warnings.simplefilter("ignore")
                  gtab_sub = gradient_table(
                      bvals[indices], bvecs[indices]
                  )
  
              for i in range(n_wm_comp):
                  # Signal generated by WM-fiber for each gradient direction
                  S = single_tensor(
                      gtab_sub,
                      evals=wm_response[n],
                      evecs=all_tensor_evecs(sticks[i]),
                  )
                  kernel[indices, i] = S
  
          # Set b0 components
          b0_indices = get_bval_indices(bvals, list_bvals[0])
          kernel[b0_indices, :] = 1
  
      else:
          # Single-shell response
          for i in range(n_wm_comp):
              # Signal generated by WM-fiber for each gradient direction
              S = single_tensor(
                  gtab,
                  evals=wm_response,
                  evecs=all_tensor_evecs(sticks[i]),
              )
              kernel[:, i] = S
  
          # Set b0 components
          kernel[gtab.b0s_mask, :] = 1
  
      # GM compartment
      if gm_response is None:
          S_gm = np.zeros(n_grad)
      else:
          S_gm = single_tensor(
              gtab,
              evals=np.array([gm_response, gm_response, gm_response]),
          )
  
      if csf_response is None:
          S_csf = np.zeros(n_grad)
      else:
          S_csf = single_tensor(
              gtab,
              evals=np.array(
                  [csf_response, csf_response, csf_response]
              ),
          )
  
      kernel[:, n_comp - 2] = S_gm
      kernel[:, n_comp - 1] = S_csf
  
      return kernel
  
imports: import logging
  import warnings
  import numpy
  from dipy.core.geometry import vec2vec_rotmat
  from dipy.core.gradients import gradient_table, unique_bvals_tolerance, get_bval_indices
  from dipy.core.onetime import auto_attr
  from dipy.core.sphere import Sphere
  from dipy.data import get_sphere
  from dipy.reconst.shm import lazy_index, normalize_data
  from dipy.reconst.odf import OdfModel, OdfFit
  from dipy.reconst.csdeconv import AxSymShResponse
  from dipy.segment.mask import bounding_box, crop
  from dipy.sims.voxel import single_tensor, all_tensor_evecs
label_desc: Use image node_id 3 for calling a global function with example usage: generate_kernel(gtab, sphere, wm_response, gm_response, csf_response) and returns: kernel