Skip to content

Commit 5b46359

Browse files
authored
Merge pull request #344 from roboflow/image-details-endpoint
Add support for the Image Details endpoint
2 parents 01d713f + 0708987 commit 5b46359

File tree

4 files changed

+99
-4
lines changed

4 files changed

+99
-4
lines changed

roboflow/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from roboflow.models import CLIPModel, GazeModel # noqa: F401
1616
from roboflow.util.general import write_line
1717

18-
__version__ = "1.1.53"
18+
__version__ = "1.1.54"
1919

2020

2121
def check_key(api_key, model, notebook, num_retries=0):

roboflow/core/project.py

+34
Original file line numberDiff line numberDiff line change
@@ -767,3 +767,37 @@ def __str__(self):
767767
json_str = {"name": self.name, "type": self.type, "workspace": self.__workspace}
768768

769769
return json.dumps(json_str, indent=2)
770+
771+
def image(self, image_id: str) -> Dict:
772+
"""
773+
Fetch the details of a specific image from the Roboflow API.
774+
775+
Args:
776+
image_id (str): The ID of the image to fetch.
777+
778+
Returns:
779+
Dict: A dictionary containing the image details.
780+
781+
Example:
782+
>>> import roboflow
783+
784+
>>> rf = roboflow.Roboflow(api_key="YOUR_API_KEY")
785+
786+
>>> project = rf.workspace().project("PROJECT_ID")
787+
788+
>>> image_details = project.image("image-id")
789+
"""
790+
url = f"{API_URL}/{self.__workspace}/{self.__project_name}/images/{image_id}?api_key={self.__api_key}"
791+
792+
data = requests.get(url).json()
793+
794+
if "error" in data:
795+
raise RuntimeError(data["error"])
796+
797+
if "image" not in data:
798+
print(data, image_id)
799+
raise RuntimeError("Image not found")
800+
801+
image_details = data["image"]
802+
803+
return image_details

roboflow/core/version.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -539,8 +539,7 @@ def __download_zip(self, link, location, format):
539539

540540
def bar_progress(current, total, width=80):
541541
progress_message = (
542-
"Downloading Dataset Version Zip in "
543-
f"{location} to {format}: "
542+
f"Downloading Dataset Version Zip in {location} to {format}: "
544543
f"{current / total * 100:.0f}% [{current} / {total}] bytes"
545544
)
546545
sys.stdout.write("\r" + progress_message)

tests/test_project.py

+63-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import requests
12
import responses
23

34
from roboflow import API_URL
45
from roboflow.adapters.rfapi import AnnotationSaveError, ImageUploadError
56
from roboflow.config import DEFAULT_BATCH_NAME
6-
from tests import PROJECT_NAME, ROBOFLOW_API_KEY, RoboflowTest
7+
from tests import PROJECT_NAME, ROBOFLOW_API_KEY, WORKSPACE_NAME, RoboflowTest
78

89

910
class TestProject(RoboflowTest):
@@ -82,3 +83,64 @@ def test_upload_raises_upload_annotation_error(self):
8283
)
8384

8485
self.assertEqual(str(error.exception), "Image was already annotated.")
86+
87+
def test_image_success(self):
88+
image_id = "test-image-id"
89+
expected_url = f"{API_URL}/{WORKSPACE_NAME}/{PROJECT_NAME}/images/{image_id}?api_key={ROBOFLOW_API_KEY}"
90+
mock_response = {
91+
"image": {
92+
"id": image_id,
93+
"name": "test_image.jpg",
94+
"annotation": {
95+
"key": "some-key",
96+
"width": 640,
97+
"height": 480,
98+
"boxes": [{"label": "person", "x": 100, "y": 150, "width": 50, "height": 80}],
99+
},
100+
"labels": ["person"],
101+
"split": "train",
102+
"tags": ["tag1", "tag2"],
103+
"created": 1616161616,
104+
"urls": {
105+
"original": "https://example.com/image.jpg",
106+
"thumb": "https://example.com/thumb.jpg",
107+
"annotation": "https://example.com/annotation.json",
108+
},
109+
"embedding": [0.1, 0.2, 0.3],
110+
}
111+
}
112+
113+
responses.add(responses.GET, expected_url, json=mock_response, status=200)
114+
115+
image_details = self.project.image(image_id)
116+
117+
self.assertIsInstance(image_details, dict)
118+
self.assertEqual(image_details["id"], image_id)
119+
self.assertEqual(image_details["name"], "test_image.jpg")
120+
self.assertIn("annotation", image_details)
121+
self.assertIn("labels", image_details)
122+
self.assertEqual(image_details["split"], "train")
123+
124+
def test_image_not_found(self):
125+
image_id = "nonexistent-image-id"
126+
expected_url = f"{API_URL}/{WORKSPACE_NAME}/{PROJECT_NAME}/images/{image_id}?api_key={ROBOFLOW_API_KEY}"
127+
mock_response = {"error": "Image not found."}
128+
129+
responses.add(responses.GET, expected_url, json=mock_response, status=404)
130+
131+
with self.assertRaises(RuntimeError) as context:
132+
self.project.image(image_id)
133+
134+
self.assertIn("HTTP error occurred while fetching image details", str(context.exception))
135+
136+
def test_image_invalid_json_response(self):
137+
image_id = "invalid-json-image-id"
138+
expected_url = f"{API_URL}/{WORKSPACE_NAME}/{PROJECT_NAME}/images/{image_id}?api_key={ROBOFLOW_API_KEY}"
139+
invalid_json = "Invalid JSON response"
140+
141+
responses.add(responses.GET, expected_url, body=invalid_json, status=200)
142+
143+
with self.assertRaises(requests.exceptions.JSONDecodeError) as context:
144+
self.project.image(image_id)
145+
146+
self.assertIn("Expecting value", str(context.exception))

0 commit comments

Comments
 (0)