useknockout 0.2.0__tar.gz → 0.4.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: useknockout
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Python SDK for useknockout — state-of-the-art background removal API.
5
5
  Project-URL: Homepage, https://useknockout.com
6
6
  Project-URL: Documentation, https://github.com/useknockout/python
@@ -128,6 +128,7 @@ c.outline("photo.jpg", outline_color="#000000", outline_width=4)
128
128
 
129
129
  # Presets
130
130
  c.studio_shot("photo.jpg", aspect="1:1", shadow=True) # e-commerce
131
+ c.studio_shot("photo.jpg", transparent=True) # transparent bg (PNG)
131
132
  c.headshot("photo.jpg", bg_color="#FFFFFF") # LinkedIn 4:5 portrait
132
133
  c.headshot("photo.jpg", bg_blur=True, blur_radius=24) # blurred original bg
133
134
 
@@ -96,6 +96,7 @@ c.outline("photo.jpg", outline_color="#000000", outline_width=4)
96
96
 
97
97
  # Presets
98
98
  c.studio_shot("photo.jpg", aspect="1:1", shadow=True) # e-commerce
99
+ c.studio_shot("photo.jpg", transparent=True) # transparent bg (PNG)
99
100
  c.headshot("photo.jpg", bg_color="#FFFFFF") # LinkedIn 4:5 portrait
100
101
  c.headshot("photo.jpg", bg_blur=True, blur_radius=24) # blurred original bg
101
102
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "useknockout"
7
- version = "0.2.0"
7
+ version = "0.4.0"
8
8
  description = "Python SDK for useknockout — state-of-the-art background removal API."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -0,0 +1 @@
1
+ __version__ = "0.4.0"
@@ -65,9 +65,12 @@ class AsyncKnockout:
65
65
  files=None,
66
66
  data=None,
67
67
  json=None,
68
+ params=None,
68
69
  ) -> bytes:
69
70
  try:
70
- r = await self._http.request(method, path, files=files, data=data, json=json)
71
+ r = await self._http.request(
72
+ method, path, files=files, data=data, json=json, params=params
73
+ )
71
74
  except httpx.RequestError as e:
72
75
  raise KnockoutError(f"network error: {e}", code="unknown") from e
73
76
  if r.status_code >= 400:
@@ -82,9 +85,12 @@ class AsyncKnockout:
82
85
  files=None,
83
86
  data=None,
84
87
  json=None,
88
+ params=None,
85
89
  ) -> Any:
86
90
  try:
87
- r = await self._http.request(method, path, files=files, data=data, json=json)
91
+ r = await self._http.request(
92
+ method, path, files=files, data=data, json=json, params=params
93
+ )
88
94
  except httpx.RequestError as e:
89
95
  raise KnockoutError(f"network error: {e}", code="unknown") from e
90
96
  if r.status_code >= 400:
@@ -101,11 +107,12 @@ class AsyncKnockout:
101
107
  return await self._request_json("GET", "/stats")
102
108
 
103
109
  async def remove(self, file: FileInput, *, format: str = "png") -> bytes:
110
+ # /remove reads `format` as a QUERY param (bare default in the API).
104
111
  return await self._request_bytes(
105
112
  "POST",
106
113
  "/remove",
107
114
  files=_multipart_files(file),
108
- data=_form({"format": format}),
115
+ params={"format": format},
109
116
  )
110
117
 
111
118
  async def remove_url(self, url: str, *, format: str = "png") -> bytes:
@@ -138,11 +145,12 @@ class AsyncKnockout:
138
145
  ) -> Dict[str, Any]:
139
146
  if len(files) > 10:
140
147
  raise ValueError("max 10 files per batch")
148
+ # /remove-batch reads `format` as a QUERY param (bare default in the API).
141
149
  return await self._request_json(
142
150
  "POST",
143
151
  "/remove-batch",
144
152
  files=_multipart_batch(files),
145
- data=_form({"format": format}),
153
+ params={"format": format},
146
154
  )
147
155
 
148
156
  async def remove_batch_url(self, urls: List[str], *, format: str = "png") -> Dict[str, Any]:
@@ -250,6 +258,7 @@ class AsyncKnockout:
250
258
  aspect: str = "1:1",
251
259
  padding: int = 48,
252
260
  shadow: bool = True,
261
+ transparent: bool = False,
253
262
  format: str = "jpg",
254
263
  ) -> bytes:
255
264
  return await self._request_bytes(
@@ -262,6 +271,7 @@ class AsyncKnockout:
262
271
  "aspect": aspect,
263
272
  "padding": padding,
264
273
  "shadow": shadow,
274
+ "transparent": transparent,
265
275
  "format": format,
266
276
  }
267
277
  ),
@@ -355,13 +365,20 @@ class AsyncKnockout:
355
365
  file: FileInput,
356
366
  *,
357
367
  only_center_face: bool = False,
368
+ bg_enhance: bool = False,
358
369
  format: str = "png",
359
370
  ) -> bytes:
360
371
  return await self._request_bytes(
361
372
  "POST",
362
373
  "/face-restore",
363
374
  files=_multipart_files(file),
364
- data=_form({"only_center_face": only_center_face, "format": format}),
375
+ data=_form(
376
+ {
377
+ "only_center_face": only_center_face,
378
+ "bg_enhance": bg_enhance,
379
+ "format": format,
380
+ }
381
+ ),
365
382
  )
366
383
 
367
384
  async def colorize(
@@ -75,9 +75,12 @@ class Knockout:
75
75
  files=None,
76
76
  data=None,
77
77
  json=None,
78
+ params=None,
78
79
  ) -> bytes:
79
80
  try:
80
- r = self._http.request(method, path, files=files, data=data, json=json)
81
+ r = self._http.request(
82
+ method, path, files=files, data=data, json=json, params=params
83
+ )
81
84
  except httpx.RequestError as e:
82
85
  raise KnockoutError(f"network error: {e}", code="unknown") from e
83
86
  if r.status_code >= 400:
@@ -92,9 +95,12 @@ class Knockout:
92
95
  files=None,
93
96
  data=None,
94
97
  json=None,
98
+ params=None,
95
99
  ) -> Any:
96
100
  try:
97
- r = self._http.request(method, path, files=files, data=data, json=json)
101
+ r = self._http.request(
102
+ method, path, files=files, data=data, json=json, params=params
103
+ )
98
104
  except httpx.RequestError as e:
99
105
  raise KnockoutError(f"network error: {e}", code="unknown") from e
100
106
  if r.status_code >= 400:
@@ -116,11 +122,13 @@ class Knockout:
116
122
 
117
123
  def remove(self, file: FileInput, *, format: str = "png") -> bytes:
118
124
  """POST /remove — remove background, return transparent PNG/WebP bytes."""
125
+ # /remove reads `format` as a QUERY param (bare default in the API), unlike
126
+ # the Form()-based endpoints. Sending it in the body would be ignored.
119
127
  return self._request_bytes(
120
128
  "POST",
121
129
  "/remove",
122
130
  files=_multipart_files(file),
123
- data=_form({"format": format}),
131
+ params={"format": format},
124
132
  )
125
133
 
126
134
  def remove_url(self, url: str, *, format: str = "png") -> bytes:
@@ -156,11 +164,12 @@ class Knockout:
156
164
  """POST /remove-batch — up to 10 multipart uploads in one call."""
157
165
  if len(files) > 10:
158
166
  raise ValueError("max 10 files per batch")
167
+ # /remove-batch reads `format` as a QUERY param (bare default in the API).
159
168
  return self._request_json(
160
169
  "POST",
161
170
  "/remove-batch",
162
171
  files=_multipart_batch(files),
163
- data=_form({"format": format}),
172
+ params={"format": format},
164
173
  )
165
174
 
166
175
  def remove_batch_url(self, urls: List[str], *, format: str = "png") -> Dict[str, Any]:
@@ -274,9 +283,14 @@ class Knockout:
274
283
  aspect: str = "1:1",
275
284
  padding: int = 48,
276
285
  shadow: bool = True,
286
+ transparent: bool = False,
277
287
  format: str = "jpg",
278
288
  ) -> bytes:
279
- """POST /studio-shot — e-commerce preset (cutout + crop + center + shadow)."""
289
+ """POST /studio-shot — e-commerce preset (cutout + crop + center + shadow).
290
+
291
+ Set ``transparent=True`` to keep a transparent background (bg_color and
292
+ shadow are ignored; output is PNG, jpg is coerced).
293
+ """
280
294
  return self._request_bytes(
281
295
  "POST",
282
296
  "/studio-shot",
@@ -287,6 +301,7 @@ class Knockout:
287
301
  "aspect": aspect,
288
302
  "padding": padding,
289
303
  "shadow": shadow,
304
+ "transparent": transparent,
290
305
  "format": format,
291
306
  }
292
307
  ),
@@ -390,11 +405,13 @@ class Knockout:
390
405
  file: FileInput,
391
406
  *,
392
407
  only_center_face: bool = False,
408
+ bg_enhance: bool = False,
393
409
  format: str = "png",
394
410
  ) -> bytes:
395
411
  """POST /face-restore — GFPGAN v1.4 portrait restoration.
396
412
 
397
- Fixes blurry / damaged / low-res faces; background is 2x upscaled by
413
+ Fixes blurry / damaged / low-res faces. By default the background is
414
+ preserved as-is. ``bg_enhance=True`` also upscales the background 2x via
398
415
  Real-ESRGAN. ``only_center_face=True`` restores just the most prominent
399
416
  face (faster).
400
417
  """
@@ -402,7 +419,13 @@ class Knockout:
402
419
  "POST",
403
420
  "/face-restore",
404
421
  files=_multipart_files(file),
405
- data=_form({"only_center_face": only_center_face, "format": format}),
422
+ data=_form(
423
+ {
424
+ "only_center_face": only_center_face,
425
+ "bg_enhance": bg_enhance,
426
+ "format": format,
427
+ }
428
+ ),
406
429
  )
407
430
 
408
431
  def colorize(
@@ -1 +0,0 @@
1
- __version__ = "0.2.0"
File without changes
File without changes