class_docstr: Defines the ExplainerManager for explaining an image-based model.
class_docstr_tok: ["Defines","the","ExplainerManager","for","explaining","an","image-based","model","."]
docstr: Plots SHAP values for image inputs.
  
  :param explanation: Computed explanation
  :type explanation: numpy.array
  :param index: Index value
  :type index: int
  :return: The computed explanation
  :rtype: base64 string
docstr_tok: ["Plots","SHAP","values","for","image","inputs",".",":","param","explanation",":","Computed","explanation",":","type","explanation",":","numpy.array",":","param","index",":","Index","value",":","type","index",":","int",":","return",":","The","computed","explanation",":","rtype",":","base64","string"]
code: def image(self, explanation, index):
      """Plots SHAP values for image inputs.
  
      :param explanation: Computed explanation
      :type explanation: numpy.array
      :param index: Index value
      :type index: int
      :return: The computed explanation
      :rtype: base64 string
      """
      width = 20
      aspect = 0.2
      hspace = 0.2
      labelpad = None
  
      shap_exp = explanation[index]
      shap_values = shap_exp.values
      shape_len = range(shap_values.shape[-1])
      if len(shap_exp.output_dims) == 1:
          shap_values = [shap_values[..., i] for i in shape_len]
      elif len(shap_exp.output_dims) == 0:
          shap_values = shap_exp.values
      else:
          raise Exception(
              "Number of outputs needs to have support added"
          )
  
      pixel_values = shap_exp.data
      labels = shap_exp.output_names
  
      multi_output = True
      if not isinstance(shap_values, list):
          multi_output = False
          shap_values = [shap_values]
  
      if len(shap_values[0].shape) == 3:
          shap_values = [v.reshape(1, *v.shape) for v in shap_values]
          pixel_values = pixel_values.reshape(1, *pixel_values.shape)
  
      # make sure labels
      if labels is not None:
          labels = np.array(labels)
          if labels.shape[0] != shap_values[0].shape[
              0
          ] and labels.shape[0] == len(shap_values):
              labels = np.tile(
                  np.array([labels]), shap_values[0].shape[0]
              )
          assert (
              labels.shape[0] == shap_values[0].shape[0]
          ), "Labels must have same row count as shap_values arrays"
          if multi_output:
              assert labels.shape[1] == len(
                  shap_values
              ), "Labels must have a column for each output in shap_values"
          else:
              assert (
                  len(labels.shape) == 1
              ), "Labels must be a vector for single output shap_values"
  
      label_kwargs = {} if labelpad is None else {"pad": labelpad}
  
      # plot our explanations
      x = pixel_values
      fig_size = np.array(
          [3 * (len(shap_values) + 1), 2.5 * (x.shape[0] + 1)]
      )
      if fig_size[0] > width:
          fig_size *= width / fig_size[0]
      fig, axes = pl.subplots(
          nrows=x.shape[0], ncols=len(shap_values) + 1, figsize=fig_size
      )
      if len(axes.shape) == 1:
          axes = axes.reshape(1, axes.size)
      for row in range(x.shape[0]):
          x_curr = x[row].copy()
  
          # make sure we have a 2D array for grayscale
          if len(x_curr.shape) == 3 and x_curr.shape[2] == 1:
              x_curr = x_curr.reshape(x_curr.shape[:2])
          if x_curr.max() > 1:
              try:
                  x_curr /= 255.0
              except Exception:
                  # In-place divide can fail for certain types
                  x_curr = x_curr / 255.0
  
          # get a grayscale version of the image
          if len(x_curr.shape) == 3 and x_curr.shape[2] == 3:
              x_curr_gray = (
                  0.2989 * x_curr[:, :, 0]
                  + 0.5870 * x_curr[:, :, 1]
                  + 0.1140 * x_curr[:, :, 2]
              )  # rgb to gray
              x_curr_disp = x_curr
          elif len(x_curr.shape) == 3:
              x_curr_gray = x_curr.mean(2)
  
              # for non-RGB multi-channel data
              flat_vals = x_curr.reshape(
                  [x_curr.shape[0] * x_curr.shape[1], x_curr.shape[2]]
              ).T
              flat_vals = (flat_vals.T - flat_vals.mean(1)).T
              means = kmeans(flat_vals, 3, round_values=False)
              means = means.data.T.reshape(
                  [x_curr.shape[0], x_curr.shape[1], 3]
              )
              x_curr_disp = (
                  means - np.percentile(means, 0.5, (0, 1))
              ) / (
                  np.percentile(means, 99.5, (0, 1))
                  - np.percentile(means, 1, (0, 1))
              )
              x_curr_disp[x_curr_disp > 1] = 1
              x_curr_disp[x_curr_disp < 0] = 0
          else:
              x_curr_gray = x_curr
              x_curr_disp = x_curr
  
          axes[row, 0].imshow(x_curr_disp, cmap=pl.get_cmap("gray"))
          axes[row, 0].axis("off")
          s_vals = shap_values
          s_range = range(len(s_vals))
          if len(shap_values[0][row].shape) == 2:
              abs_vals = np.stack(
                  [np.abs(s_vals[i]) for i in s_range], 0
              )
          else:
              abs_vals = np.stack(
                  [np.abs(s_vals[i].sum(-1)) for i in s_range], 0
              )
          abs_vals = abs_vals.flatten()
          max_val = np.nanpercentile(abs_vals, 99.9)
          for i in s_range:
              if labels is not None:
                  axes[row, i + 1].set_title(
                      labels[row, i], **label_kwargs
                  )
              sv = (
                  s_vals[i][row]
                  if len(s_vals[i][row].shape) == 2
                  else s_vals[i][row].sum(-1)
              )
              axes[row, i + 1].imshow(
                  x_curr_gray,
                  cmap=pl.get_cmap("gray"),
                  alpha=0.15,
                  extent=(-1, sv.shape[1], sv.shape[0], -1),
              )
              im = axes[row, i + 1].imshow(
                  sv,
                  cmap=colors.red_transparent_blue,
                  vmin=-max_val,
                  vmax=max_val,
              )
              axes[row, i + 1].axis("off")
      if hspace == "auto":
          fig.tight_layout()
      else:
          fig.subplots_adjust(hspace=hspace)
      cb = fig.colorbar(
          im,
          ax=np.ravel(axes).tolist(),
          label="SHAP value",
          orientation="horizontal",
          aspect=fig_size[0] / aspect,
      )
      cb.outline.set_visible(False)
      s = io.BytesIO()
      pl.savefig(s, format="jpg")
      s.seek(0)
      b64 = base64.b64encode(s.read())
      b64 = b64.decode(CommonTags.IMAGE_DECODE_UTF_FORMAT)
      pl.clf()
      return b64
  
imports: import base64
  import io
  import json
  import pickle
  import traceback
  import warnings
  from pathlib import Path
  from typing import Any, List, Optional
  import cv2
  import matplotlib.pyplot
  import numpy
  import pandas
  import shap
  from ml_wrappers import wrap_model
  from ml_wrappers.common.constants import Device
  from ml_wrappers.model.image_model_wrapper import MLflowDRiseWrapper, PytorchDRiseWrapper
  from PIL import Image, ImageDraw, ImageFont
  from shap.plots import colors
  from shap.utils._legacy import kmeans
  from vision_explanation_methods.DRISE_runner import get_drise_saliency_map
  from responsibleai._interfaces import ModelExplanationData
  from responsibleai._internal.constants import ExplainerManagerKeys
  from responsibleai._internal.constants import ListProperties, ManagerNames, Metadata
  from responsibleai._tools.shared.state_directory_management import DirectoryManager
  from responsibleai.exceptions import UserConfigValidationException
  from responsibleai.managers.base_manager import BaseManager
  from responsibleai_vision.common.constants import CommonTags, ExplainabilityDefaults, ExplainabilityLiterals, MLFlowSchemaLiterals, ModelTask, XAIPredictionLiterals
  from responsibleai_vision.utils.image_reader import get_base64_string_from_path, get_image_from_path, is_automl_image_model
label_desc: Use image node_id 7 for calling the ExplainerManager obj's underlying member method code with example usage: obj.image(explanation, index) and returns: b64