PyTurboJPEG 2.0.0__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.
@@ -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.