PyTurboJPEG 2.2.0__tar.gz → 2.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.
- {pyturbojpeg-2.2.0 → pyturbojpeg-2.4.0}/PKG-INFO +8 -1
- {pyturbojpeg-2.2.0 → pyturbojpeg-2.4.0}/PyTurboJPEG.egg-info/PKG-INFO +8 -1
- {pyturbojpeg-2.2.0 → pyturbojpeg-2.4.0}/README.md +7 -0
- {pyturbojpeg-2.2.0 → pyturbojpeg-2.4.0}/setup.py +1 -1
- {pyturbojpeg-2.2.0 → pyturbojpeg-2.4.0}/tests/test_turbojpeg.py +11 -0
- {pyturbojpeg-2.2.0 → pyturbojpeg-2.4.0}/turbojpeg.py +58 -18
- {pyturbojpeg-2.2.0 → pyturbojpeg-2.4.0}/LICENSE +0 -0
- {pyturbojpeg-2.2.0 → pyturbojpeg-2.4.0}/PyTurboJPEG.egg-info/SOURCES.txt +0 -0
- {pyturbojpeg-2.2.0 → pyturbojpeg-2.4.0}/PyTurboJPEG.egg-info/dependency_links.txt +0 -0
- {pyturbojpeg-2.2.0 → pyturbojpeg-2.4.0}/PyTurboJPEG.egg-info/requires.txt +0 -0
- {pyturbojpeg-2.2.0 → pyturbojpeg-2.4.0}/PyTurboJPEG.egg-info/top_level.txt +0 -0
- {pyturbojpeg-2.2.0 → pyturbojpeg-2.4.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyTurboJPEG
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: A Python wrapper of libjpeg-turbo for decoding and encoding JPEG image.
|
|
5
5
|
Home-page: https://github.com/lilohuang/PyTurboJPEG
|
|
6
6
|
Author: Lilo Huang
|
|
@@ -179,6 +179,13 @@ with open('input.jpg', 'rb') as f:
|
|
|
179
179
|
cropped_data = jpeg.crop(f.read(), 8, 8, 320, 240)
|
|
180
180
|
with open('cropped_output.jpg', 'wb') as f:
|
|
181
181
|
f.write(cropped_data)
|
|
182
|
+
|
|
183
|
+
# Lossless Huffman table optimization (re-encodes with optimal tables,
|
|
184
|
+
# identical pixels; typically smaller unless already optimized)
|
|
185
|
+
with open('input.jpg', 'rb') as f:
|
|
186
|
+
optimized_data = jpeg.optimize(f.read())
|
|
187
|
+
with open('optimized_output.jpg', 'wb') as f:
|
|
188
|
+
f.write(optimized_data)
|
|
182
189
|
```
|
|
183
190
|
|
|
184
191
|
### In-Place Operations
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyTurboJPEG
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: A Python wrapper of libjpeg-turbo for decoding and encoding JPEG image.
|
|
5
5
|
Home-page: https://github.com/lilohuang/PyTurboJPEG
|
|
6
6
|
Author: Lilo Huang
|
|
@@ -179,6 +179,13 @@ with open('input.jpg', 'rb') as f:
|
|
|
179
179
|
cropped_data = jpeg.crop(f.read(), 8, 8, 320, 240)
|
|
180
180
|
with open('cropped_output.jpg', 'wb') as f:
|
|
181
181
|
f.write(cropped_data)
|
|
182
|
+
|
|
183
|
+
# Lossless Huffman table optimization (re-encodes with optimal tables,
|
|
184
|
+
# identical pixels; typically smaller unless already optimized)
|
|
185
|
+
with open('input.jpg', 'rb') as f:
|
|
186
|
+
optimized_data = jpeg.optimize(f.read())
|
|
187
|
+
with open('optimized_output.jpg', 'wb') as f:
|
|
188
|
+
f.write(optimized_data)
|
|
182
189
|
```
|
|
183
190
|
|
|
184
191
|
### In-Place Operations
|
|
@@ -155,6 +155,13 @@ with open('input.jpg', 'rb') as f:
|
|
|
155
155
|
cropped_data = jpeg.crop(f.read(), 8, 8, 320, 240)
|
|
156
156
|
with open('cropped_output.jpg', 'wb') as f:
|
|
157
157
|
f.write(cropped_data)
|
|
158
|
+
|
|
159
|
+
# Lossless Huffman table optimization (re-encodes with optimal tables,
|
|
160
|
+
# identical pixels; typically smaller unless already optimized)
|
|
161
|
+
with open('input.jpg', 'rb') as f:
|
|
162
|
+
optimized_data = jpeg.optimize(f.read())
|
|
163
|
+
with open('optimized_output.jpg', 'wb') as f:
|
|
164
|
+
f.write(optimized_data)
|
|
158
165
|
```
|
|
159
166
|
|
|
160
167
|
### In-Place Operations
|
|
@@ -2,7 +2,7 @@ import io
|
|
|
2
2
|
from setuptools import setup, find_packages
|
|
3
3
|
setup(
|
|
4
4
|
name='PyTurboJPEG',
|
|
5
|
-
version='2.
|
|
5
|
+
version='2.4.0',
|
|
6
6
|
description='A Python wrapper of libjpeg-turbo for decoding and encoding JPEG image.',
|
|
7
7
|
author='Lilo Huang',
|
|
8
8
|
author_email='kuso.cc@gmail.com',
|
|
@@ -513,6 +513,17 @@ class TestCropMultiple:
|
|
|
513
513
|
assert subsample == TJSAMP_GRAY
|
|
514
514
|
|
|
515
515
|
|
|
516
|
+
class TestOptimize:
|
|
517
|
+
"""Test optimize function."""
|
|
518
|
+
|
|
519
|
+
def test_optimize_is_lossless(self, jpeg_instance, encoded_sample_jpeg):
|
|
520
|
+
"""Test optimization returns a valid JPEG with unchanged pixels."""
|
|
521
|
+
optimized = jpeg_instance.optimize(encoded_sample_jpeg)
|
|
522
|
+
original_pixels = jpeg_instance.decode(encoded_sample_jpeg)
|
|
523
|
+
optimized_pixels = jpeg_instance.decode(optimized)
|
|
524
|
+
assert np.array_equal(original_pixels, optimized_pixels)
|
|
525
|
+
|
|
526
|
+
|
|
516
527
|
class TestBufferSize:
|
|
517
528
|
"""Test buffer_size function."""
|
|
518
529
|
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
# SOFTWARE.
|
|
24
24
|
|
|
25
25
|
__author__ = 'Lilo Huang <kuso.cc@gmail.com>'
|
|
26
|
-
__version__ = '2.
|
|
26
|
+
__version__ = '2.4.0'
|
|
27
27
|
|
|
28
28
|
from ctypes import *
|
|
29
29
|
from ctypes.util import find_library
|
|
@@ -125,6 +125,7 @@ TJXOPT_GRAY = 8
|
|
|
125
125
|
TJXOPT_NOOUTPUT = 16
|
|
126
126
|
TJXOPT_PROGRESSIVE = 32
|
|
127
127
|
TJXOPT_COPYNONE = 64
|
|
128
|
+
TJXOPT_OPTIMIZE = 256 # Huffman table optimization
|
|
128
129
|
|
|
129
130
|
# pixel size
|
|
130
131
|
# see details in https://github.com/libjpeg-turbo/libjpeg-turbo/blob/main/src/turbojpeg.h
|
|
@@ -305,12 +306,12 @@ def fill_background(coeffs_ptr, arrayRegion, planeRegion, componentID, transform
|
|
|
305
306
|
min(arrayRegion.y+arrayRegion.h, background_data.h)
|
|
306
307
|
- arrayRegion.y
|
|
307
308
|
)
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
309
|
+
y_start = left_start_row // MCU_HEIGHT
|
|
310
|
+
y_end = left_end_row // MCU_HEIGHT
|
|
311
|
+
x_start = background_data.w // MCU_WIDTH
|
|
312
|
+
x_end = planeRegion.w // MCU_WIDTH
|
|
313
|
+
if y_end > y_start and x_end > x_start:
|
|
314
|
+
coeffs[y_start:y_end, x_start:x_end, 0] = background_data.lum
|
|
314
315
|
|
|
315
316
|
# fill mcus under image
|
|
316
317
|
bottom_start_row = (
|
|
@@ -320,12 +321,11 @@ def fill_background(coeffs_ptr, arrayRegion, planeRegion, componentID, transform
|
|
|
320
321
|
max(arrayRegion.y+arrayRegion.h, background_data.h)
|
|
321
322
|
- arrayRegion.y
|
|
322
323
|
)
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
coeffs[y][x][0] = background_data.lum
|
|
324
|
+
y_start = bottom_start_row // MCU_HEIGHT
|
|
325
|
+
y_end = bottom_end_row // MCU_HEIGHT
|
|
326
|
+
x_end = planeRegion.w // MCU_WIDTH
|
|
327
|
+
if y_end > y_start and x_end > 0:
|
|
328
|
+
coeffs[y_start:y_end, 0:x_end, 0] = background_data.lum
|
|
329
329
|
|
|
330
330
|
return 1
|
|
331
331
|
|
|
@@ -1311,6 +1311,8 @@ class TurboJPEG(object):
|
|
|
1311
1311
|
|
|
1312
1312
|
# Define crop transforms from cropping_regions
|
|
1313
1313
|
crop_transforms = (TransformStruct * number_of_operations)()
|
|
1314
|
+
# Pre-compute luminance coefficient once for all crops
|
|
1315
|
+
lum_coefficient = None
|
|
1314
1316
|
for i, crop_region in enumerate(crop_regions):
|
|
1315
1317
|
# The fill_background callback is slow, only use it if needed
|
|
1316
1318
|
if self.__need_fill_background(
|
|
@@ -1318,14 +1320,16 @@ class TurboJPEG(object):
|
|
|
1318
1320
|
(image_width, image_height),
|
|
1319
1321
|
background_luminance
|
|
1320
1322
|
):
|
|
1323
|
+
if lum_coefficient is None:
|
|
1324
|
+
lum_coefficient = self.__map_luminance_to_dc_dct_coefficient(
|
|
1325
|
+
bytearray(jpeg_buf),
|
|
1326
|
+
background_luminance
|
|
1327
|
+
)
|
|
1321
1328
|
# Use callback to fill in background post-transform
|
|
1322
1329
|
callback_data = BackgroundStruct(
|
|
1323
1330
|
image_width,
|
|
1324
1331
|
image_height,
|
|
1325
|
-
|
|
1326
|
-
bytearray(jpeg_buf),
|
|
1327
|
-
background_luminance
|
|
1328
|
-
)
|
|
1332
|
+
lum_coefficient
|
|
1329
1333
|
)
|
|
1330
1334
|
callback = CUSTOMFILTER(fill_background)
|
|
1331
1335
|
crop_transforms[i] = TransformStruct(
|
|
@@ -1348,9 +1352,45 @@ class TurboJPEG(object):
|
|
|
1348
1352
|
finally:
|
|
1349
1353
|
self.__destroy(handle)
|
|
1350
1354
|
|
|
1355
|
+
def optimize(self, jpeg_buf, copynone=False):
|
|
1356
|
+
"""Losslessly optimize the Huffman tables of a jpeg image.
|
|
1357
|
+
|
|
1358
|
+
Re-encodes the entropy-coded data with optimal Huffman tables
|
|
1359
|
+
(equivalent to ``jpegtran -optimize``) without any loss in image
|
|
1360
|
+
quality. Typically reduces file size, unless the input is already
|
|
1361
|
+
optimized.
|
|
1362
|
+
|
|
1363
|
+
Parameters
|
|
1364
|
+
----------
|
|
1365
|
+
jpeg_buf: bytes
|
|
1366
|
+
Input jpeg image.
|
|
1367
|
+
copynone: bool
|
|
1368
|
+
True = do not copy EXIF data (False by default)
|
|
1369
|
+
|
|
1370
|
+
Returns
|
|
1371
|
+
----------
|
|
1372
|
+
bytes
|
|
1373
|
+
Huffman-optimized jpeg image.
|
|
1374
|
+
"""
|
|
1375
|
+
handle = self.__init(TJINIT_TRANSFORM)
|
|
1376
|
+
try:
|
|
1377
|
+
jpeg_array = np.frombuffer(jpeg_buf, dtype=np.uint8)
|
|
1378
|
+
src_addr = self.__getaddr(jpeg_array)
|
|
1379
|
+
status = self.__decompress_header(handle, src_addr, jpeg_array.size)
|
|
1380
|
+
if status != 0:
|
|
1381
|
+
self.__report_error(handle)
|
|
1382
|
+
# Without TJXOPT_CROP the cropping region is ignored and the whole
|
|
1383
|
+
# image is transformed, so the (zeroed) region needs no setup.
|
|
1384
|
+
transforms = (TransformStruct * 1)()
|
|
1385
|
+
transforms[0].op = TJXOP_NONE
|
|
1386
|
+
transforms[0].options = TJXOPT_OPTIMIZE | (copynone and TJXOPT_COPYNONE)
|
|
1387
|
+
return self.__do_transform(handle, src_addr, jpeg_array.size, 1, transforms)[0]
|
|
1388
|
+
|
|
1389
|
+
finally:
|
|
1390
|
+
self.__destroy(handle)
|
|
1391
|
+
|
|
1351
1392
|
def buffer_size(self, img_array, jpeg_subsample=TJSAMP_422):
|
|
1352
1393
|
"""Get maximum number of bytes of compressed jpeg data"""
|
|
1353
|
-
img_array = np.ascontiguousarray(img_array)
|
|
1354
1394
|
height, width = img_array.shape[:2]
|
|
1355
1395
|
return self.__buffer_size(width, height, jpeg_subsample)
|
|
1356
1396
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|