PyTurboJPEG 2.3.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyTurboJPEG
3
- Version: 2.3.0
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.0
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.3.0',
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.3.0'
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
@@ -1351,6 +1352,43 @@ class TurboJPEG(object):
1351
1352
  finally:
1352
1353
  self.__destroy(handle)
1353
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
+
1354
1392
  def buffer_size(self, img_array, jpeg_subsample=TJSAMP_422):
1355
1393
  """Get maximum number of bytes of compressed jpeg data"""
1356
1394
  height, width = img_array.shape[:2]
File without changes
File without changes