PyTurboJPEG 1.8.3__tar.gz → 2.1.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.
- pyturbojpeg-2.1.0/PKG-INFO +371 -0
- pyturbojpeg-2.1.0/PyTurboJPEG.egg-info/PKG-INFO +371 -0
- {pyturbojpeg-1.8.3 → pyturbojpeg-2.1.0}/PyTurboJPEG.egg-info/SOURCES.txt +2 -1
- pyturbojpeg-2.1.0/README.md +347 -0
- {pyturbojpeg-1.8.3 → pyturbojpeg-2.1.0}/setup.py +1 -1
- pyturbojpeg-2.1.0/tests/test_turbojpeg.py +1806 -0
- {pyturbojpeg-1.8.3 → pyturbojpeg-2.1.0}/turbojpeg.py +711 -173
- pyturbojpeg-1.8.3/PKG-INFO +0 -232
- pyturbojpeg-1.8.3/PyTurboJPEG.egg-info/PKG-INFO +0 -232
- pyturbojpeg-1.8.3/README.md +0 -218
- {pyturbojpeg-1.8.3 → pyturbojpeg-2.1.0}/LICENSE +0 -0
- {pyturbojpeg-1.8.3 → pyturbojpeg-2.1.0}/PyTurboJPEG.egg-info/dependency_links.txt +0 -0
- {pyturbojpeg-1.8.3 → pyturbojpeg-2.1.0}/PyTurboJPEG.egg-info/requires.txt +0 -0
- {pyturbojpeg-1.8.3 → pyturbojpeg-2.1.0}/PyTurboJPEG.egg-info/top_level.txt +0 -0
- {pyturbojpeg-1.8.3 → pyturbojpeg-2.1.0}/setup.cfg +0 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PyTurboJPEG
|
|
3
|
+
Version: 2.1.0
|
|
4
|
+
Summary: A Python wrapper of libjpeg-turbo for decoding and encoding JPEG image.
|
|
5
|
+
Home-page: https://github.com/lilohuang/PyTurboJPEG
|
|
6
|
+
Author: Lilo Huang
|
|
7
|
+
Author-email: kuso.cc@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: numpy
|
|
12
|
+
Provides-Extra: test
|
|
13
|
+
Requires-Dist: pytest>=7.0.0; extra == "test"
|
|
14
|
+
Dynamic: author
|
|
15
|
+
Dynamic: author-email
|
|
16
|
+
Dynamic: description
|
|
17
|
+
Dynamic: description-content-type
|
|
18
|
+
Dynamic: home-page
|
|
19
|
+
Dynamic: license
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
Dynamic: provides-extra
|
|
22
|
+
Dynamic: requires-dist
|
|
23
|
+
Dynamic: summary
|
|
24
|
+
|
|
25
|
+
# PyTurboJPEG
|
|
26
|
+
|
|
27
|
+
A Python wrapper for libjpeg-turbo that enables efficient JPEG image decoding and encoding.
|
|
28
|
+
|
|
29
|
+
## Prerequisites
|
|
30
|
+
|
|
31
|
+
- [libjpeg-turbo](https://github.com/libjpeg-turbo/libjpeg-turbo/releases) **3.0 or later** (required for PyTurboJPEG 2.0+)
|
|
32
|
+
- [numpy](https://github.com/numpy/numpy)
|
|
33
|
+
|
|
34
|
+
**Important:** PyTurboJPEG 2.0+ requires libjpeg-turbo 3.0 or later as it uses the new function-based TurboJPEG 3 API. For libjpeg-turbo 2.x compatibility, please use PyTurboJPEG 1.x.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
### macOS
|
|
39
|
+
```bash
|
|
40
|
+
brew install jpeg-turbo
|
|
41
|
+
pip install -U git+https://github.com/lilohuang/PyTurboJPEG.git
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Windows
|
|
45
|
+
1. Download the [libjpeg-turbo official installer](https://github.com/libjpeg-turbo/libjpeg-turbo/releases)
|
|
46
|
+
2. Install PyTurboJPEG:
|
|
47
|
+
```bash
|
|
48
|
+
pip install -U git+https://github.com/lilohuang/PyTurboJPEG.git
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Linux
|
|
52
|
+
1. Download the [libjpeg-turbo official installer](https://github.com/libjpeg-turbo/libjpeg-turbo/releases)
|
|
53
|
+
2. Install PyTurboJPEG:
|
|
54
|
+
```bash
|
|
55
|
+
pip install -U git+https://github.com/lilohuang/PyTurboJPEG.git
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Basic Usage
|
|
59
|
+
|
|
60
|
+
### Initialization
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from turbojpeg import TurboJPEG
|
|
64
|
+
|
|
65
|
+
# Use default library installation
|
|
66
|
+
jpeg = TurboJPEG()
|
|
67
|
+
|
|
68
|
+
# Or specify library path explicitly
|
|
69
|
+
# jpeg = TurboJPEG(r'D:\turbojpeg.dll') # Windows
|
|
70
|
+
# jpeg = TurboJPEG('/usr/lib64/libturbojpeg.so') # Linux
|
|
71
|
+
# jpeg = TurboJPEG('/usr/local/lib/libturbojpeg.dylib') # macOS
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Decoding
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
import cv2
|
|
78
|
+
from turbojpeg import TurboJPEG, TJPF_GRAY, TJFLAG_FASTUPSAMPLE, TJFLAG_FASTDCT
|
|
79
|
+
|
|
80
|
+
jpeg = TurboJPEG()
|
|
81
|
+
|
|
82
|
+
# Basic decoding to BGR array
|
|
83
|
+
with open('input.jpg', 'rb') as f:
|
|
84
|
+
bgr_array = jpeg.decode(f.read())
|
|
85
|
+
cv2.imshow('bgr_array', bgr_array)
|
|
86
|
+
cv2.waitKey(0)
|
|
87
|
+
|
|
88
|
+
# Fast decoding (lower accuracy, higher speed)
|
|
89
|
+
with open('input.jpg', 'rb') as f:
|
|
90
|
+
bgr_array = jpeg.decode(f.read(), flags=TJFLAG_FASTUPSAMPLE|TJFLAG_FASTDCT)
|
|
91
|
+
|
|
92
|
+
# Decode with direct rescaling (1/2 size)
|
|
93
|
+
with open('input.jpg', 'rb') as f:
|
|
94
|
+
bgr_array_half = jpeg.decode(f.read(), scaling_factor=(1, 2))
|
|
95
|
+
|
|
96
|
+
# Get available scaling factors
|
|
97
|
+
scaling_factors = jpeg.scaling_factors
|
|
98
|
+
|
|
99
|
+
# Decode to grayscale
|
|
100
|
+
with open('input.jpg', 'rb') as f:
|
|
101
|
+
gray_array = jpeg.decode(f.read(), pixel_format=TJPF_GRAY)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Decoding Header Information
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
# Get image properties without full decoding (backward compatible)
|
|
108
|
+
with open('input.jpg', 'rb') as f:
|
|
109
|
+
width, height, jpeg_subsample, jpeg_colorspace = jpeg.decode_header(f.read())
|
|
110
|
+
|
|
111
|
+
# Get precision to select appropriate decode function
|
|
112
|
+
with open('input.jpg', 'rb') as f:
|
|
113
|
+
jpeg_data = f.read()
|
|
114
|
+
width, height, jpeg_subsample, jpeg_colorspace, precision = jpeg.decode_header(jpeg_data, return_precision=True)
|
|
115
|
+
|
|
116
|
+
# Use precision to select appropriate decode function
|
|
117
|
+
if precision == 8:
|
|
118
|
+
img = jpeg.decode(jpeg_data)
|
|
119
|
+
elif precision == 12:
|
|
120
|
+
img = jpeg.decode_12bit(jpeg_data)
|
|
121
|
+
elif precision == 16:
|
|
122
|
+
img = jpeg.decode_16bit(jpeg_data)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### YUV Decoding
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
# Decode to YUV buffer
|
|
129
|
+
with open('input.jpg', 'rb') as f:
|
|
130
|
+
buffer_array, plane_sizes = jpeg.decode_to_yuv(f.read())
|
|
131
|
+
|
|
132
|
+
# Decode to YUV planes
|
|
133
|
+
with open('input.jpg', 'rb') as f:
|
|
134
|
+
planes = jpeg.decode_to_yuv_planes(f.read())
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Encoding
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from turbojpeg import TJSAMP_GRAY, TJFLAG_PROGRESSIVE
|
|
141
|
+
|
|
142
|
+
# Basic encoding with default settings
|
|
143
|
+
with open('output.jpg', 'wb') as f:
|
|
144
|
+
f.write(jpeg.encode(bgr_array))
|
|
145
|
+
|
|
146
|
+
# Encode with grayscale subsample
|
|
147
|
+
with open('output_gray.jpg', 'wb') as f:
|
|
148
|
+
f.write(jpeg.encode(bgr_array, jpeg_subsample=TJSAMP_GRAY))
|
|
149
|
+
|
|
150
|
+
# Encode with custom quality
|
|
151
|
+
with open('output_quality_50.jpg', 'wb') as f:
|
|
152
|
+
f.write(jpeg.encode(bgr_array, quality=50))
|
|
153
|
+
|
|
154
|
+
# Encode with progressive entropy coding
|
|
155
|
+
with open('output_progressive.jpg', 'wb') as f:
|
|
156
|
+
f.write(jpeg.encode(bgr_array, quality=100, flags=TJFLAG_PROGRESSIVE))
|
|
157
|
+
|
|
158
|
+
# Encode with lossless JPEG compression
|
|
159
|
+
with open('output_gray.jpg', 'wb') as f:
|
|
160
|
+
f.write(jpeg.encode(bgr_array, lossless=True))
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Advanced Operations
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
# Scale with quality (without color conversion)
|
|
167
|
+
with open('input.jpg', 'rb') as f:
|
|
168
|
+
scaled_data = jpeg.scale_with_quality(f.read(), scaling_factor=(1, 4), quality=70)
|
|
169
|
+
with open('scaled_output.jpg', 'wb') as f:
|
|
170
|
+
f.write(scaled_data)
|
|
171
|
+
|
|
172
|
+
# Lossless crop
|
|
173
|
+
with open('input.jpg', 'rb') as f:
|
|
174
|
+
cropped_data = jpeg.crop(f.read(), 8, 8, 320, 240)
|
|
175
|
+
with open('cropped_output.jpg', 'wb') as f:
|
|
176
|
+
f.write(cropped_data)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### In-Place Operations
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
import numpy as np
|
|
183
|
+
|
|
184
|
+
# In-place decoding (reuse existing array)
|
|
185
|
+
img_array = np.empty((640, 480, 3), dtype=np.uint8)
|
|
186
|
+
with open('input.jpg', 'rb') as f:
|
|
187
|
+
result = jpeg.decode(f.read(), dst=img_array)
|
|
188
|
+
# result is the same as img_array: id(result) == id(img_array)
|
|
189
|
+
|
|
190
|
+
# In-place encoding (reuse existing buffer)
|
|
191
|
+
buffer_size = jpeg.buffer_size(img_array)
|
|
192
|
+
dest_buf = bytearray(buffer_size)
|
|
193
|
+
result, n_bytes = jpeg.encode(img_array, dst=dest_buf)
|
|
194
|
+
with open('output.jpg', 'wb') as f:
|
|
195
|
+
f.write(dest_buf[:n_bytes])
|
|
196
|
+
# result is the same as dest_buf: id(result) == id(dest_buf)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### EXIF Orientation Handling
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
import cv2
|
|
203
|
+
import numpy as np
|
|
204
|
+
import exifread
|
|
205
|
+
from turbojpeg import TurboJPEG
|
|
206
|
+
|
|
207
|
+
def transpose_image(image, orientation):
|
|
208
|
+
"""Transpose image based on EXIF Orientation tag.
|
|
209
|
+
|
|
210
|
+
See: https://www.exif.org/Exif2-2.PDF
|
|
211
|
+
"""
|
|
212
|
+
if orientation is None:
|
|
213
|
+
return image
|
|
214
|
+
|
|
215
|
+
val = orientation.values[0]
|
|
216
|
+
if val == 1: return image
|
|
217
|
+
elif val == 2: return np.fliplr(image)
|
|
218
|
+
elif val == 3: return np.rot90(image, 2)
|
|
219
|
+
elif val == 4: return np.flipud(image)
|
|
220
|
+
elif val == 5: return np.rot90(np.flipud(image), -1)
|
|
221
|
+
elif val == 6: return np.rot90(image, -1)
|
|
222
|
+
elif val == 7: return np.rot90(np.flipud(image))
|
|
223
|
+
elif val == 8: return np.rot90(image)
|
|
224
|
+
|
|
225
|
+
jpeg = TurboJPEG()
|
|
226
|
+
|
|
227
|
+
with open('foobar.jpg', 'rb') as f:
|
|
228
|
+
# Parse EXIF orientation
|
|
229
|
+
orientation = exifread.process_file(f).get('Image Orientation', None)
|
|
230
|
+
|
|
231
|
+
# Decode image
|
|
232
|
+
f.seek(0)
|
|
233
|
+
image = jpeg.decode(f.read())
|
|
234
|
+
|
|
235
|
+
# Apply orientation transformation
|
|
236
|
+
transposed_image = transpose_image(image, orientation)
|
|
237
|
+
|
|
238
|
+
cv2.imshow('transposed_image', transposed_image)
|
|
239
|
+
cv2.waitKey(0)
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## High-Precision JPEG Support
|
|
243
|
+
|
|
244
|
+
PyTurboJPEG 2.0+ supports 12-bit and 16-bit precision JPEG encoding and decoding using libjpeg-turbo 3.0+ APIs. This feature is ideal for medical imaging, scientific photography, and other applications requiring higher bit depth.
|
|
245
|
+
|
|
246
|
+
**Requirements:**
|
|
247
|
+
- libjpeg-turbo 3.0 or later (12-bit and 16-bit support is built-in)
|
|
248
|
+
|
|
249
|
+
**Precision Modes:**
|
|
250
|
+
- **12-bit JPEG:** Supports both lossy and lossless compression
|
|
251
|
+
- **16-bit JPEG:** Only supports lossless compression (JPEG standard limitation)
|
|
252
|
+
|
|
253
|
+
### 12-bit JPEG (Lossy)
|
|
254
|
+
|
|
255
|
+
12-bit JPEG provides higher precision than standard 8-bit JPEG while maintaining compatibility with lossy compression.
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
import numpy as np
|
|
259
|
+
from turbojpeg import TurboJPEG
|
|
260
|
+
|
|
261
|
+
jpeg = TurboJPEG()
|
|
262
|
+
|
|
263
|
+
# Create 12-bit image (values range from 0 to 4095)
|
|
264
|
+
img_12bit = np.random.randint(0, 4096, (480, 640, 3), dtype=np.uint16)
|
|
265
|
+
|
|
266
|
+
# Encode to 12-bit lossy JPEG
|
|
267
|
+
jpeg_data = jpeg.encode_12bit(img_12bit, quality=95)
|
|
268
|
+
|
|
269
|
+
# Decode from 12-bit JPEG
|
|
270
|
+
decoded_img = jpeg.decode_12bit(jpeg_data)
|
|
271
|
+
|
|
272
|
+
# Save to file
|
|
273
|
+
with open('output_12bit.jpg', 'wb') as f:
|
|
274
|
+
f.write(jpeg_data)
|
|
275
|
+
|
|
276
|
+
# Load from file
|
|
277
|
+
with open('output_12bit.jpg', 'rb') as f:
|
|
278
|
+
decoded_from_file = jpeg.decode_12bit(f.read())
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### 16-bit JPEG (Lossless)
|
|
282
|
+
|
|
283
|
+
16-bit JPEG provides the highest precision with perfect reconstruction through lossless compression. The JPEG standard only supports 16-bit for lossless mode.
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
import numpy as np
|
|
287
|
+
from turbojpeg import TurboJPEG
|
|
288
|
+
|
|
289
|
+
jpeg = TurboJPEG()
|
|
290
|
+
|
|
291
|
+
# Create 16-bit image (values range from 0 to 65535)
|
|
292
|
+
img_16bit = np.random.randint(0, 65536, (480, 640, 3), dtype=np.uint16)
|
|
293
|
+
|
|
294
|
+
# Encode to 16-bit lossless JPEG
|
|
295
|
+
jpeg_data = jpeg.encode_16bit(img_16bit)
|
|
296
|
+
|
|
297
|
+
# Decode from 16-bit lossless JPEG
|
|
298
|
+
decoded_img = jpeg.decode_16bit(jpeg_data)
|
|
299
|
+
|
|
300
|
+
# Verify perfect reconstruction (lossless)
|
|
301
|
+
assert np.array_equal(img_16bit, decoded_img) # True
|
|
302
|
+
|
|
303
|
+
# Save to file
|
|
304
|
+
with open('output_16bit_lossless.jpg', 'wb') as f:
|
|
305
|
+
f.write(jpeg_data)
|
|
306
|
+
|
|
307
|
+
# Load from file
|
|
308
|
+
with open('output_16bit_lossless.jpg', 'rb') as f:
|
|
309
|
+
decoded_from_file = jpeg.decode_16bit(f.read())
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Lossless JPEG for 12-bit and 16-bit
|
|
313
|
+
|
|
314
|
+
12-bit and 16-bit JPEG support lossless compression for perfect reconstruction:
|
|
315
|
+
|
|
316
|
+
#### 12-bit Lossless JPEG
|
|
317
|
+
|
|
318
|
+
12-bit precision with lossless compression:
|
|
319
|
+
|
|
320
|
+
```python
|
|
321
|
+
import numpy as np
|
|
322
|
+
from turbojpeg import TurboJPEG
|
|
323
|
+
|
|
324
|
+
jpeg = TurboJPEG()
|
|
325
|
+
|
|
326
|
+
# Create 12-bit image
|
|
327
|
+
img_12bit = np.random.randint(0, 4096, (480, 640, 3), dtype=np.uint16)
|
|
328
|
+
|
|
329
|
+
# Encode to 12-bit lossless JPEG using encode_12bit() with lossless=True
|
|
330
|
+
jpeg_data = jpeg.encode_12bit(img_12bit, lossless=True)
|
|
331
|
+
|
|
332
|
+
# Decode using decode_12bit()
|
|
333
|
+
decoded_img = jpeg.decode_12bit(jpeg_data)
|
|
334
|
+
|
|
335
|
+
# Perfect reconstruction
|
|
336
|
+
assert np.array_equal(img_12bit, decoded_img) # True
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Medical and Scientific Imaging
|
|
340
|
+
|
|
341
|
+
For medical and scientific applications, 12-bit JPEG provides excellent precision while maintaining file size efficiency:
|
|
342
|
+
|
|
343
|
+
```python
|
|
344
|
+
import numpy as np
|
|
345
|
+
from turbojpeg import TurboJPEG, TJPF_GRAY, TJSAMP_GRAY
|
|
346
|
+
|
|
347
|
+
jpeg = TurboJPEG()
|
|
348
|
+
|
|
349
|
+
# Create 12-bit medical image (e.g., DICOM format)
|
|
350
|
+
# Medical images typically use 0-4095 range
|
|
351
|
+
medical_img = np.random.randint(0, 4096, (512, 512, 1), dtype=np.uint16)
|
|
352
|
+
|
|
353
|
+
# Encode with highest quality for medical applications
|
|
354
|
+
jpeg_medical = jpeg.encode_12bit(
|
|
355
|
+
medical_img,
|
|
356
|
+
pixel_format=TJPF_GRAY,
|
|
357
|
+
jpeg_subsample=TJSAMP_GRAY,
|
|
358
|
+
quality=100
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
# Decode for analysis
|
|
362
|
+
decoded_medical = jpeg.decode_12bit(jpeg_medical, pixel_format=TJPF_GRAY)
|
|
363
|
+
|
|
364
|
+
# Verify value range preservation
|
|
365
|
+
print(f"Original range: [{medical_img.min()}, {medical_img.max()}]")
|
|
366
|
+
print(f"Decoded range: [{decoded_medical.min()}, {decoded_medical.max()}]")
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## License
|
|
370
|
+
|
|
371
|
+
See the LICENSE file for details.
|