Skip to content

Commit

Permalink
Merge pull request #897 from roboflow/visualization-block-copy
Browse files Browse the repository at this point in the history
Visualization block copy
  • Loading branch information
PawelPeczek-Roboflow authored Dec 20, 2024
2 parents a3bd826 + 65c814d commit 59743c4
Show file tree
Hide file tree
Showing 11 changed files with 96 additions and 133 deletions.
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

0 comments on commit 59743c4

Please sign in to comment.