Skip to content

Commit 279f58e

Browse files
3 -> 2 arguments
1 parent 6fe289c commit 279f58e

File tree

3 files changed

+94
-95
lines changed

3 files changed

+94
-95
lines changed

docs/source/user-guide.md

+44-37
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ explanation = explainer(
137137
)
138138

139139
# Save saliency maps
140-
explanation.save("output_path", "name")
140+
explanation.save("output_path", "name_")
141141
```
142142

143143
### Specifying `preprocess_fn`
@@ -184,7 +184,7 @@ explanation = explainer(
184184
)
185185

186186
# Save saliency maps
187-
explanation.save("output_path", "name")
187+
explanation.save("output_path", "name_")
188188
```
189189

190190

@@ -242,7 +242,7 @@ explanation = explainer(
242242
)
243243

244244
# Save saliency maps
245-
explanation.save("output_path", "name")
245+
explanation.save("output_path", "name_")
246246
```
247247

248248

@@ -298,7 +298,7 @@ explanation = explainer(
298298
)
299299

300300
# Save saliency maps
301-
explanation.save("output_path", "name")
301+
explanation.save("output_path", "name_")
302302

303303
```
304304

@@ -333,16 +333,16 @@ model_xai = xai.insert_xai(
333333

334334
## Saving saliency maps
335335

336-
You can easily save saliency maps with flexible naming options, including image name as prefix, so saliency maps from the same image will start the same, target prefix, terget suffix.
336+
You can easily save saliency maps with flexible naming options by using a `prefix` and `postfix`. The `prefix` allows saliency maps from the same image to have consistent naming.
337337

338-
For the name `image_name_target_aeroplane.jpg`:
339-
- image_name_prefix = `image_name`,
340-
- target_prefix = `target`,
341-
- label name = `aeroplane`,
342-
- target_suffix = ``.
338+
The format for naming is:
339+
340+
`{prefix} + target_id + {postfix}.jpg`
343341

344342
Additionally, you can include the confidence score for each class in the saved saliency map's name.
345343

344+
`{prefix} + target_id + {postfix} + confidence.jpg`
345+
346346
```python
347347
import cv2
348348
import numpy as np
@@ -352,13 +352,20 @@ import openvino_xai as xai
352352

353353
def preprocess_fn(image: np.ndarray) -> np.ndarray:
354354
"""Preprocess the input image."""
355-
resized_image = cv2.resize(src=image, dsize=(224, 224))
356-
expanded_image = np.expand_dims(resized_image, 0)
357-
return expanded_image
355+
x = cv2.resize(src=image, dsize=(224, 224))
356+
x = x.transpose((2, 0, 1))
357+
processed_image = np.expand_dims(x, 0)
358+
return processed_image
358359

359360
def postprocess_fn(output: Mapping):
360361
"""Postprocess the model output."""
361-
return output["logits"]
362+
output = softmax(output)
363+
return output[0]
364+
365+
def softmax(x: np.ndarray) -> np.ndarray:
366+
"""Compute softmax values of x."""
367+
e_x = np.exp(x - np.max(x))
368+
return e_x / e_x.sum()
362369

363370
# Generate and process saliency maps (as many as required, sequentially)
364371
image = cv2.imread("path/to/image.jpg")
@@ -367,17 +374,7 @@ image = cv2.imread("path/to/image.jpg")
367374
MODEL_PATH = "path/to/model.xml"
368375
model = ov.Core().read_model(MODEL_PATH) # type: ov.Model
369376

370-
# Get predicted confidences for the image
371-
compiled_model = core.compile_model(model=model, device_name="AUTO")
372-
logits = compiled_model([preprocess_fn(image)])[0]
373-
postprocessed_logits = postprocess_fn(logits)[0]
374-
result_index = np.argmax(postprocessed_logits)
375-
result_scores = postprocessed_logits[result_index]
376-
377-
# Generate dict {class_index: confidence} to save saliency maps
378-
scores_dict = {i: score for i, score in enumerate(result_scores)}
379-
380-
# The Explainer object will prepare and load the model once in the beginning
377+
# Initialize Explainer
381378
explainer = xai.Explainer(
382379
model,
383380
task=xai.Task.CLASSIFICATION,
@@ -389,30 +386,40 @@ voc_labels = [
389386
'dog', 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor'
390387
]
391388

389+
# Get predicted confidences for the image
390+
compiled_model = core.compile_model(model=model, device_name="AUTO")
391+
logits = compiled_model(preprocess_fn(image))[0]
392+
result_infer = postprocess_fn(logits)
393+
394+
# Generate list of predicted class indices and scores
395+
result_idxs = np.argwhere(result_infer > 0.4).flatten()
396+
result_scores = result_infer[result_idxs]
397+
398+
# Generate dict {class_index: confidence} to save saliency maps
399+
scores_dict = {i: score for i, score in zip(result_idxs, result_scores)}
400+
392401
# Run explanation
393402
explanation = explainer(
394403
image,
395-
explain_mode=ExplainMode.WHITEBOX,
404+
explain_mode=xai.ExplainMode.WHITEBOX,
396405
label_names=voc_labels,
397-
target_explain_labels=1, # target classes to explain
406+
target_explain_labels=result_idxs, # target classes to explain
398407
)
399408

400409
# Save saliency maps flexibly
401410
OUTPUT_PATH = "output_path"
402-
explanation.save(OUTPUT_PATH) # target_aeroplane.jpg
403-
explanation.save(OUTPUT_PATH, "image_name") # image_name_target_aeroplane.jpg
404-
explanation.save(OUTPUT_PATH, image_name_prefix="image_name") # image_name_target_aeroplane.jpg
405-
406-
# Avoid "target" in salinecy map names
407-
explanation.save(OUTPUT_PATH, target_prefix="") # aeroplane.jpg
408-
explanation.save(OUTPUT_PATH, target_prefix="", target_suffix="class") # aeroplane_class.jpg
409-
explanation.save(OUTPUT_PATH, image_name_prefix="image_name", target_prefix="") # image_name_aeroplane.jpg
411+
explanation.save(OUTPUT_PATH) # aeroplane.jpg
412+
explanation.save(OUTPUT_PATH, "image_name_target_") # image_name_target_aeroplane.jpg
413+
explanation.save(OUTPUT_PATH, prefix="image_name_target_") # image_name_target_aeroplane.jpg
414+
explanation.save(OUTPUT_PATH, postfix="_class_map") # aeroplane_class_map.jpg
415+
explanation.save(OUTPUT_PATH, prefix="image_name_", postfix="_class_map") # image_name_aeroplane_class_map.jpg
410416

411417
# Save saliency maps with confidence scores
412-
explanation.save(OUTPUT_PATH, target_suffix="conf", confidence_scores=scores_dict) # target_aeroplane_conf_0.92.jpg```
418+
explanation.save(
419+
OUTPUT_PATH, prefix="image_name_", postfix="_conf_", confidence_scores=scores_dict
420+
) # image_name_aeroplane_conf_0.85.jpg
413421
```
414422

415-
416423
## Example scripts
417424

418425
More usage scenarios that can be used with your own models and images as arguments are available in [examples](../../examples).

openvino_xai/explainer/explanation.py

+33-41
Original file line numberDiff line numberDiff line change
@@ -135,63 +135,55 @@ def _select_target_indices(
135135
def save(
136136
self,
137137
dir_path: Path | str,
138-
image_name_prefix: str | None = "",
139-
target_prefix: str | None = "target",
140-
target_suffix: str | None = "",
138+
prefix: str = "",
139+
postfix: str = "",
141140
confidence_scores: Dict[int, float] | None = None,
142141
) -> None:
143142
"""
144143
Dumps saliency map images to the specified directory.
145144
146-
Allows flexibly name the files with the image_name_prefix, target_prefix, and target_suffix.
147-
For the name 'image_name_target_aeroplane.jpg': prefix = 'image_name',
148-
target_prefix = 'target', label name = 'aeroplane', target_suffix = ''.
145+
Allows flexibly name the files with the prefix and postfix.
146+
{prefix} + target_id + {postfix}.jpg
149147
150-
save(output_dir) -> target_aeroplane.jpg
151-
save(output_dir, image_name_prefix="test_map", target_prefix="") -> test_map_aeroplane.jpg
152-
save(output_dir, image_name_prefix="test_map") -> test_map_target_aeroplane.jpg
153-
save(output_dir, target_suffix="conf", confidence_scores=scores) -> target_aeroplane_conf_0.92.jpg
148+
Also allows to add confidence scores to the file names.
149+
{prefix} + target_id + {postfix} + confidence.jpg
150+
151+
save(output_dir) -> aeroplane.jpg
152+
save(output_dir, prefix="image_name_target_") -> image_name_target_aeroplane.jpg
153+
save(output_dir, postfix="_class_map") -> aeroplane_class_map.jpg
154+
save(
155+
output_dir, prefix="image_name_", postfix="_conf_", confidence_scores=scores
156+
) -> image_name_aeroplane_conf_0.85.jpg
154157
155158
Parameters:
156-
:param dir_path: The directory path where the saliency maps will be saved.
157-
:type dir_path: Path | str
158-
:param image_name_prefix: Optional prefix for the file names. Default is an empty string.
159-
:type image_name_prefix: str | None
160-
:param target_prefix: Optional suffix for the target. Default is "target".
161-
:type target_prefix: str | None
162-
:param target_suffix: Optional suffix for the saliency map name. Default is an empty string.
163-
:type target_suffix: str | None
164-
:param confidence_scores: Dict with confidence scores for each class to saliency maps with them1 Default is None.
165-
:type confidence_scores: Dict[int, float] | None
159+
:param dir_path: The directory path where the saliency maps will be saved.
160+
:type dir_path: Path | str
161+
:param prefix: Optional prefix for the saliency map names. Default is an empty string.
162+
:type prefix: str
163+
:param postfix: Optional postfix for the saliency map names. Default is an empty string.
164+
:type postfix: str
165+
:param confidence_scores: Dict with confidence scores for each class index. Default is None.
166+
:type confidence_scores: Dict[int, float] | None
166167
167168
"""
168169

169170
os.makedirs(dir_path, exist_ok=True)
170171

171-
image_name_prefix = f"{image_name_prefix}_" if image_name_prefix != "" else image_name_prefix
172-
target_suffix = f"_{target_suffix}" if target_suffix != "" else target_suffix
173-
template = f"{{image_name_prefix}}{{target_prefix}}{{target_name}}{target_suffix}.jpg"
174-
175-
target_prefix = f"{target_prefix}_" if target_prefix != "" else target_prefix
176-
for cls_idx, map_to_save in self._saliency_map.items():
172+
template = f"{prefix}{{target_name}}{postfix}{{conf_score}}.jpg"
173+
for target_idx, map_to_save in self._saliency_map.items():
174+
conf_score = ""
177175
map_to_save = cv2.cvtColor(map_to_save, code=cv2.COLOR_RGB2BGR)
178-
if isinstance(cls_idx, str):
179-
target_name = ""
180-
if target_prefix == "target_":
181-
# Default activation map suffix
182-
target_prefix = "activation_map"
183-
elif target_prefix == "":
184-
# Remove the underscore in case of empty suffix
185-
image_name_prefix = image_name_prefix[:-1] if image_name_prefix.endswith("_") else image_name_prefix
176+
if isinstance(target_idx, str):
177+
target_name = "activation_map"
178+
elif self.label_names and isinstance(target_idx, np.int64):
179+
target_name = self.label_names[target_idx]
186180
else:
187-
target_name = self.label_names[cls_idx] if self.label_names else str(cls_idx)
188-
if confidence_scores:
189-
class_confidence = confidence_scores[cls_idx]
190-
target_name = f"{target_name}_{class_confidence:.2f}"
181+
target_name = str(target_idx)
191182

192-
image_name = template.format(
193-
image_name_prefix=image_name_prefix, target_prefix=target_prefix, target_name=target_name
194-
)
183+
if confidence_scores and target_idx in confidence_scores:
184+
conf_score = f"{confidence_scores[int(target_idx)]:.2f}"
185+
186+
image_name = template.format(target_name=target_name, conf_score=conf_score)
195187
cv2.imwrite(os.path.join(dir_path, image_name), img=map_to_save)
196188

197189

tests/unit/explanation/test_explanation.py

+17-17
Original file line numberDiff line numberDiff line change
@@ -41,37 +41,37 @@ def test_save(self, tmp_path):
4141
save_path = tmp_path / "saliency_maps"
4242

4343
explanation = self._get_explanation()
44-
explanation.save(save_path, image_name_prefix="test_map")
45-
assert os.path.isfile(save_path / "test_map_target_aeroplane.jpg")
46-
assert os.path.isfile(save_path / "test_map_target_bird.jpg")
44+
explanation.save(save_path, prefix="image_name_")
45+
assert os.path.isfile(save_path / "image_name_aeroplane.jpg")
46+
assert os.path.isfile(save_path / "image_name_bird.jpg")
4747

4848
explanation = self._get_explanation()
4949
explanation.save(save_path)
50-
assert os.path.isfile(save_path / "target_aeroplane.jpg")
51-
assert os.path.isfile(save_path / "target_bird.jpg")
50+
assert os.path.isfile(save_path / "aeroplane.jpg")
51+
assert os.path.isfile(save_path / "bird.jpg")
5252

5353
explanation = self._get_explanation(label_names=None)
54-
explanation.save(save_path, "test_map")
55-
assert os.path.isfile(save_path / "test_map_target_0.jpg")
56-
assert os.path.isfile(save_path / "test_map_target_2.jpg")
54+
explanation.save(save_path, postfix="_class_map")
55+
assert os.path.isfile(save_path / "0_class_map.jpg")
56+
assert os.path.isfile(save_path / "2_class_map.jpg")
5757

5858
explanation = self._get_explanation()
59-
explanation.save(save_path, target_prefix="", target_suffix="map")
60-
assert os.path.isfile(save_path / "aeroplane_map.jpg")
61-
assert os.path.isfile(save_path / "bird_map.jpg")
59+
explanation.save(save_path, prefix="image_name_", postfix="_map")
60+
assert os.path.isfile(save_path / "image_name_aeroplane_map.jpg")
61+
assert os.path.isfile(save_path / "image_name_bird_map.jpg")
6262

6363
explanation = self._get_explanation()
64-
explanation.save(save_path, target_suffix="conf", confidence_scores={0: 0.92, 2: 0.85})
65-
assert os.path.isfile(save_path / "target_aeroplane_0.92_conf.jpg")
66-
assert os.path.isfile(save_path / "target_bird_0.85_conf.jpg")
64+
explanation.save(save_path, postfix="_conf_", confidence_scores={0: 0.92, 2: 0.85})
65+
assert os.path.isfile(save_path / "aeroplane_conf_0.92.jpg")
66+
assert os.path.isfile(save_path / "bird_conf_0.85.jpg")
6767

6868
explanation = self._get_explanation(saliency_maps=SALIENCY_MAPS_IMAGE, label_names=None)
69-
explanation.save(save_path, image_name_prefix="test_map")
69+
explanation.save(save_path, prefix="test_map_")
7070
assert os.path.isfile(save_path / "test_map_activation_map.jpg")
7171

7272
explanation = self._get_explanation(saliency_maps=SALIENCY_MAPS_IMAGE, label_names=None)
73-
explanation.save(save_path, image_name_prefix="test_map", target_prefix="")
74-
assert os.path.isfile(save_path / "test_map.jpg")
73+
explanation.save(save_path, prefix="test_map_", postfix="_result")
74+
assert os.path.isfile(save_path / "test_map_activation_map_result.jpg")
7575

7676
def _get_explanation(self, saliency_maps=SALIENCY_MAPS, label_names=VOC_NAMES):
7777
explain_targets = [0, 2]

0 commit comments

Comments
 (0)