Skip to content

Commit 2c92f27

Browse files
authored
Merge pull request #464 from roboflow/riaz/ent-1169-roboflow-python-remove-client-side-image-conversion-upload
fix: upload original image bytes instead of re-encoding to JPEG (ENT-1169)
2 parents 68b6495 + 23dee0f commit 2c92f27

2 files changed

Lines changed: 36 additions & 13 deletions

File tree

‎roboflow/adapters/rfapi.py‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import mimetypes
23
import os
34
import urllib
45
from typing import Dict, List, Optional, Union
@@ -293,18 +294,18 @@ def upload_image(
293294

294295
# If image is not a hosted image
295296
if not hosted_image:
296-
from roboflow.util import image_utils
297-
298297
image_name = os.path.basename(image_path)
299-
imgjpeg = image_utils.file2jpeg(image_path)
298+
with open(image_path, "rb") as fh:
299+
image_bytes = fh.read()
300+
content_type = mimetypes.guess_type(image_path)[0] or "application/octet-stream"
300301

301302
upload_url = _local_upload_url(
302303
api_key, project_url, coalesced_batch_name, tag_names, sequence_number, sequence_size, kwargs
303304
)
304305
fields = {
305306
"name": image_name,
306307
"split": split,
307-
"file": ("imageToUpload", imgjpeg, "image/jpeg"),
308+
"file": (image_name, image_bytes, content_type),
308309
}
309310
if metadata is not None:
310311
fields["metadata"] = json.dumps(metadata)

‎tests/test_rfapi.py‎

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33
import unittest
44
import urllib
5-
from unittest.mock import patch
5+
from unittest.mock import mock_open, patch
66

77
import responses
88

@@ -22,10 +22,8 @@ class TestUploadImage(unittest.TestCase):
2222
IMAGE_NAME_HOSTED = os.path.basename(IMAGE_PATH_HOSTED)
2323

2424
@responses.activate
25-
@patch("roboflow.util.image_utils.file2jpeg")
26-
def test_upload_image_local(self, mock_file2jpeg):
27-
mock_file2jpeg.return_value = b"image_data"
28-
25+
@patch("roboflow.adapters.rfapi.open", new_callable=mock_open, read_data=b"image_data")
26+
def test_upload_image_local(self, _mock_file):
2927
scenarios = [
3028
{
3129
"desc": "with batch_name",
@@ -123,10 +121,8 @@ def test_upload_image_hosted(self):
123121
self.assertTrue(result["success"], msg=f"Failed in scenario: {scenario['desc']}")
124122

125123
@responses.activate
126-
@patch("roboflow.util.image_utils.file2jpeg")
127-
def test_upload_image_local_with_metadata(self, mock_file2jpeg):
128-
mock_file2jpeg.return_value = b"image_data"
129-
124+
@patch("roboflow.adapters.rfapi.open", new_callable=mock_open, read_data=b"image_data")
125+
def test_upload_image_local_with_metadata(self, _mock_file):
130126
metadata = {"camera_id": "cam001", "location": "warehouse"}
131127
expected_url = (
132128
f"{API_URL}/dataset/{self.PROJECT_URL}/upload?"
@@ -172,6 +168,32 @@ def test_upload_image_hosted_with_metadata(self):
172168
)
173169
self.assertTrue(result["success"])
174170

171+
@responses.activate
172+
def test_upload_image_local_uploads_original_bytes(self):
173+
"""Server-side dedup relies on the SDK uploading the file bytes exactly as-is."""
174+
import tempfile
175+
176+
raw_bytes = b"\x89PNG\r\n\x1a\nfake-png-bytes-not-decodable-as-jpeg"
177+
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tf:
178+
tf.write(raw_bytes)
179+
tmp_path = tf.name
180+
181+
try:
182+
expected_url = (
183+
f"{API_URL}/dataset/{self.PROJECT_URL}/upload?"
184+
f"api_key={self.API_KEY}&batch={urllib.parse.quote_plus(DEFAULT_BATCH_NAME)}"
185+
)
186+
responses.add(responses.POST, expected_url, json={"success": True}, status=200)
187+
188+
result = upload_image(self.API_KEY, self.PROJECT_URL, tmp_path)
189+
self.assertTrue(result["success"])
190+
191+
request_body = responses.calls[0].request.body
192+
self.assertIn(raw_bytes, request_body)
193+
self.assertIn(b"image/png", request_body)
194+
finally:
195+
os.unlink(tmp_path)
196+
175197
def _reset_responses(self):
176198
responses.reset()
177199

0 commit comments

Comments
 (0)