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

Add support for the Image Details endpoint #344

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
34 changes: 34 additions & 0 deletions roboflow/core/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -767,3 +767,37 @@ def __str__(self):
json_str = {"name": self.name, "type": self.type, "workspace": self.__workspace}

return json.dumps(json_str, indent=2)

def image(self, image_id: str) -> Dict:
"""
Fetch the details of a specific image from the Roboflow API.

Args:
image_id (str): The ID of the image to fetch.

Returns:
Dict: A dictionary containing the image details.

Example:
>>> import roboflow

>>> rf = roboflow.Roboflow(api_key="YOUR_API_KEY")

>>> project = rf.workspace().project("PROJECT_ID")

>>> image_details = project.image("image-id")
"""
url = f"{API_URL}/{self.__workspace}/{self.__project_name}/images/{image_id}" f"?api_key={self.__api_key}"

data = requests.get(url).json()

if "error" in data:
raise RuntimeError(data["error"])

if "image" not in data:
print(data, image_id)
raise RuntimeError("Image not found")

image_details = data["image"]

return image_details
7 changes: 2 additions & 5 deletions roboflow/core/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -818,11 +818,8 @@ def __download_zip(self, link, location, format):

def bar_progress(current, total, width=80):
progress_message = (
"Downloading Dataset Version Zip in "
+ location
+ " to "
+ format
+ ": %d%% [%d / %d] bytes" % (current / total * 100, current, total)
f"Downloading Dataset Version Zip in {location} to {format}: "
f"{current / total * 100:.0f}% [{current} / {total}] bytes"
)
sys.stdout.write("\r" + progress_message)
sys.stdout.flush()
Expand Down
2 changes: 1 addition & 1 deletion roboflow/deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def is_valid_ISO8601_timestamp(ts):
try:
datetime.fromisoformat(ts)
return True
except:
except ValueError:
return False


Expand Down
64 changes: 63 additions & 1 deletion tests/test_project.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import requests
import responses

from roboflow import API_URL
from roboflow.adapters.rfapi import AnnotationSaveError, ImageUploadError
from roboflow.config import DEFAULT_BATCH_NAME
from tests import PROJECT_NAME, ROBOFLOW_API_KEY, RoboflowTest
from tests import PROJECT_NAME, ROBOFLOW_API_KEY, WORKSPACE_NAME, RoboflowTest


class TestProject(RoboflowTest):
Expand Down Expand Up @@ -82,3 +83,64 @@ def test_upload_raises_upload_annotation_error(self):
)

self.assertEqual(str(error.exception), "Image was already annotated.")

def test_image_success(self):
image_id = "test-image-id"
expected_url = f"{API_URL}/{WORKSPACE_NAME}/{PROJECT_NAME}/images/{image_id}?api_key={ROBOFLOW_API_KEY}"
mock_response = {
"image": {
"id": image_id,
"name": "test_image.jpg",
"annotation": {
"key": "some-key",
"width": 640,
"height": 480,
"boxes": [{"label": "person", "x": 100, "y": 150, "width": 50, "height": 80}],
},
"labels": ["person"],
"split": "train",
"tags": ["tag1", "tag2"],
"created": 1616161616,
"urls": {
"original": "https://example.com/image.jpg",
"thumb": "https://example.com/thumb.jpg",
"annotation": "https://example.com/annotation.json",
},
"embedding": [0.1, 0.2, 0.3],
}
}

responses.add(responses.GET, expected_url, json=mock_response, status=200)

image_details = self.project.image(image_id)

self.assertIsInstance(image_details, dict)
self.assertEqual(image_details["id"], image_id)
self.assertEqual(image_details["name"], "test_image.jpg")
self.assertIn("annotation", image_details)
self.assertIn("labels", image_details)
self.assertEqual(image_details["split"], "train")

def test_image_not_found(self):
image_id = "nonexistent-image-id"
expected_url = f"{API_URL}/{WORKSPACE_NAME}/{PROJECT_NAME}/images/{image_id}?api_key={ROBOFLOW_API_KEY}"
mock_response = {"error": "Image not found."}

responses.add(responses.GET, expected_url, json=mock_response, status=404)

with self.assertRaises(RuntimeError) as context:
self.project.image(image_id)

self.assertIn("HTTP error occurred while fetching image details", str(context.exception))

def test_image_invalid_json_response(self):
image_id = "invalid-json-image-id"
expected_url = f"{API_URL}/{WORKSPACE_NAME}/{PROJECT_NAME}/images/{image_id}?api_key={ROBOFLOW_API_KEY}"
invalid_json = "Invalid JSON response"

responses.add(responses.GET, expected_url, body=invalid_json, status=200)

with self.assertRaises(requests.exceptions.JSONDecodeError) as context:
self.project.image(image_id)

self.assertIn("Expecting value", str(context.exception))