Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding the pipeline for the task explanation and Llm #2190

Open
wants to merge 50 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
adbca17
Add Task EXPLANATION and the visualization of images with description.
Bepitic Jul 15, 2024
5611ec1
upd dataset task with explanation
Bepitic Jul 15, 2024
8ed23a3
fix tasktype on metrics, depth, cataset, inferencer.
Bepitic Jul 15, 2024
a463b5b
Merge branch 'main' into llm-pipeline
Bepitic Jul 15, 2024
d5baf6b
fix lint on visualization/image
Bepitic Jul 16, 2024
b7c8eaa
Merge branch 'openvinotoolkit:main' into llm-pipeline
Bepitic Jul 18, 2024
5b563d9
Merge branch 'llm-pipeline' of github.com:Bepitic/anomalib into llm-p…
Bepitic Jul 18, 2024
bfd936e
Fix formatting dataset
Bepitic Jul 18, 2024
f541316
fix format data/base/depth
Bepitic Jul 18, 2024
4e392a9
Fix formatting openvino_inferencer
Bepitic Jul 18, 2024
5fc70ba
fix formatting
Bepitic Jul 18, 2024
75099af
Add Explanation to error-msg.
Bepitic Aug 2, 2024
e5040d3
OpenAI - VLM init
Bepitic Aug 3, 2024
86ad803
Add wrapper to run OpenAI
Bepitic Aug 4, 2024
3678f72
add in ppyproject
Bepitic Aug 4, 2024
7413842
Add Test and fix description/title
Bepitic Aug 12, 2024
dc42cbd
Add Readme and fix bug.
Bepitic Aug 13, 2024
5788d22
Update src/anomalib/models/image/openai_vlm/lightning_model.py
Bepitic Aug 13, 2024
e4f6bec
Update src/anomalib/models/image/openai_vlm/__init__.py
Bepitic Aug 13, 2024
5437467
Add fix pipeline bug.
Bepitic Aug 13, 2024
982c9ca
Add test.
Bepitic Aug 13, 2024
642fd26
Merge branch 'OpenAI-VLM' of github.com:Bepitic/anomalib into OpenAI-VLM
Bepitic Aug 13, 2024
b8cacf0
add changes
Bepitic Aug 16, 2024
0929dc9
Add integration test and unit test + skip export.
Bepitic Aug 16, 2024
39cf996
change to LANGUAGE
Bepitic Aug 16, 2024
671693d
Update images in Readme.
Bepitic Aug 17, 2024
224118b
Update src/anomalib/models/image/chatgpt_vision/__init__.py
Bepitic Aug 20, 2024
b703a41
Update src/anomalib/models/image/chatgpt_vision/chatgpt.py
Bepitic Aug 20, 2024
24c5486
Update src/anomalib/models/image/chatgpt_vision/lightning_model.py
Bepitic Aug 20, 2024
68e757e
Update tests/integration/model/test_models.py
Bepitic Aug 20, 2024
86714a1
Update src/anomalib/models/image/chatgpt_vision/lightning_model.py
Bepitic Aug 20, 2024
196d2a3
Update src/anomalib/models/image/chatgpt_vision/lightning_model.py
Bepitic Aug 20, 2024
b7f345a
fix comments
Bepitic Aug 20, 2024
b285d10
remove last file of chatgpt_vision.
Bepitic Aug 20, 2024
a688530
fix tests
Bepitic Aug 20, 2024
0fb5f79
Merge pull request #1 from Bepitic/OpenAI-VLM (GPTVad)
Bepitic Aug 20, 2024
6503543
Merge branch 'main' into llm-pipeline
Bepitic Aug 20, 2024
8e92e5e
Update src/anomalib/models/image/gptvad/chatgpt.py
Bepitic Aug 21, 2024
5ab044d
upd: language -> VISUAL_PROMPTING
Bepitic Aug 21, 2024
3f9ca93
fix visual prompting and model_name
Bepitic Aug 21, 2024
391b4c4
fix GPT for Gpt and the folder of the tests.
Bepitic Aug 21, 2024
ca1a0bb
fix: change import error outside.
Bepitic Aug 21, 2024
022dcb7
fix readme pointing to the right model.
Bepitic Aug 21, 2024
af7b9e9
fix import cycle, and separate usecase by explicit if.
Bepitic Aug 21, 2024
faf334f
upd: add comments to the few shot / zero shot.
Bepitic Aug 21, 2024
3ed8d3f
fix: dataset expected colums
Bepitic Aug 21, 2024
7f454c4
upd: add the same logic of the label on visualize_full.
Bepitic Aug 22, 2024
45bd520
Merge branch 'main' into llm-pipeline
Bepitic Aug 22, 2024
44586d6
Fix in the logic of the code.
Bepitic Aug 22, 2024
7adb835
Merge branch 'llm-pipeline' of github.com:Bepitic/anomalib into llm-p…
Bepitic Aug 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/anomalib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ class TaskType(str, Enum):
CLASSIFICATION = "classification"
DETECTION = "detection"
SEGMENTATION = "segmentation"
EXPLANATION = "explanation"
Bepitic marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion src/anomalib/callbacks/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def setup(
pixel_metric_names: list[str] | dict[str, dict[str, Any]]
if self.pixel_metric_names is None:
pixel_metric_names = []
elif self.task == TaskType.CLASSIFICATION:
elif self.task in (TaskType.CLASSIFICATION, TaskType.EXPLANATION):
pixel_metric_names = []
logger.warning(
"Cannot perform pixel-level evaluation when task type is classification. "
Bepitic marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
4 changes: 3 additions & 1 deletion src/anomalib/data/base/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
from anomalib.data.utils import LabelName, masks_to_boxes, read_image, read_mask

_EXPECTED_COLUMNS_CLASSIFICATION = ["image_path", "split"]
_EXPECTED_COLUMNS_EXPLANATION = ["image_path", "split"]
_EXPECTED_COLUMNS_SEGMENTATION = [*_EXPECTED_COLUMNS_CLASSIFICATION, "mask_path"]
_EXPECTED_COLUMNS_PERTASK = {
"classification": _EXPECTED_COLUMNS_CLASSIFICATION,
"explanation": _EXPECTED_COLUMNS_EXPLANATION,
"segmentation": _EXPECTED_COLUMNS_SEGMENTATION,
"detection": _EXPECTED_COLUMNS_SEGMENTATION,
}
Expand Down Expand Up @@ -169,7 +171,7 @@ def __getitem__(self, index: int) -> dict[str, str | torch.Tensor]:
image = read_image(image_path, as_tensor=True)
item = {"image_path": image_path, "label": label_index}

if self.task == TaskType.CLASSIFICATION:
if self.task in (TaskType.CLASSIFICATION, TaskType.EXPLANATION):
item["image"] = self.transform(image) if self.transform else image
elif self.task in (TaskType.DETECTION, TaskType.SEGMENTATION):
# Only Anomalous (1) images have masks in anomaly datasets
Expand Down
2 changes: 1 addition & 1 deletion src/anomalib/data/base/depth.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def __getitem__(self, index: int) -> dict[str, str | torch.Tensor]:
depth_image = to_tensor(read_depth_image(depth_path))
item = {"image_path": image_path, "depth_path": depth_path, "label": label_index}

if self.task == TaskType.CLASSIFICATION:
if self.task in (TaskType.CLASSIFICATION, TaskType.EXPLANATION):
item["image"], item["depth_image"] = (
self.transform(image, depth_image) if self.transform else (image, depth_image)
)
Expand Down
2 changes: 1 addition & 1 deletion src/anomalib/deploy/inferencers/openvino_inferencer.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ def post_process(self, predictions: np.ndarray, metadata: dict | DictConfig | No
pred_idx = pred_score >= metadata["image_threshold"]
pred_label = LabelName.ABNORMAL if pred_idx else LabelName.NORMAL

if task == TaskType.CLASSIFICATION:
if task in (TaskType.CLASSIFICATION, TaskType.EXPLANATION):
_, pred_score = self._normalize(pred_scores=pred_score, metadata=metadata)
elif task in (TaskType.SEGMENTATION, TaskType.DETECTION):
if "pixel_threshold" in metadata:
Expand Down
49 changes: 47 additions & 2 deletions src/anomalib/utils/visualization/image.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""Image/video generator."""


# Copyright (C) 2022-2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

import textwrap
from collections.abc import Iterator
from enum import Enum
from pathlib import Path
Expand Down Expand Up @@ -39,6 +41,7 @@ def __init__(
image: np.ndarray,
pred_score: float,
pred_label: str,
text_descr: str | None = None,
anomaly_map: np.ndarray | None = None,
gt_mask: np.ndarray | None = None,
pred_mask: np.ndarray | None = None,
Expand All @@ -47,6 +50,7 @@ def __init__(
box_labels: np.ndarray | None = None,
normalize: bool = False,
) -> None:
self.text_descr = text_descr
self.anomaly_map = anomaly_map
self.box_labels = box_labels
self.gt_boxes = gt_boxes
Expand Down Expand Up @@ -93,6 +97,7 @@ def __repr__(self) -> str:
repr_str += f", segmentations={self.segmentations}" if self.segmentations is not None else ""
repr_str += f", normal_boxes={self.normal_boxes}" if self.normal_boxes is not None else ""
repr_str += f", anomalous_boxes={self.anomalous_boxes}" if self.anomalous_boxes is not None else ""
repr_str += f", text_descr={self.text_descr}" if self.text_descr is not None else ""
repr_str += ")"
return repr_str

Expand Down Expand Up @@ -236,6 +241,13 @@ def _visualize_full(self, image_result: ImageResult) -> np.ndarray:
else:
image_classified = add_normal_label(image_result.image, 1 - image_result.pred_score)
image_grid.add_image(image=image_classified, title="Prediction")
elif self.task == TaskType.EXPLANATION:
description = ""
if image_result.text_descr:
description = image_result.text_descr

image_classified = add_normal_label(image_result.image, 1 - image_result.pred_score)
image_grid.add_image(image_classified, title="Explanation of Image", description=description)

return image_grid.generate()

Expand Down Expand Up @@ -274,6 +286,22 @@ def _visualize_simple(self, image_result: ImageResult) -> np.ndarray:
else:
image_classified = add_normal_label(image_result.image, 1 - image_result.pred_score)
return image_classified

if self.task == TaskType.EXPLANATION:
image_grid = _ImageGrid()
description = ""
if image_result.text_descr:
description = image_result.text_descr

if image_result.pred_label:
Bepitic marked this conversation as resolved.
Show resolved Hide resolved
image_classified = add_anomalous_label(image_result.image, image_result.pred_score)
else:
image_classified = add_normal_label(image_result.image, 1 - image_result.pred_score)

image_grid.add_image(image_classified, title="Explanation of Image", description=description)

return image_grid.generate()

msg = f"Unknown task type: {self.task}"
raise ValueError(msg)

Expand All @@ -290,15 +318,22 @@ def __init__(self) -> None:
self.figure: matplotlib.figure.Figure | None = None
self.axis: Axes | np.ndarray | None = None

def add_image(self, image: np.ndarray, title: str | None = None, color_map: str | None = None) -> None:
def add_image(
self,
image: np.ndarray,
title: str | None = None,
color_map: str | None = None,
description: str | None = None,
) -> None:
"""Add an image to the grid.

Args:
image (np.ndarray): Image which should be added to the figure.
title (str): Image title shown on the plot.
description (str): Description of the image shown on the plot TaskType.EXPLANATION.
color_map (str | None): Name of matplotlib color map used to map scalar data to colours. Defaults to None.
"""
image_data = {"image": image, "title": title, "color_map": color_map}
image_data = {"image": image, "title": title, "color_map": color_map, "descr": description}
self.images.append(image_data)

def generate(self) -> np.ndarray:
Expand All @@ -324,6 +359,16 @@ def generate(self) -> np.ndarray:
axis.imshow(image_dict["image"], image_dict["color_map"], vmin=0, vmax=255)
if image_dict["title"] is not None:
axis.title.set_text(image_dict["title"])
if image_dict["descr"] is not None:
wrapped_text = textwrap.fill(
image_dict["descr"],
width=70 // num_cols,
) # Adjust 'width' based on your subplot size and preference

axis.set_title(wrapped_text, fontsize=10)
Bepitic marked this conversation as resolved.
Show resolved Hide resolved

self.figure.subplots_adjust(top=0.7)

self.figure.canvas.draw()
# convert canvas to numpy array to prepare for visualization with opencv
img = np.frombuffer(self.figure.canvas.tostring_rgb(), dtype=np.uint8)
Expand Down