dragoneye-python 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dragoneye_python-0.1.0 → dragoneye_python-0.2.0}/PKG-INFO +1 -1
- {dragoneye_python-0.1.0 → dragoneye_python-0.2.0}/dragoneye_python.egg-info/PKG-INFO +1 -1
- dragoneye_python-0.2.0/dragoneye_python.egg-info/SOURCES.txt +15 -0
- dragoneye_python-0.2.0/dragoneye_python.egg-info/top_level.txt +1 -0
- dragoneye_python-0.2.0/pyproject.toml +11 -0
- dragoneye_python-0.2.0/src/dragoneye/__init__.py +22 -0
- dragoneye_python-0.2.0/src/dragoneye/classification.py +117 -0
- dragoneye_python-0.2.0/src/dragoneye/client.py +18 -0
- dragoneye_python-0.2.0/src/dragoneye/types/__init__.py +0 -0
- dragoneye_python-0.2.0/src/dragoneye/types/common.py +26 -0
- dragoneye_python-0.2.0/src/dragoneye/types/image.py +12 -0
- dragoneye_python-0.1.0/dragoneye_python.egg-info/SOURCES.txt +0 -9
- dragoneye_python-0.1.0/dragoneye_python.egg-info/top_level.txt +0 -1
- dragoneye_python-0.1.0/pyproject.toml +0 -8
- {dragoneye_python-0.1.0 → dragoneye_python-0.2.0}/LICENSE +0 -0
- {dragoneye_python-0.1.0 → dragoneye_python-0.2.0}/README.md +0 -0
- {dragoneye_python-0.1.0 → dragoneye_python-0.2.0}/dragoneye_python.egg-info/dependency_links.txt +0 -0
- {dragoneye_python-0.1.0 → dragoneye_python-0.2.0}/dragoneye_python.egg-info/requires.txt +0 -0
- {dragoneye_python-0.1.0 → dragoneye_python-0.2.0}/requirements.txt +0 -0
- {dragoneye_python-0.1.0 → dragoneye_python-0.2.0}/setup.cfg +0 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
requirements.txt
|
|
5
|
+
dragoneye_python.egg-info/PKG-INFO
|
|
6
|
+
dragoneye_python.egg-info/SOURCES.txt
|
|
7
|
+
dragoneye_python.egg-info/dependency_links.txt
|
|
8
|
+
dragoneye_python.egg-info/requires.txt
|
|
9
|
+
dragoneye_python.egg-info/top_level.txt
|
|
10
|
+
src/dragoneye/__init__.py
|
|
11
|
+
src/dragoneye/classification.py
|
|
12
|
+
src/dragoneye/client.py
|
|
13
|
+
src/dragoneye/types/__init__.py
|
|
14
|
+
src/dragoneye/types/common.py
|
|
15
|
+
src/dragoneye/types/image.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dragoneye
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from .classification import (
|
|
2
|
+
Classification,
|
|
3
|
+
ClassificationObjectPrediction,
|
|
4
|
+
ClassificationPredictImageResponse,
|
|
5
|
+
ClassificationTraitRootPrediction,
|
|
6
|
+
)
|
|
7
|
+
from .client import Dragoneye
|
|
8
|
+
from .types.common import NormalizedBbox, TaxonID, TaxonPrediction, TaxonType
|
|
9
|
+
from .types.image import Image
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"Classification",
|
|
13
|
+
"ClassificationObjectPrediction",
|
|
14
|
+
"ClassificationPredictImageResponse",
|
|
15
|
+
"ClassificationTraitRootPrediction",
|
|
16
|
+
"Dragoneye",
|
|
17
|
+
"Image",
|
|
18
|
+
"NormalizedBbox",
|
|
19
|
+
"TaxonID",
|
|
20
|
+
"TaxonPrediction",
|
|
21
|
+
"TaxonType",
|
|
22
|
+
]
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import io
|
|
2
|
+
from typing import TYPE_CHECKING, BinaryIO, Sequence
|
|
3
|
+
|
|
4
|
+
import requests
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
from .types.common import BASE_API_URL, NormalizedBbox, TaxonID, TaxonPrediction
|
|
8
|
+
from .types.image import Image, assert_consistent_data_type
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .client import Dragoneye
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ClassificationTraitRootPrediction(BaseModel):
|
|
15
|
+
id: TaxonID
|
|
16
|
+
name: str
|
|
17
|
+
displayName: str
|
|
18
|
+
taxons: Sequence[TaxonPrediction]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ClassificationObjectPrediction(BaseModel):
|
|
22
|
+
normalizedBbox: NormalizedBbox
|
|
23
|
+
category: TaxonPrediction
|
|
24
|
+
traits: Sequence[ClassificationTraitRootPrediction]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ClassificationPredictImageResponse(BaseModel):
|
|
28
|
+
predictions: Sequence[ClassificationObjectPrediction]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ClassificationProductPrediction(BaseModel):
|
|
32
|
+
category: TaxonPrediction
|
|
33
|
+
traits: Sequence[ClassificationTraitRootPrediction]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ClassificationPredictProductResponse(BaseModel):
|
|
37
|
+
predictions: Sequence[ClassificationProductPrediction]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Classification:
|
|
41
|
+
def __init__(self, client: "Dragoneye"):
|
|
42
|
+
self._client = client
|
|
43
|
+
|
|
44
|
+
def predict(
|
|
45
|
+
self, image: Image, model_name: str
|
|
46
|
+
) -> ClassificationPredictImageResponse:
|
|
47
|
+
url = f"{BASE_API_URL}/predict"
|
|
48
|
+
|
|
49
|
+
files = {}
|
|
50
|
+
data = {}
|
|
51
|
+
|
|
52
|
+
if image.file_or_bytes is not None:
|
|
53
|
+
if isinstance(image.file_or_bytes, bytes):
|
|
54
|
+
files["image_file"] = io.BytesIO(image.file_or_bytes)
|
|
55
|
+
elif isinstance(image.file_or_bytes, BinaryIO): # pyright: ignore [reportUnnecessaryIsInstance]
|
|
56
|
+
files["image_file"] = image.file_or_bytes
|
|
57
|
+
else:
|
|
58
|
+
raise ValueError("Invalid image type: Must be bytes or BinaryIO")
|
|
59
|
+
elif image.url is not None:
|
|
60
|
+
data["image_url"] = image.url
|
|
61
|
+
else:
|
|
62
|
+
raise ValueError(
|
|
63
|
+
"Missing image: Either image file or image url must be specified"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
data["model_name"] = model_name
|
|
67
|
+
|
|
68
|
+
headers = {"Authorization": f"Bearer {self._client.api_key}"}
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
response = requests.post(url, files=files, data=data, headers=headers)
|
|
72
|
+
response.raise_for_status()
|
|
73
|
+
except requests.RequestException as error:
|
|
74
|
+
raise Exception(
|
|
75
|
+
"Error during Dragoneye Classification prediction request:", error
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
return ClassificationPredictImageResponse.model_validate(response.json())
|
|
79
|
+
|
|
80
|
+
def predict_product(
|
|
81
|
+
self, images: Sequence[Image], model_name: str
|
|
82
|
+
) -> ClassificationPredictProductResponse:
|
|
83
|
+
url = f"{BASE_API_URL}/predict-product"
|
|
84
|
+
|
|
85
|
+
files = []
|
|
86
|
+
data = {}
|
|
87
|
+
|
|
88
|
+
assert_consistent_data_type(images)
|
|
89
|
+
|
|
90
|
+
for image in images:
|
|
91
|
+
if image.file_or_bytes is not None:
|
|
92
|
+
if isinstance(image.file_or_bytes, bytes):
|
|
93
|
+
files.append(("image_file", io.BytesIO(image.file_or_bytes)))
|
|
94
|
+
elif (
|
|
95
|
+
isinstance(image.file_or_bytes, BinaryIO) # pyright: ignore [reportUnnecessaryIsInstance]
|
|
96
|
+
or issubclass(type(image.file_or_bytes), BinaryIO)
|
|
97
|
+
or isinstance(image.file_or_bytes, io.BufferedReader)
|
|
98
|
+
):
|
|
99
|
+
files.append(("image_file", image.file_or_bytes))
|
|
100
|
+
else:
|
|
101
|
+
raise ValueError("Invalid image type: Must be bytes or BinaryIO")
|
|
102
|
+
elif image.url is not None:
|
|
103
|
+
data.setdefault("image_urls", []).append(image.url)
|
|
104
|
+
|
|
105
|
+
data["model_name"] = model_name
|
|
106
|
+
|
|
107
|
+
headers = {"Authorization": f"Bearer {self._client.api_key}"}
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
response = requests.post(url, files=files, data=data, headers=headers)
|
|
111
|
+
response.raise_for_status()
|
|
112
|
+
except requests.RequestException as error:
|
|
113
|
+
raise Exception(
|
|
114
|
+
"Error during Dragoneye Classification prediction request:", error
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return ClassificationPredictProductResponse.model_validate(response.json())
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from dragoneye.classification import Classification
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Dragoneye:
|
|
8
|
+
def __init__(self, api_key: Optional[str] = None):
|
|
9
|
+
if api_key is None:
|
|
10
|
+
api_key = os.getenv("DRAGONEYE_API_KEY")
|
|
11
|
+
|
|
12
|
+
assert (
|
|
13
|
+
api_key is not None
|
|
14
|
+
), "API key is required - set the DRAGONEYE_API_KEY environment variable or pass it to the [Dragoneye] constructor"
|
|
15
|
+
|
|
16
|
+
self.api_key = api_key
|
|
17
|
+
|
|
18
|
+
self.classification = Classification(self)
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import NewType, Optional, Sequence, Tuple
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
NormalizedBbox = NewType("NormalizedBbox", Tuple[float, float, float, float])
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TaxonType(str, Enum):
|
|
10
|
+
CATEGORY = ("category",)
|
|
11
|
+
TRAIT = ("trait",)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
TaxonID = NewType("TaxonID", int)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TaxonPrediction(BaseModel):
|
|
18
|
+
id: TaxonID
|
|
19
|
+
type: TaxonType
|
|
20
|
+
name: str
|
|
21
|
+
displayName: str
|
|
22
|
+
score: Optional[float]
|
|
23
|
+
children: Sequence["TaxonPrediction"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
BASE_API_URL = "https://api.dragoneye.ai"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from typing import BinaryIO, NamedTuple, Optional, Sequence, Union
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Image(NamedTuple):
|
|
5
|
+
file_or_bytes: Optional[Union[bytes, BinaryIO]] = None
|
|
6
|
+
url: Optional[str] = None
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def assert_consistent_data_type(images: Sequence[Image]) -> None:
|
|
10
|
+
assert all(image.file_or_bytes is not None for image in images) ^ all(
|
|
11
|
+
image.url is not None for image in images
|
|
12
|
+
)
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
LICENSE
|
|
2
|
-
README.md
|
|
3
|
-
pyproject.toml
|
|
4
|
-
requirements.txt
|
|
5
|
-
dragoneye_python.egg-info/PKG-INFO
|
|
6
|
-
dragoneye_python.egg-info/SOURCES.txt
|
|
7
|
-
dragoneye_python.egg-info/dependency_links.txt
|
|
8
|
-
dragoneye_python.egg-info/requires.txt
|
|
9
|
-
dragoneye_python.egg-info/top_level.txt
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
File without changes
|
|
File without changes
|
{dragoneye_python-0.1.0 → dragoneye_python-0.2.0}/dragoneye_python.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|