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

Visualization block copy #897

Merged
merged 6 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ class BoundingBoxManifest(ColorableVisualizationManifest):
)

thickness: Union[int, Selector(kind=[INTEGER_KIND])] = Field( # type: ignore
description="Thickness of the bounding box in pixels.",
description="Set the thickness of the bounding box edges.",
default=2,
examples=[2, "$inputs.thickness"],
)

roundness: Union[FloatZeroToOne, Selector(kind=[FLOAT_ZERO_TO_ONE_KIND])] = Field( # type: ignore
description="Roundness of the corners of the bounding box.",
description="Define the roundness of the bounding box corners.",
default=0.0,
examples=[0.0, "$inputs.roundness"],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ class VisualizationManifest(WorkflowBlockManifest, ABC):
)
image: Selector(kind=[IMAGE_KIND]) = Field(
title="Input Image",
description="The input image for this step.",
description="Select the input image to visualize on.",
examples=["$inputs.image", "$steps.cropping.crops"],
validation_alias=AliasChoices("image", "images"),
)
copy_image: Union[bool, Selector(kind=[BOOLEAN_KIND])] = Field( # type: ignore
description="Duplicate the image contents (vs overwriting the image in place). Deselect for chained visualizations that should stack on previous ones where the intermediate state is not needed.",
description="Enable this option to create a copy of the input image for visualization, preserving the original. Use this when stacking multiple visualizations.",
default=True,
examples=[True, False],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class ColorableVisualizationManifest(PredictionsVisualizationManifest, ABC):
Selector(kind=[STRING_KIND]),
] = Field( # type: ignore
default="DEFAULT",
description="Color palette to use for annotations.",
description="Select a color palette for the visualised elements.",
examples=["DEFAULT", "$inputs.color_palette"],
)

Expand All @@ -86,14 +86,14 @@ class ColorableVisualizationManifest(PredictionsVisualizationManifest, ABC):
Selector(kind=[INTEGER_KIND]),
] = Field( # type: ignore
default=10,
description="Number of colors in the color palette. Applies when using a matplotlib `color_palette`.",
description="Specify the number of colors in the palette. This applies when using custom or Matplotlib palettes.",
examples=[10, "$inputs.palette_size"],
)

custom_colors: Union[List[str], Selector(kind=[LIST_OF_VALUES_KIND])] = (
Field( # type: ignore
default=[],
description='List of colors to use for annotations when `color_palette` is set to "CUSTOM".',
description="Define a list of custom colors for bounding boxes in HEX format.",
examples=[["#FF0000", "#00FF00", "#0000FF"], "$inputs.custom_colors"],
)
)
Expand All @@ -103,7 +103,7 @@ class ColorableVisualizationManifest(PredictionsVisualizationManifest, ABC):
Selector(kind=[STRING_KIND]),
] = Field( # type: ignore
default="CLASS",
description="Strategy to use for mapping colors to annotations.",
description="Choose how bounding box colors are assigned.",
examples=["CLASS", "$inputs.color_axis"],
)

Expand Down
4 changes: 3 additions & 1 deletion inference/core/workflows/prototypes/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ class WorkflowBlockManifest(BaseModel, ABC):
)

type: str
name: str = Field(title="Step Name", description="The unique name of this step.")
name: str = Field(
title="Step Name", description="Enter a unique identifier for this step."
)

@classmethod
@abstractmethod
Expand Down
35 changes: 22 additions & 13 deletions tests/inference/models_predictions_tests/test_owlv2.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import pytest
import gc
from unittest.mock import MagicMock
import os
from unittest.mock import MagicMock

import pytest
import torch

from inference.core.cache.model_artifacts import get_cache_file_path
from inference.core.entities.requests.inference import ObjectDetectionInferenceRequest
from inference.core.entities.requests.owlv2 import OwlV2InferenceRequest
from inference.models.owlv2.owlv2 import OwlV2, SerializedOwlV2, Owlv2Singleton, LazyImageRetrievalWrapper
from inference.core.env import OWLV2_VERSION_ID
from inference.core.cache.model_artifacts import get_cache_file_path
from inference.models.owlv2.owlv2 import (
LazyImageRetrievalWrapper,
OwlV2,
Owlv2Singleton,
SerializedOwlV2,
)


@pytest.mark.slow
Expand Down Expand Up @@ -58,13 +65,14 @@ def test_owlv2():
assert abs(532 - posts[3].x) < 1.5
assert abs(572 - posts[4].x) < 1.5


def test_owlv2_serialized():
image = {
"type": "url",
"value": "https://media.roboflow.com/inference/seawithdock.jpeg",
}

training_data=[
training_data = [
{
"image": image,
"boxes": [
Expand Down Expand Up @@ -93,19 +101,20 @@ def test_owlv2_serialized():
hf_id=f"google/{OWLV2_VERSION_ID}",
)
assert os.path.exists(serialized_pt)
pt_path = get_cache_file_path(file=SerializedOwlV2.weights_file_path, model_id=model_id)
pt_path = get_cache_file_path(
file=SerializedOwlV2.weights_file_path, model_id=model_id
)
os.makedirs(os.path.dirname(pt_path), exist_ok=True)
os.rename(serialized_pt, pt_path)
serialized_owlv2 = SerializedOwlV2(model_id=model_id)

# Get the image hash before inference
image_wrapper = LazyImageRetrievalWrapper(request.image)
image_hash = image_wrapper.image_hash
assert image_hash in serialized_owlv2.owlv2.cpu_image_embed_cache

response = serialized_owlv2.infer_from_request(request)



assert len(response.predictions) == 5
posts = [p for p in response.predictions if p.class_name == "post"]
posts.sort(key=lambda x: x.x)
Expand All @@ -114,15 +123,13 @@ def test_owlv2_serialized():
assert abs(264 - posts[2].x) < 1.5
assert abs(532 - posts[3].x) < 1.5
assert abs(572 - posts[4].x) < 1.5

pt_path = serialized_owlv2.save_small_model_without_image_embeds()
assert os.path.exists(pt_path)
pt_dict = torch.load(pt_path)
assert len(pt_dict["image_embeds"]) == 0




@pytest.mark.slow
def test_owlv2_multiple_prompts():
image = {
Expand Down Expand Up @@ -411,12 +418,14 @@ def test_owlv2_multiple_training_images_repeated_inference():
assert p1.height == p2.height
assert p1.confidence == p2.confidence


@pytest.mark.slow
def test_owlv2_model_unloaded_when_garbage_collected():
model = OwlV2()
del model
gc.collect()
assert len(Owlv2Singleton._instances) == 0


if __name__ == "__main__":
test_owlv2()
1 change: 1 addition & 0 deletions tests/inference/models_predictions_tests/test_sam2.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from inference.core.workflows.core_steps.models.foundation.segment_anything2.v1 import (
convert_sam2_segmentation_response_to_inference_instances_seg_response,
)

try:
from inference.models.sam2 import SegmentAnything2
from inference.models.sam2.segment_anything2 import (
Expand Down
35 changes: 16 additions & 19 deletions tests/inference_cli/unit_tests/lib/workflows/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,25 +86,22 @@ def test_aggregate_batch_processing_results_when_json_output_is_expected_and_res
continue
decoded_results.append(json.loads(line))
print(decoded_results)
assert (
decoded_results
== [
{
"some": "value",
"image": "other.jpg",
"other": 3.0,
"list_field": [1, 2, 3],
"object_field": {"nested": "value"},
},
{
"some": "value",
"image": "some.jpg",
"other": 3.0,
"list_field": [1, 2, 3],
"object_field": {"nested": "value"},
}
]
)
assert decoded_results == [
{
"some": "value",
"image": "other.jpg",
"other": 3.0,
"list_field": [1, 2, 3],
"object_field": {"nested": "value"},
},
{
"some": "value",
"image": "some.jpg",
"other": 3.0,
"list_field": [1, 2, 3],
"object_field": {"nested": "value"},
},
]


def test_aggregate_batch_processing_results_when_json_output_is_expected_and_results_not_present(
Expand Down
67 changes: 16 additions & 51 deletions tests/workflows/unit_tests/core_steps/cache/test_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,16 @@

import numpy as np

from inference.core.workflows.core_steps.cache.cache_get.v1 import CacheGetBlockV1
from inference.core.workflows.core_steps.cache.cache_set.v1 import CacheSetBlockV1
from inference.core.workflows.core_steps.common.entities import StepExecutionMode
from inference.core.workflows.core_steps.cache.cache_get.v1 import (
CacheGetBlockV1,
)
from inference.core.workflows.core_steps.cache.cache_set.v1 import (
CacheSetBlockV1,
)

from inference.core.workflows.execution_engine.entities.base import (
ImageParentMetadata,
VideoMetadata,
WorkflowImageData,
)


def test_cache_on_video() -> None:
# given
metadata = VideoMetadata(
Expand All @@ -34,28 +30,19 @@ def test_cache_on_video() -> None:
cache_set_block = CacheSetBlockV1(step_execution_mode=StepExecutionMode.LOCAL)

# empty result
get_empty = cache_get_block.run(
image=image,
key="foo"
)
get_empty = cache_get_block.run(image=image, key="foo")
assert get_empty == {
"output": False,
}

# set then get
cache_set_block.run(
image=image,
key="foo",
value="bar"
)
get_full = cache_get_block.run(
image=image,
key="foo"
)
cache_set_block.run(image=image, key="foo", value="bar")
get_full = cache_get_block.run(image=image, key="foo")
assert get_full == {
"output": "bar",
}


def test_cache_with_no_metadata() -> None:
# given
image = WorkflowImageData(
Expand All @@ -66,28 +53,19 @@ def test_cache_with_no_metadata() -> None:
cache_set_block = CacheSetBlockV1(step_execution_mode=StepExecutionMode.LOCAL)

# empty result
get_empty = cache_get_block.run(
image=image,
key="foo"
)
get_empty = cache_get_block.run(image=image, key="foo")
assert get_empty == {
"output": False,
}

# set then get
cache_set_block.run(
image=image,
key="foo",
value="bar"
)
get_full = cache_get_block.run(
image=image,
key="foo"
)
cache_set_block.run(image=image, key="foo", value="bar")
get_full = cache_get_block.run(image=image, key="foo")
assert get_full == {
"output": "bar",
}


def test_cache_on_multiple_videos() -> None:
# given
metadata_1 = VideoMetadata(
Expand Down Expand Up @@ -120,33 +98,20 @@ def test_cache_on_multiple_videos() -> None:
cache_set_block = CacheSetBlockV1(step_execution_mode=StepExecutionMode.LOCAL)

# empty result
get_empty = cache_get_block.run(
image=image_1,
key="foo"
)
get_empty = cache_get_block.run(image=image_1, key="foo")
assert get_empty == {
"output": False,
}

# set then get
cache_set_block.run(
image=image_1,
key="foo",
value="bar"
)
get_full = cache_get_block.run(
image=image_1,
key="foo"
)
cache_set_block.run(image=image_1, key="foo", value="bar")
get_full = cache_get_block.run(image=image_1, key="foo")
assert get_full == {
"output": "bar",
}

# make sure it doesn't bleed over
get_empty = cache_get_block.run(
image=image_2,
key="foo"
)
get_empty = cache_get_block.run(image=image_2, key="foo")
assert get_empty == {
"output": False,
}
}
Loading
Loading