Source code for vision_unlearning.benchmarks.I_care.utils

"""Utility helpers for the I-CARE benchmark.

- Image encoding/decoding (base64 PNG)
- SHAP Explanation serialization (requires shap package — optional dep)
- Error classes used across result templates

Note: shap is imported lazily inside functions to avoid forcing all users
to install it. It is only needed for the SHAP-related result templates.
"""

from __future__ import annotations

import base64
import io
import json
import os
from typing import Any, Dict, List, Optional

import numpy as np
from PIL import Image


[docs] def _encode_image_file(img_path: str, max_dim: int = 1024) -> str: ''' Downsample / reduce resolution to limit size before encoding ''' assert os.path.exists(img_path), f"Image file not found at {img_path}" with Image.open(img_path) as im: # Convert to RGB to ensure compatibility with JPEG if im.mode != 'RGB': im = im.convert('RGB') if max(im.size) > max_dim: scale = max_dim / max(im.size) new_size = (int(im.size[0] * scale), int(im.size[1] * scale)) im = im.resize(new_size, Image.LANCZOS) #print(f"Resized image from {im.size} to {new_size} to limit size before encoding.") buf = io.BytesIO() im.save(buf, format='PNG', quality=85, optimize=True) image_bytes = buf.getvalue() return base64.b64encode(image_bytes).decode('ascii')
[docs] def _decode_image(image_data: str) -> io.BytesIO: assert isinstance(image_data, str), f"Expected image data to be a base64 string, but got {type(image_data)}" return io.BytesIO(base64.b64decode(image_data))
[docs] def explanation_to_dict(expl: Any) -> Dict[str, Any]: """Serialize a shap.Explanation to a plain dict (JSON-serializable).""" return { "values": expl.values.tolist() if expl.values is not None else None, "base_values": ( expl.base_values.tolist() if isinstance(expl.base_values, np.ndarray) else expl.base_values ), "data": expl.data.tolist() if expl.data is not None else None, "feature_names": list(expl.feature_names) if expl.feature_names is not None else None, "output_names": list(expl.output_names) if expl.output_names is not None else None, }
[docs] def dict_to_explanation(d: Dict[str, Any]) -> Any: """Deserialize a plain dict back to a shap.Explanation. Requires 'shap' package (optional dependency — install with pip install shap or pip install vision-unlearning[testbed]). """ import shap # lazy import — only needed for SHAP result templates return shap.Explanation( values=np.array(d["values"]) if d["values"] is not None else None, base_values=np.array(d["base_values"]) if d["base_values"] is not None else None, data=np.array(d["data"]) if d["data"] is not None else None, feature_names=d["feature_names"], output_names=d["output_names"], )
[docs] class InvalidAttributeTypeError(ValueError): pass
[docs] class InsufficientSamplesError(ValueError): pass