vision-agent 0.2.43__py3-none-any.whl → 0.2.44__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vision_agent/agent/vision_agent.py +4 -0
- vision_agent/tools/__init__.py +1 -0
- vision_agent/tools/tools.py +44 -3
- vision_agent/utils/execute.py +24 -0
- vision_agent/utils/video.py +35 -0
- {vision_agent-0.2.43.dist-info → vision_agent-0.2.44.dist-info}/METADATA +2 -2
- {vision_agent-0.2.43.dist-info → vision_agent-0.2.44.dist-info}/RECORD +9 -9
- {vision_agent-0.2.43.dist-info → vision_agent-0.2.44.dist-info}/LICENSE +0 -0
- {vision_agent-0.2.43.dist-info → vision_agent-0.2.44.dist-info}/WHEEL +0 -0
@@ -28,6 +28,7 @@ from vision_agent.utils import CodeInterpreterFactory, Execution
|
|
28
28
|
from vision_agent.utils.execute import CodeInterpreter
|
29
29
|
from vision_agent.utils.image_utils import b64_to_pil
|
30
30
|
from vision_agent.utils.sim import Sim
|
31
|
+
from vision_agent.utils.video import play_video
|
31
32
|
|
32
33
|
logging.basicConfig(stream=sys.stdout)
|
33
34
|
_LOGGER = logging.getLogger(__name__)
|
@@ -522,6 +523,9 @@ class VisionAgent(Agent):
|
|
522
523
|
for res in execution_result.results:
|
523
524
|
if res.png:
|
524
525
|
b64_to_pil(res.png).show()
|
526
|
+
if res.mp4:
|
527
|
+
play_video(res.mp4)
|
528
|
+
|
525
529
|
return {
|
526
530
|
"code": code,
|
527
531
|
"test": test,
|
vision_agent/tools/__init__.py
CHANGED
vision_agent/tools/tools.py
CHANGED
@@ -15,6 +15,7 @@ from PIL import Image, ImageDraw, ImageFont
|
|
15
15
|
|
16
16
|
from vision_agent.tools.tool_utils import _send_inference_request
|
17
17
|
from vision_agent.utils import extract_frames_from_video
|
18
|
+
from vision_agent.utils.execute import FileSerializer, MimeType
|
18
19
|
from vision_agent.utils.image_utils import (
|
19
20
|
b64_to_pil,
|
20
21
|
convert_to_b64,
|
@@ -550,6 +551,29 @@ def save_image(image: np.ndarray) -> str:
|
|
550
551
|
return f.name
|
551
552
|
|
552
553
|
|
554
|
+
def save_video_to_result(video_uri: str) -> None:
|
555
|
+
"""'save_video_to_result' a utility function that saves a video into the result of the code execution (as an intermediate output).
|
556
|
+
This function is required to run if user wants to visualize the video generated by the code.
|
557
|
+
|
558
|
+
Parameters:
|
559
|
+
video_uri (str): The URI to the video file. Currently only local file paths are supported.
|
560
|
+
|
561
|
+
Example
|
562
|
+
-------
|
563
|
+
>>> save_video_to_result("path/to/video.mp4")
|
564
|
+
"""
|
565
|
+
from IPython.display import display
|
566
|
+
|
567
|
+
serializer = FileSerializer(video_uri)
|
568
|
+
display(
|
569
|
+
{
|
570
|
+
MimeType.VIDEO_MP4_B64: serializer.base64(),
|
571
|
+
MimeType.TEXT_PLAIN: str(serializer),
|
572
|
+
},
|
573
|
+
raw=True,
|
574
|
+
)
|
575
|
+
|
576
|
+
|
553
577
|
def overlay_bounding_boxes(
|
554
578
|
image: np.ndarray, bboxes: List[Dict[str, Any]]
|
555
579
|
) -> np.ndarray:
|
@@ -570,6 +594,8 @@ def overlay_bounding_boxes(
|
|
570
594
|
image, [{'score': 0.99, 'label': 'dinosaur', 'bbox': [0.1, 0.11, 0.35, 0.4]}],
|
571
595
|
)
|
572
596
|
"""
|
597
|
+
from IPython.display import display
|
598
|
+
|
573
599
|
pil_image = Image.fromarray(image.astype(np.uint8))
|
574
600
|
|
575
601
|
if len(set([box["label"] for box in bboxes])) > len(COLORS):
|
@@ -606,7 +632,10 @@ def overlay_bounding_boxes(
|
|
606
632
|
text_box = draw.textbbox((box[0], box[1]), text=text, font=font)
|
607
633
|
draw.rectangle((box[0], box[1], text_box[2], text_box[3]), fill=color[label])
|
608
634
|
draw.text((box[0], box[1]), text, fill="black", font=font)
|
609
|
-
|
635
|
+
|
636
|
+
pil_image = pil_image.convert("RGB")
|
637
|
+
display(pil_image)
|
638
|
+
return np.array(pil_image)
|
610
639
|
|
611
640
|
|
612
641
|
def overlay_segmentation_masks(
|
@@ -637,6 +666,8 @@ def overlay_segmentation_masks(
|
|
637
666
|
}],
|
638
667
|
)
|
639
668
|
"""
|
669
|
+
from IPython.display import display
|
670
|
+
|
640
671
|
pil_image = Image.fromarray(image.astype(np.uint8)).convert("RGBA")
|
641
672
|
|
642
673
|
if len(set([mask["label"] for mask in masks])) > len(COLORS):
|
@@ -656,7 +687,10 @@ def overlay_segmentation_masks(
|
|
656
687
|
np_mask[mask > 0, :] = color[label] + (255 * 0.5,)
|
657
688
|
mask_img = Image.fromarray(np_mask.astype(np.uint8))
|
658
689
|
pil_image = Image.alpha_composite(pil_image, mask_img)
|
659
|
-
|
690
|
+
|
691
|
+
pil_image = pil_image.convert("RGB")
|
692
|
+
display(pil_image)
|
693
|
+
return np.array(pil_image)
|
660
694
|
|
661
695
|
|
662
696
|
def overlay_heat_map(
|
@@ -686,6 +720,8 @@ def overlay_heat_map(
|
|
686
720
|
},
|
687
721
|
)
|
688
722
|
"""
|
723
|
+
from IPython.display import display
|
724
|
+
|
689
725
|
pil_image = Image.fromarray(image.astype(np.uint8)).convert("RGB")
|
690
726
|
|
691
727
|
if "heat_map" not in heat_map or len(heat_map["heat_map"]) == 0:
|
@@ -701,7 +737,10 @@ def overlay_heat_map(
|
|
701
737
|
combined = Image.alpha_composite(
|
702
738
|
pil_image.convert("RGBA"), overlay.resize(pil_image.size)
|
703
739
|
)
|
704
|
-
|
740
|
+
|
741
|
+
pil_image = combined.convert("RGB")
|
742
|
+
display(pil_image)
|
743
|
+
return np.array(pil_image)
|
705
744
|
|
706
745
|
|
707
746
|
def get_tool_documentation(funcs: List[Callable[..., Any]]) -> str:
|
@@ -763,6 +802,7 @@ TOOLS = [
|
|
763
802
|
save_json,
|
764
803
|
load_image,
|
765
804
|
save_image,
|
805
|
+
save_video_to_result,
|
766
806
|
overlay_bounding_boxes,
|
767
807
|
overlay_segmentation_masks,
|
768
808
|
overlay_heat_map,
|
@@ -775,6 +815,7 @@ UTILITIES_DOCSTRING = get_tool_documentation(
|
|
775
815
|
save_json,
|
776
816
|
load_image,
|
777
817
|
save_image,
|
818
|
+
save_video_to_result,
|
778
819
|
overlay_bounding_boxes,
|
779
820
|
overlay_segmentation_masks,
|
780
821
|
overlay_heat_map,
|
vision_agent/utils/execute.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import abc
|
2
2
|
import atexit
|
3
|
+
import base64
|
3
4
|
import copy
|
4
5
|
import logging
|
5
6
|
import os
|
@@ -45,12 +46,31 @@ class MimeType(str, Enum):
|
|
45
46
|
IMAGE_SVG = "image/svg+xml"
|
46
47
|
IMAGE_PNG = "image/png"
|
47
48
|
IMAGE_JPEG = "image/jpeg"
|
49
|
+
VIDEO_MP4_B64 = "video/mp4/base64"
|
48
50
|
APPLICATION_PDF = "application/pdf"
|
49
51
|
TEXT_LATEX = "text/latex"
|
50
52
|
APPLICATION_JSON = "application/json"
|
51
53
|
APPLICATION_JAVASCRIPT = "application/javascript"
|
52
54
|
|
53
55
|
|
56
|
+
class FileSerializer:
|
57
|
+
"""Adaptor class that allows IPython.display.display() to serialize a file to a base64 string representation."""
|
58
|
+
|
59
|
+
def __init__(self, file_uri: str):
|
60
|
+
self.video_uri = file_uri
|
61
|
+
assert os.path.isfile(
|
62
|
+
file_uri
|
63
|
+
), f"Only support local files currently: {file_uri}"
|
64
|
+
assert Path(file_uri).exists(), f"File not found: {file_uri}"
|
65
|
+
|
66
|
+
def __repr__(self) -> str:
|
67
|
+
return f"FileSerializer({self.video_uri})"
|
68
|
+
|
69
|
+
def base64(self) -> str:
|
70
|
+
with open(self.video_uri, "rb") as file:
|
71
|
+
return base64.b64encode(file.read()).decode("utf-8")
|
72
|
+
|
73
|
+
|
54
74
|
class Result:
|
55
75
|
"""
|
56
76
|
Represents the data to be displayed as a result of executing a cell in a Jupyter notebook.
|
@@ -70,6 +90,7 @@ class Result:
|
|
70
90
|
png: Optional[str] = None
|
71
91
|
jpeg: Optional[str] = None
|
72
92
|
pdf: Optional[str] = None
|
93
|
+
mp4: Optional[str] = None
|
73
94
|
latex: Optional[str] = None
|
74
95
|
json: Optional[Dict[str, Any]] = None
|
75
96
|
javascript: Optional[str] = None
|
@@ -93,6 +114,7 @@ class Result:
|
|
93
114
|
self.png = data.pop(MimeType.IMAGE_PNG, None)
|
94
115
|
self.jpeg = data.pop(MimeType.IMAGE_JPEG, None)
|
95
116
|
self.pdf = data.pop(MimeType.APPLICATION_PDF, None)
|
117
|
+
self.mp4 = data.pop(MimeType.VIDEO_MP4_B64, None)
|
96
118
|
self.latex = data.pop(MimeType.TEXT_LATEX, None)
|
97
119
|
self.json = data.pop(MimeType.APPLICATION_JSON, None)
|
98
120
|
self.javascript = data.pop(MimeType.APPLICATION_JAVASCRIPT, None)
|
@@ -190,6 +212,8 @@ class Result:
|
|
190
212
|
formats.append("json")
|
191
213
|
if self.javascript:
|
192
214
|
formats.append("javascript")
|
215
|
+
if self.mp4:
|
216
|
+
formats.append("mp4")
|
193
217
|
if self.extra:
|
194
218
|
formats.extend(iter(self.extra))
|
195
219
|
return formats
|
vision_agent/utils/video.py
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
import base64
|
1
2
|
import logging
|
2
3
|
import math
|
3
4
|
import os
|
4
5
|
from concurrent.futures import ProcessPoolExecutor, as_completed
|
6
|
+
import tempfile
|
5
7
|
from typing import List, Tuple, cast
|
6
8
|
|
7
9
|
import cv2
|
@@ -14,6 +16,39 @@ _LOGGER = logging.getLogger(__name__)
|
|
14
16
|
_CLIP_LENGTH = 30.0
|
15
17
|
|
16
18
|
|
19
|
+
def play_video(video_base64: str) -> None:
|
20
|
+
"""Play a video file"""
|
21
|
+
video_data = base64.b64decode(video_base64)
|
22
|
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as temp_video:
|
23
|
+
temp_video.write(video_data)
|
24
|
+
temp_video_path = temp_video.name
|
25
|
+
|
26
|
+
cap = cv2.VideoCapture(temp_video_path)
|
27
|
+
if not cap.isOpened():
|
28
|
+
_LOGGER.error("Error: Could not open video.")
|
29
|
+
return
|
30
|
+
|
31
|
+
# Display the first frame and wait for any key press to start the video
|
32
|
+
ret, frame = cap.read()
|
33
|
+
if ret:
|
34
|
+
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
35
|
+
cv2.imshow("Video Player", frame)
|
36
|
+
_LOGGER.info(f"Press any key to start playing the video: {temp_video_path}")
|
37
|
+
cv2.waitKey(0) # Wait for any key press
|
38
|
+
|
39
|
+
while cap.isOpened():
|
40
|
+
ret, frame = cap.read()
|
41
|
+
if not ret:
|
42
|
+
break
|
43
|
+
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
44
|
+
cv2.imshow("Video Player", frame)
|
45
|
+
# Press 'q' to exit the video
|
46
|
+
if cv2.waitKey(200) & 0xFF == ord("q"):
|
47
|
+
break
|
48
|
+
cap.release()
|
49
|
+
cv2.destroyAllWindows()
|
50
|
+
|
51
|
+
|
17
52
|
def extract_frames_from_video(
|
18
53
|
video_uri: str, fps: float = 0.5, motion_detection_threshold: float = 0.0
|
19
54
|
) -> List[Tuple[np.ndarray, float]]:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: vision-agent
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.44
|
4
4
|
Summary: Toolset for Vision Agent
|
5
5
|
Author: Landing AI
|
6
6
|
Author-email: dev@landing.ai
|
@@ -18,7 +18,7 @@ Requires-Dist: nbclient (>=0.10.0,<0.11.0)
|
|
18
18
|
Requires-Dist: nbformat (>=5.10.4,<6.0.0)
|
19
19
|
Requires-Dist: numpy (>=1.21.0,<2.0.0)
|
20
20
|
Requires-Dist: openai (>=1.0.0,<2.0.0)
|
21
|
-
Requires-Dist: opencv-python
|
21
|
+
Requires-Dist: opencv-python (>=4.0.0,<5.0.0)
|
22
22
|
Requires-Dist: pandas (>=2.0.0,<3.0.0)
|
23
23
|
Requires-Dist: pillow (>=10.0.0,<11.0.0)
|
24
24
|
Requires-Dist: pydantic-settings (>=2.2.1,<3.0.0)
|
@@ -11,7 +11,7 @@ vision_agent/agent/easytool_v2.py,sha256=CjY-sSj3abxnSq3ZHZMt-7YvRWDXEZsC6RN8FFI
|
|
11
11
|
vision_agent/agent/easytool_v2_prompts.py,sha256=MZSIwovYgB-f-kdJ6btaNDVXptJn47bfOL3-Zn6NiC0,8573
|
12
12
|
vision_agent/agent/reflexion.py,sha256=AlM5AvBJvCslXlYQdZiadq4oVHsNBm3IF_03DglTxRo,10506
|
13
13
|
vision_agent/agent/reflexion_prompts.py,sha256=G7UAeNz_g2qCb2yN6OaIC7bQVUkda4m3z42EG8wAyfE,9342
|
14
|
-
vision_agent/agent/vision_agent.py,sha256=
|
14
|
+
vision_agent/agent/vision_agent.py,sha256=JtPDIiLINXm3jBR0LbqblfB9yCv-8M-B7XRx1EPDhFU,18749
|
15
15
|
vision_agent/agent/vision_agent_prompts.py,sha256=FnIYF2Fe3joRvFnOJD9ZyWXMihMyL606nXxWJ0adTZ8,8314
|
16
16
|
vision_agent/fonts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
17
|
vision_agent/fonts/default_font_ch_en.ttf,sha256=1YM0Z3XqLDjSNbF7ihQFSAIUdjF9m1rtHiNC_6QosTE,1594400
|
@@ -19,18 +19,18 @@ vision_agent/llm/__init__.py,sha256=BoUm_zSAKnLlE8s-gKTSQugXDqVZKPqYlWwlTLdhcz4,
|
|
19
19
|
vision_agent/llm/llm.py,sha256=UZ73GqQHE-NKOJWsrOTWfmdHYsbCBkJ5rZ7dhcSCHHw,5951
|
20
20
|
vision_agent/lmm/__init__.py,sha256=nnNeKD1k7q_4vLb1x51O_EUTYaBgGfeiCx5F433gr3M,67
|
21
21
|
vision_agent/lmm/lmm.py,sha256=NwcZYLTzi95LSMAk0sTtw7G_zBLa9lU-DHM5GUUCiK4,10622
|
22
|
-
vision_agent/tools/__init__.py,sha256=
|
22
|
+
vision_agent/tools/__init__.py,sha256=K_7knxmyTIcSEGL8c9wF8RpVh3GrMYfybFaq-2SUM1w,1538
|
23
23
|
vision_agent/tools/easytool_tools.py,sha256=pZc5dQlYINlV4nYbbzsDi3-wauA-fCeD2iGmJUMoUfE,47373
|
24
24
|
vision_agent/tools/prompts.py,sha256=V1z4YJLXZuUl_iZ5rY0M5hHc_2tmMEUKr0WocXKGt4E,1430
|
25
25
|
vision_agent/tools/tool_utils.py,sha256=wzRacbUpqk9hhfX_Y08rL8qP0XCN2w-8IZoYLi3Upn4,869
|
26
|
-
vision_agent/tools/tools.py,sha256=
|
26
|
+
vision_agent/tools/tools.py,sha256=PhmJ0kQeZ-tSQ675HI8QnR49zlH6nJ_opt6QS4dNSVA,25889
|
27
27
|
vision_agent/utils/__init__.py,sha256=Ce4yPhoWanRsnTy3X7YzZNBYYRJsrJeT7N59WUf8GZM,209
|
28
|
-
vision_agent/utils/execute.py,sha256=
|
28
|
+
vision_agent/utils/execute.py,sha256=GlpUGe3pg5KdSvRHLFfVcn9ptXBIp-QRoHT3Wa6aIMs,20318
|
29
29
|
vision_agent/utils/image_utils.py,sha256=_cdiS5YrLzqkq_ZgFUO897m5M4_SCIThwUy4lOklfB8,7700
|
30
30
|
vision_agent/utils/sim.py,sha256=oUZ-6eu8Io-UNt9GXJ0XRKtP-Wc0sPWVzYGVpB2yDFk,3001
|
31
31
|
vision_agent/utils/type_defs.py,sha256=BlI8ywWHAplC7kYWLvt4AOdnKpEW3qWEFm-GEOSkrFQ,1792
|
32
|
-
vision_agent/utils/video.py,sha256=
|
33
|
-
vision_agent-0.2.
|
34
|
-
vision_agent-0.2.
|
35
|
-
vision_agent-0.2.
|
36
|
-
vision_agent-0.2.
|
32
|
+
vision_agent/utils/video.py,sha256=EuJJ7Owi3pIV-q3WcZ-LaaTrGAmmZ8YAA22rmEkY7GI,8885
|
33
|
+
vision_agent-0.2.44.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
34
|
+
vision_agent-0.2.44.dist-info/METADATA,sha256=EbnJiKZzbAgeCN30GRMYfMPN5w_wo9XBkuhWEP_0cN8,6817
|
35
|
+
vision_agent-0.2.44.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
|
36
|
+
vision_agent-0.2.44.dist-info/RECORD,,
|
File without changes
|
File without changes
|