Skip to content

Commit 169c02d

Browse files
authored
Merge pull request #7 from robmarkcole/development
Adds tests
2 parents 79f8f37 + c562c0c commit 169c02d

File tree

6 files changed

+162
-34
lines changed

6 files changed

+162
-34
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,9 @@
22
[![PyPI Version](https://img.shields.io/pypi/v/deepstack-python.svg)](https://pypi.org/project/deepstack-python/)
33

44
# deepstack-python
5-
Unofficial python API for DeepStack
5+
Unofficial python API for DeepStack. Provides class for making requests to the object detection endpoint, and functions for processing the result. See the `usage.ipynb` notebook for usage.
6+
7+
## Development
8+
* Use `venv`
9+
* `pip install -r requirements-dev.txt`
10+
* Run tests with `venv/bin/pytest tests/*`

deepstack/core.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77

88
## Const
99
HTTP_OK = 200
10-
HTTP_BAD_REQUEST = 400
11-
HTTP_UNAUTHORIZED = 401
1210
DEFAULT_TIMEOUT = 10 # seconds
1311

1412

@@ -26,12 +24,12 @@ def get_confidences_above_threshold(
2624
return [val for val in confidences if val >= confidence_threshold]
2725

2826

29-
def get_object_labels(predictions: List[Dict]) -> Set[str]:
27+
def get_object_labels(predictions: List[Dict]) -> List[str]:
3028
"""
3129
Get a list of the unique object labels predicted.
3230
"""
3331
labels = [pred["label"] for pred in predictions]
34-
return set(labels)
32+
return list(set(labels))
3533

3634

3735
def get_label_confidences(predictions: List[Dict], target_label: str):

requirements-dev.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pytest
2+
black
3+
requests_mock

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
requests
2+
pillow

tests/test_deepstack.py

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,95 @@
1-
# Placeholder
1+
# Placeholder
2+
3+
import deepstack.core as ds
4+
import requests
5+
import requests_mock
6+
import pytest
7+
8+
MOCK_IP_ADDRESS = "localhost"
9+
MOCK_PORT = 5000
10+
MOCK_URL = "http://{}:{}/v1/vision/detection".format(MOCK_IP_ADDRESS, MOCK_PORT)
11+
12+
MOCK_BYTES = b"Test"
13+
MOCK_API_KEY = "mock_api_key"
14+
MOCK_TIMEOUT = 8
15+
16+
MOCK_RESPONSE = {
17+
"success": True,
18+
"predictions": [
19+
{
20+
"confidence": 0.6998661,
21+
"label": "person",
22+
"y_min": 0,
23+
"x_min": 258,
24+
"y_max": 676,
25+
"x_max": 485,
26+
},
27+
{
28+
"confidence": 0.7996547,
29+
"label": "person",
30+
"y_min": 0,
31+
"x_min": 405,
32+
"y_max": 652,
33+
"x_max": 639,
34+
},
35+
{
36+
"confidence": 0.59745613,
37+
"label": "dog",
38+
"y_min": 311,
39+
"x_min": 624,
40+
"y_max": 591,
41+
"x_max": 825,
42+
},
43+
],
44+
}
45+
46+
MOCK_PREDICTIONS = MOCK_RESPONSE["predictions"]
47+
MOCK_CONFIDENCES = [0.6998661, 0.7996547]
48+
CONFIDENCE_THRESHOLD = 0.7
49+
50+
51+
def test_DeepstackObject_process_image_bytes():
52+
"""Test a good response from server."""
53+
with requests_mock.Mocker() as mock_req:
54+
mock_req.post(MOCK_URL, status_code=ds.HTTP_OK, json=MOCK_RESPONSE)
55+
56+
dsobject = ds.DeepstackObject(MOCK_IP_ADDRESS, MOCK_PORT)
57+
dsobject.process_image_bytes(MOCK_BYTES)
58+
assert dsobject.predictions == MOCK_PREDICTIONS
59+
60+
61+
def test_DeepstackObject_process_image_bytes_timeout():
62+
"""Test a timeout. THIS SHOULD FAIL"""
63+
with pytest.raises(ds.DeepstackException) as excinfo:
64+
with requests_mock.Mocker() as mock_req:
65+
mock_req.post(MOCK_URL, exc=requests.exceptions.ConnectTimeout)
66+
dsobject = ds.DeepstackObject(MOCK_IP_ADDRESS, MOCK_PORT)
67+
dsobject.process_image_bytes(MOCK_BYTES)
68+
assert False
69+
assert "SHOULD FAIL" in str(excinfo.value)
70+
71+
72+
def test_get_object_labels():
73+
"""Cant always be sure order of returned list items."""
74+
object_labels = ds.get_object_labels(MOCK_PREDICTIONS)
75+
assert type(object_labels) is list
76+
assert "dog" in object_labels
77+
assert "person" in object_labels
78+
assert len(object_labels) == 2
79+
80+
81+
def test_get_objects_summary():
82+
objects_summary = ds.get_objects_summary(MOCK_PREDICTIONS)
83+
assert objects_summary == {"dog": 1, "person": 2}
84+
85+
86+
def test_get_label_confidences():
87+
label_confidences = ds.get_label_confidences(MOCK_PREDICTIONS, "person")
88+
assert label_confidences == MOCK_CONFIDENCES
89+
90+
91+
def test_get_confidences_above_threshold():
92+
assert (
93+
len(ds.get_confidences_above_threshold(MOCK_CONFIDENCES, CONFIDENCE_THRESHOLD))
94+
== 1
95+
)

usage.ipynb

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,21 @@
44
"cell_type": "markdown",
55
"metadata": {},
66
"source": [
7-
"This package provides convenience classes and functions for working with deepstack object detection API"
7+
"This package provides convenience classes and functions for working with deepstack object detection API. \n",
8+
"Pull the latest image for noavx:\n",
9+
"```\n",
10+
"docker pull deepquestai/deepstack:noavx\n",
11+
"```\n",
12+
"\n",
13+
"Run deepstack with:\n",
14+
"```\n",
15+
"docker run -e VISION-DETECTION=True -e VISION-FACE=True -e MODE=High -d \\\n",
16+
" -v localstorage:/datastore -p 5000:5000 \\\n",
17+
" -e API-KEY=\"Mysecretkey\" \\\n",
18+
" --name deepstack deepquestai/deepstack:noavx\n",
19+
"```\n",
20+
"\n",
21+
"Note that by default, the minimum confidence for detected objects is 0.45"
822
]
923
},
1024
{
@@ -22,20 +36,19 @@
2236
},
2337
{
2438
"cell_type": "code",
25-
"execution_count": 6,
39+
"execution_count": 2,
2640
"metadata": {},
2741
"outputs": [],
2842
"source": [
2943
"IP_ADDRESS = 'localhost'\n",
3044
"PORT = '5000'\n",
3145
"API_KEY = \"Mysecretkey\"\n",
32-
"# API_KEY = \"BadKey\"\n",
3346
"TIMEOUT = 8"
3447
]
3548
},
3649
{
3750
"cell_type": "code",
38-
"execution_count": 7,
51+
"execution_count": 3,
3952
"metadata": {},
4053
"outputs": [],
4154
"source": [
@@ -44,7 +57,7 @@
4457
},
4558
{
4659
"cell_type": "code",
47-
"execution_count": 8,
60+
"execution_count": 4,
4861
"metadata": {},
4962
"outputs": [
5063
{
@@ -75,7 +88,7 @@
7588
},
7689
{
7790
"cell_type": "code",
78-
"execution_count": 9,
91+
"execution_count": 5,
7992
"metadata": {},
8093
"outputs": [
8194
{
@@ -104,7 +117,7 @@
104117
},
105118
{
106119
"cell_type": "code",
107-
"execution_count": 10,
120+
"execution_count": 6,
108121
"metadata": {},
109122
"outputs": [
110123
{
@@ -130,7 +143,7 @@
130143
" 'x_max': 825}]"
131144
]
132145
},
133-
"execution_count": 10,
146+
"execution_count": 6,
134147
"metadata": {},
135148
"output_type": "execute_result"
136149
}
@@ -149,7 +162,7 @@
149162
},
150163
{
151164
"cell_type": "code",
152-
"execution_count": 11,
165+
"execution_count": 7,
153166
"metadata": {},
154167
"outputs": [],
155168
"source": [
@@ -165,16 +178,16 @@
165178
},
166179
{
167180
"cell_type": "code",
168-
"execution_count": 12,
181+
"execution_count": 8,
169182
"metadata": {},
170183
"outputs": [
171184
{
172185
"data": {
173186
"text/plain": [
174-
"{'dog', 'person'}"
187+
"['person', 'dog']"
175188
]
176189
},
177-
"execution_count": 12,
190+
"execution_count": 8,
178191
"metadata": {},
179192
"output_type": "execute_result"
180193
}
@@ -187,12 +200,12 @@
187200
"cell_type": "markdown",
188201
"metadata": {},
189202
"source": [
190-
"Get a summary of the number of occurances of labels, and confidence level"
203+
"Get a summary of the number of occurances of labels"
191204
]
192205
},
193206
{
194207
"cell_type": "code",
195-
"execution_count": 13,
208+
"execution_count": 9,
196209
"metadata": {},
197210
"outputs": [
198211
{
@@ -201,13 +214,34 @@
201214
"{'person': 2, 'dog': 1}"
202215
]
203216
},
204-
"execution_count": 13,
217+
"execution_count": 9,
218+
"metadata": {},
219+
"output_type": "execute_result"
220+
}
221+
],
222+
"source": [
223+
"summary = ds.get_objects_summary(dsobject.predictions)\n",
224+
"summary"
225+
]
226+
},
227+
{
228+
"cell_type": "code",
229+
"execution_count": 10,
230+
"metadata": {},
231+
"outputs": [
232+
{
233+
"data": {
234+
"text/plain": [
235+
"['person', 'dog']"
236+
]
237+
},
238+
"execution_count": 10,
205239
"metadata": {},
206240
"output_type": "execute_result"
207241
}
208242
],
209243
"source": [
210-
"ds.get_objects_summary(dsobject.predictions)"
244+
"list(summary.keys())"
211245
]
212246
},
213247
{
@@ -219,7 +253,7 @@
219253
},
220254
{
221255
"cell_type": "code",
222-
"execution_count": 14,
256+
"execution_count": 11,
223257
"metadata": {},
224258
"outputs": [
225259
{
@@ -228,7 +262,7 @@
228262
"[0.9998661, 0.9996547]"
229263
]
230264
},
231-
"execution_count": 14,
265+
"execution_count": 11,
232266
"metadata": {},
233267
"output_type": "execute_result"
234268
}
@@ -247,17 +281,9 @@
247281
},
248282
{
249283
"cell_type": "code",
250-
"execution_count": 15,
284+
"execution_count": null,
251285
"metadata": {},
252-
"outputs": [
253-
{
254-
"name": "stdout",
255-
"output_type": "stream",
256-
"text": [
257-
"1\n"
258-
]
259-
}
260-
],
286+
"outputs": [],
261287
"source": [
262288
"CONFIDENCE_THRESHOLD = 0.9997\n",
263289
"print(len(ds.get_confidences_above_threshold(confidences, CONFIDENCE_THRESHOLD)))"

0 commit comments

Comments
 (0)