Skip to content
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
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ matplotlib
numpy>=1.18.5
opencv-python-headless==4.10.0.84
Pillow>=7.1.2
pillow-heif>=0.18.0
python-dateutil
python-dotenv
requests
Expand Down
2 changes: 1 addition & 1 deletion roboflow/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from roboflow.models import CLIPModel, GazeModel # noqa: F401
from roboflow.util.general import write_line

__version__ = "1.1.57"
__version__ = "1.1.58"


def check_key(api_key, model, notebook, num_retries=0):
Expand Down
3 changes: 3 additions & 0 deletions roboflow/core/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
"image/jpeg",
"image/png",
"image/webp",
"image/tiff",
"image/avif",
"image/heic",
}


Expand Down
2 changes: 1 addition & 1 deletion roboflow/util/folderparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from .image_utils import load_labelmap

IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".bmp"}
IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".tif", ".avif", ".heic"}
ANNOTATION_EXTENSIONS = {".txt", ".json", ".xml", ".csv", ".jsonl"}
LABELMAPS_EXTENSIONS = {".labels", ".yaml", ".yml"}

Expand Down
19 changes: 17 additions & 2 deletions roboflow/util/image_utils.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
# Standard library imports
import base64
import io
import os
import urllib

# Third-party imports
import pillow_heif # type: ignore[import-untyped]
import requests
import yaml
from PIL import Image

pillow_heif.register_heif_opener(thumbnails=False) # Register for HEIF/HEIC
pillow_heif.register_avif_opener(thumbnails=False) # Register for AVIF


def check_image_path(image_path):
"""
Expand Down Expand Up @@ -74,9 +80,18 @@ def validate_image_path(image_path):
def file2jpeg(image_path):
import cv2

# OpenCV will handle standard formats efficiently
img = cv2.imread(image_path)
image = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
pilImage = Image.fromarray(image)
if img is not None:
# Convert BGR to RGB for PIL
image = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
pilImage = Image.fromarray(image)
else:
# If OpenCV fails, the format might be HEIC/AVIF which are handled by PIL
pilImage = Image.open(image_path)
if pilImage.mode != "RGB":
pilImage = pilImage.convert("RGB")

buffered = io.BytesIO()
pilImage.save(buffered, quality=100, format="JPEG")
return buffered.getvalue()
Expand Down
Binary file added tests/images/file_example_TIFF_1MB.tiff
Binary file not shown.
Binary file added tests/images/whatsnew.avif
Binary file not shown.
9 changes: 5 additions & 4 deletions tests/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ def test_check_valid_image_with_accepted_formats(self):
"rabbit2.jpg",
"hand-rabbit.PNG",
"woodland-rabbit.png",
"file_example_TIFF_1MB.tiff",
"sky-rabbit.heic",
]

for image in images_to_test:
Expand All @@ -23,7 +25,6 @@ def test_check_valid_image_with_accepted_formats(self):
def test_check_valid_image_with_unaccepted_formats(self):
images_to_test = [
"sky-rabbit.gif",
"sky-rabbit.heic",
]

for image in images_to_test:
Expand All @@ -32,7 +33,7 @@ def test_check_valid_image_with_unaccepted_formats(self):
def test_upload_raises_upload_image_error(self):
responses.add(
responses.POST,
f"{API_URL}/dataset/{PROJECT_NAME}/upload?api_key={ROBOFLOW_API_KEY}" f"&batch={DEFAULT_BATCH_NAME}",
f"{API_URL}/dataset/{PROJECT_NAME}/upload?api_key={ROBOFLOW_API_KEY}&batch={DEFAULT_BATCH_NAME}",
json={
"error": {
"message": "Invalid image.",
Expand All @@ -58,15 +59,15 @@ def test_upload_raises_upload_annotation_error(self):
# Image upload
responses.add(
responses.POST,
f"{API_URL}/dataset/{PROJECT_NAME}/upload?api_key={ROBOFLOW_API_KEY}" f"&batch={DEFAULT_BATCH_NAME}",
f"{API_URL}/dataset/{PROJECT_NAME}/upload?api_key={ROBOFLOW_API_KEY}&batch={DEFAULT_BATCH_NAME}",
json={"success": True, "id": image_id},
status=200,
)

# Annotation
responses.add(
responses.POST,
f"{API_URL}/dataset/{PROJECT_NAME}/annotate/{image_id}?api_key={ROBOFLOW_API_KEY}" f"&name={image_name}",
f"{API_URL}/dataset/{PROJECT_NAME}/annotate/{image_id}?api_key={ROBOFLOW_API_KEY}&name={image_name}",
json={
"error": {
"message": "Image was already annotated.",
Expand Down