""" Общие вспомогательные утилиты пакета analysis. """ from typing import List import numpy as np import pandas as pd from analysis.features.feature_indexing import FeatureMatrix def get_top_images_for_feature( features: FeatureMatrix, meta: pd.DataFrame, feature_id: int, top_n: int = 10, aggregation: str = 'mean_acts', ) -> List[int]: """ Возвращает индексы изображений, на которых признак feature_id активируется сильнее всего. Активации патчей внутри одного изображения агрегируются в одно скалярное значение, после чего изображения сортируются по убыванию. Параметры ---------- features : CSR activations with global id per column meta : DataFrame с колонкой 'image_idx' (один патч — одна строка) feature_id : global SAE feature id top_n : число возвращаемых изображений aggregation : 'mean_acts' | 'max' | 'sum' mean_acts — среднее по патчам с активацией > 0 max — максимальная активация среди патчей sum — сумма всех активаций Возвращает ---------- List[int] — image_idx в порядке убывания агрегированной активации """ assert aggregation in ('mean_acts', 'max', 'sum'), ( f"aggregation must be 'mean_acts', 'max' or 'sum', got {aggregation!r}" ) col = features.column_for(feature_id) feature_acts = np.asarray(features.codes[:, col].todense()).ravel() # (n_patches,) image_idx_arr = meta['image_idx'].values unique_images = np.unique(image_idx_arr) scores = np.empty(len(unique_images), dtype=np.float32) for i, img_idx in enumerate(unique_images): mask = image_idx_arr == img_idx vals = feature_acts[mask] if aggregation == 'mean_acts': active = vals[vals > 0] scores[i] = active.mean() if len(active) > 0 else 0.0 elif aggregation == 'max': scores[i] = vals.max() else: scores[i] = vals.sum() order = np.argsort(scores)[::-1] return unique_images[order[:top_n]].tolist() def get_top_images_for_feature_by_iou( features: FeatureMatrix, meta: pd.DataFrame, feature_id: int, top_n: int = 10, dataset: str | None = None, ) -> List[int]: """ Возвращает индексы изображений с наибольшим IoU между бинарной картой активаций признака и маской искажений для каждого изображения. Требует, чтобы в `meta` были колонки `image_idx` и либо `patch_mask_label`, либо `patch_is_distorted` (см. iou_utils._load_patch_mask_for_group). """ from analysis.metrics import iou_utils col = features.column_for(feature_id) feature_acts = np.asarray(features.codes[:, col].todense()).ravel() image_groups = meta.groupby('image_idx') scores = [] # list of (image_idx, iou) for image_idx, group_df in image_groups: sample_indices = group_df.index.to_numpy() vals = feature_acts[sample_indices] # binary activation map for this image (per-patch) act_binary = (vals > 0).astype(np.uint8) try: patch_masks = iou_utils._load_patch_mask_for_group( group_df, target_dist_type=None, dataset=(dataset or ''), ) except Exception: # If no patch masks available, IoU is undefined — treat as 0 scores.append((int(image_idx), 0.0)) continue if patch_masks.shape[0] != act_binary.shape[0]: # mismatch: skip or treat as 0 scores.append((int(image_idx), 0.0)) continue try: iou = float(iou_utils._compute_patch_iou(act_binary, patch_masks)) except Exception: iou = 0.0 scores.append((int(image_idx), iou)) if not scores: return [] scores.sort(key=lambda x: x[1], reverse=True) return [img for img, _ in scores[:top_n]]