2
2
import json
3
3
import mimetypes
4
4
import os
5
- import re
6
5
import sys
7
6
import time
8
7
import warnings
12
11
import requests
13
12
14
13
from roboflow .adapters import rfapi
14
+ from roboflow .adapters .rfapi import ImageUploadError
15
15
from roboflow .config import API_URL , DEMO_KEYS
16
16
from roboflow .core .version import Version
17
17
from roboflow .util .general import Retry
@@ -465,6 +465,76 @@ def upload(
465
465
print ("[ " + path + " ] was skipped." )
466
466
continue
467
467
468
+ def upload_image (
469
+ self ,
470
+ image_path = None ,
471
+ hosted_image = False ,
472
+ split = "train" ,
473
+ num_retry_uploads = 0 ,
474
+ batch_name = None ,
475
+ tag_names = [],
476
+ sequence_number = None ,
477
+ sequence_size = None ,
478
+ ** kwargs ,
479
+ ):
480
+ project_url = self .id .rsplit ("/" )[1 ]
481
+
482
+ t0 = time .time ()
483
+ upload_retry_attempts = 0
484
+ retry = Retry (num_retry_uploads , ImageUploadError )
485
+
486
+ try :
487
+ image = retry (
488
+ rfapi .upload_image ,
489
+ self .__api_key ,
490
+ project_url ,
491
+ image_path ,
492
+ hosted_image = hosted_image ,
493
+ split = split ,
494
+ batch_name = batch_name ,
495
+ tag_names = tag_names ,
496
+ sequence_number = sequence_number ,
497
+ sequence_size = sequence_size ,
498
+ ** kwargs ,
499
+ )
500
+ upload_retry_attempts = retry .retries
501
+ except ImageUploadError as e :
502
+ e .retries = upload_retry_attempts
503
+ raise e
504
+
505
+ upload_time = time .time () - t0
506
+
507
+ return image , upload_time , upload_retry_attempts
508
+
509
+ def save_annotation (
510
+ self ,
511
+ annotation_path = None ,
512
+ annotation_labelmap = None ,
513
+ image_id = None ,
514
+ job_name = None ,
515
+ is_prediction : bool = False ,
516
+ annotation_overwrite = False ,
517
+ ):
518
+ project_url = self .id .rsplit ("/" )[1 ]
519
+ annotation_name , annotation_str = self ._annotation_params (annotation_path )
520
+ t0 = time .time ()
521
+
522
+ annotation = rfapi .save_annotation (
523
+ self .__api_key ,
524
+ project_url ,
525
+ annotation_name , # type: ignore[type-var]
526
+ annotation_str , # type: ignore[type-var]
527
+ image_id ,
528
+ job_name = job_name , # type: ignore[type-var]
529
+ is_prediction = is_prediction ,
530
+ annotation_labelmap = annotation_labelmap ,
531
+ overwrite = annotation_overwrite ,
532
+ )
533
+
534
+ upload_time = time .time () - t0
535
+
536
+ return annotation , upload_time
537
+
468
538
def single_upload (
469
539
self ,
470
540
image_path = None ,
@@ -482,64 +552,41 @@ def single_upload(
482
552
sequence_size = None ,
483
553
** kwargs ,
484
554
):
485
- project_url = self .id .rsplit ("/" )[1 ]
486
555
if image_path and image_id :
487
556
raise Exception ("You can't pass both image_id and image_path" )
488
557
if not (image_path or image_id ):
489
558
raise Exception ("You need to pass image_path or image_id" )
490
559
if isinstance (annotation_labelmap , str ):
491
560
annotation_labelmap = load_labelmap (annotation_labelmap )
561
+
492
562
uploaded_image , uploaded_annotation = None , None
493
- upload_time = None
563
+ upload_time , annotation_time = None , None
494
564
upload_retry_attempts = 0
565
+
495
566
if image_path :
496
- t0 = time .time ()
497
- try :
498
- retry = Retry (num_retry_uploads , Exception )
499
- uploaded_image = retry (
500
- rfapi .upload_image ,
501
- self .__api_key ,
502
- project_url ,
503
- image_path ,
504
- hosted_image = hosted_image ,
505
- split = split ,
506
- batch_name = batch_name ,
507
- tag_names = tag_names ,
508
- sequence_number = sequence_number ,
509
- sequence_size = sequence_size ,
510
- ** kwargs ,
511
- )
512
- image_id = uploaded_image ["id" ] # type: ignore[index]
513
- upload_retry_attempts = retry .retries
514
- except rfapi .UploadError as e :
515
- raise RuntimeError (f"Error uploading image: { self ._parse_upload_error (e )} " )
516
- except BaseException as e :
517
- uploaded_image = {"error" : e }
518
- finally :
519
- upload_time = time .time () - t0
520
-
521
- annotation_time = None
567
+ uploaded_image , upload_time , upload_retry_attempts = self .upload_image (
568
+ image_path ,
569
+ hosted_image ,
570
+ split ,
571
+ num_retry_uploads ,
572
+ batch_name ,
573
+ tag_names ,
574
+ sequence_number ,
575
+ sequence_size ,
576
+ ** kwargs ,
577
+ )
578
+ image_id = uploaded_image ["id" ] # type: ignore[index]
579
+
522
580
if annotation_path and image_id :
523
- annotation_name , annotation_str = self ._annotation_params (annotation_path )
524
- try :
525
- t0 = time .time ()
526
- uploaded_annotation = rfapi .save_annotation (
527
- self .__api_key ,
528
- project_url ,
529
- annotation_name , # type: ignore[type-var]
530
- annotation_str , # type: ignore[type-var]
531
- image_id ,
532
- job_name = batch_name , # type: ignore[type-var]
533
- is_prediction = is_prediction ,
534
- annotation_labelmap = annotation_labelmap ,
535
- overwrite = annotation_overwrite ,
536
- )
537
- except rfapi .UploadError as e :
538
- raise RuntimeError (f"Error uploading annotation: { self ._parse_upload_error (e )} " )
539
- except BaseException as e :
540
- uploaded_annotation = {"error" : e }
541
- finally :
542
- annotation_time = time .time () - t0
581
+ uploaded_annotation , annotation_time = self .save_annotation (
582
+ annotation_path ,
583
+ annotation_labelmap ,
584
+ image_id ,
585
+ batch_name ,
586
+ is_prediction ,
587
+ annotation_overwrite ,
588
+ )
589
+
543
590
return {
544
591
"image" : uploaded_image ,
545
592
"annotation" : uploaded_annotation ,
@@ -568,20 +615,6 @@ def _annotation_params(self, annotation_path):
568
615
)
569
616
return annotation_name , annotation_string
570
617
571
- def _parse_upload_error (self , error : rfapi .UploadError ) -> str :
572
- dict_part = str (error ).split (": " , 2 )[2 ]
573
- dict_part = dict_part .replace ("True" , "true" )
574
- dict_part = dict_part .replace ("False" , "false" )
575
- dict_part = dict_part .replace ("None" , "null" )
576
- if re .search (r"'\w+':" , dict_part ):
577
- temp_str = dict_part .replace (r"\'" , "<PLACEHOLDER>" )
578
- temp_str = temp_str .replace ('"' , r"\"" )
579
- temp_str = temp_str .replace ("'" , '"' )
580
- dict_part = temp_str .replace ("<PLACEHOLDER>" , "'" )
581
- parsed_dict : dict = json .loads (dict_part )
582
- message = parsed_dict .get ("message" )
583
- return message or str (parsed_dict )
584
-
585
618
def search (
586
619
self ,
587
620
like_image : Optional [str ] = None ,
0 commit comments