0
# Image Transformations
1
2
Lossless transformation operations for JPEG images including cropping, scaling with quality adjustment, multiple crop operations with background filling, and advanced transform operations that work directly in the compressed domain.
3
4
## Capabilities
5
6
### Single Crop Operation
7
8
Perform lossless crop operations on JPEG images without decompression/recompression quality loss.
9
10
```python { .api }
11
def crop(
12
jpeg_buf: bytes,
13
x: int, y: int, w: int, h: int,
14
preserve: bool = False,
15
gray: bool = False,
16
copynone: bool = False
17
) -> bytes:
18
"""
19
Losslessly crop a JPEG image.
20
21
Args:
22
jpeg_buf: Input JPEG data as bytes
23
x: Crop origin X coordinate (must be MCU-aligned unless preserve=True)
24
y: Crop origin Y coordinate (must be MCU-aligned unless preserve=True)
25
w: Crop width in pixels
26
h: Crop height in pixels
27
preserve: If True, preserve exact crop boundaries (slower)
28
gray: Convert to grayscale during crop
29
copynone: Do not copy EXIF metadata
30
31
Returns:
32
bytes: Cropped JPEG data
33
34
Raises:
35
OSError: If crop request is invalid or coordinates are not MCU-aligned
36
"""
37
```
38
39
### Multiple Crop Operations
40
41
Perform multiple crop and/or extension operations in a single call with optional background filling.
42
43
```python { .api }
44
def crop_multiple(
45
jpeg_buf: bytes,
46
crop_parameters: list[tuple[int, int, int, int]],
47
background_luminance: float = 1.0,
48
gray: bool = False,
49
copynone: bool = False
50
) -> list[bytes]:
51
"""
52
Lossless crop and/or extension operations on JPEG image.
53
54
Args:
55
jpeg_buf: Input JPEG data as bytes
56
crop_parameters: List of (x, y, w, h) tuples for each crop operation
57
background_luminance: Luminance level (0-1) for background fill when extending
58
gray: Produce grayscale output
59
copynone: Do not copy EXIF metadata
60
61
Returns:
62
list[bytes]: List of cropped/extended JPEG images
63
64
Notes:
65
- Crop origins must be divisible by MCU block size
66
- Extensions beyond image boundaries filled with background_luminance color
67
- background_luminance: 0=black, 0.5=gray, 1=white
68
"""
69
```
70
71
### Scale with Quality
72
73
Scale JPEG images while adjusting quality, combining decompression to YUV, scaling, and recompression in one operation.
74
75
```python { .api }
76
def scale_with_quality(
77
jpeg_buf: bytes,
78
scaling_factor: tuple[int, int] | None = None,
79
quality: int = 85,
80
flags: int = 0
81
) -> bytes:
82
"""
83
Scale JPEG image and re-encode with specified quality.
84
85
Args:
86
jpeg_buf: Input JPEG data as bytes
87
scaling_factor: Scaling as (numerator, denominator) tuple
88
quality: Output JPEG quality (1-100)
89
flags: Processing flags (TJFLAG_* constants)
90
91
Returns:
92
bytes: Scaled and re-encoded JPEG data
93
94
Notes:
95
- Avoids color conversion step for efficiency
96
- Useful for creating thumbnails with size and quality control
97
"""
98
```
99
100
## Constants
101
102
### Transform Operations
103
104
```python { .api }
105
TJXOP_NONE: int # No transform
106
TJXOP_HFLIP: int # Horizontal flip
107
TJXOP_VFLIP: int # Vertical flip
108
TJXOP_TRANSPOSE: int # Transpose
109
TJXOP_TRANSVERSE: int # Transverse
110
TJXOP_ROT90: int # 90 degree rotation
111
TJXOP_ROT180: int # 180 degree rotation
112
TJXOP_ROT270: int # 270 degree rotation
113
```
114
115
### Transform Options
116
117
```python { .api }
118
TJXOPT_PERFECT: int # Perfect transform (require exact alignment)
119
TJXOPT_TRIM: int # Trim partial MCUs
120
TJXOPT_CROP: int # Crop operation
121
TJXOPT_GRAY: int # Convert to grayscale
122
TJXOPT_NOOUTPUT: int # No output (validation only)
123
TJXOPT_PROGRESSIVE: int # Progressive output
124
TJXOPT_COPYNONE: int # Copy no metadata
125
```
126
127
### MCU Block Sizes
128
129
MCU (Minimum Coded Unit) alignment requirements:
130
131
```python { .api }
132
tjMCUWidth: list[int] # MCU width for each subsampling type
133
tjMCUHeight: list[int] # MCU height for each subsampling type
134
135
# MCU sizes by subsampling:
136
# TJSAMP_444 (4:4:4): 8x8 pixels
137
# TJSAMP_422 (4:2:2): 16x8 pixels
138
# TJSAMP_420 (4:2:0): 16x16 pixels
139
# TJSAMP_GRAY: 8x8 pixels
140
# TJSAMP_440 (4:4:0): 8x16 pixels
141
# TJSAMP_411 (4:1:1): 32x8 pixels
142
```
143
144
## Structure Definitions
145
146
### Cropping Region
147
148
```python { .api }
149
class CroppingRegion(Structure):
150
"""Defines a rectangular cropping region."""
151
_fields_ = [
152
("x", c_int), # X coordinate of crop origin
153
("y", c_int), # Y coordinate of crop origin
154
("w", c_int), # Width of crop region
155
("h", c_int) # Height of crop region
156
]
157
```
158
159
### Scaling Factor
160
161
```python { .api }
162
class ScalingFactor(Structure):
163
"""Defines a scaling factor as numerator/denominator."""
164
_fields_ = [
165
("num", c_int), # Numerator
166
("denom", c_int) # Denominator
167
]
168
```
169
170
### Background Structure
171
172
```python { .api }
173
class BackgroundStruct(Structure):
174
"""Background fill parameters for image extension operations."""
175
_fields_ = [
176
("w", c_int), # Input image width
177
("h", c_int), # Input image height
178
("lum", c_int) # Luminance value for background fill
179
]
180
```
181
182
### Transform Structure
183
184
```python { .api }
185
class TransformStruct(Structure):
186
"""Complete transform operation definition."""
187
_fields_ = [
188
("r", CroppingRegion), # Crop region
189
("op", c_int), # Transform operation (TJXOP_*)
190
("options", c_int), # Transform options (TJXOPT_*)
191
("data", POINTER(BackgroundStruct)), # Background data for extensions
192
("customFilter", CUSTOMFILTER) # Custom filter function
193
]
194
```
195
196
## Usage Examples
197
198
### Basic Cropping
199
200
```python
201
from turbojpeg import TurboJPEG
202
203
jpeg = TurboJPEG()
204
205
# Load JPEG image
206
with open('input.jpg', 'rb') as f:
207
jpeg_data = f.read()
208
209
# Get image info for crop planning
210
width, height, subsample, _ = jpeg.decode_header(jpeg_data)
211
print(f"Original: {width}x{height}, subsampling: {subsample}")
212
213
# Crop from (100, 100) with size 300x200
214
cropped = jpeg.crop(jpeg_data, 100, 100, 300, 200)
215
216
# Save cropped image
217
with open('cropped.jpg', 'wb') as f:
218
f.write(cropped)
219
```
220
221
### MCU-Aligned Cropping
222
223
```python
224
# For 4:2:0 subsampling, MCU is 16x16 pixels
225
# Crop coordinates should be multiples of 16 for perfect alignment
226
227
# MCU-aligned crop (fast, lossless)
228
aligned_crop = jpeg.crop(jpeg_data, 16, 32, 320, 240) # All multiples of 16
229
230
# Non-aligned crop with preserve=True (slower but exact)
231
exact_crop = jpeg.crop(jpeg_data, 15, 33, 318, 238, preserve=True)
232
```
233
234
### Multiple Crops
235
236
```python
237
# Define multiple crop regions
238
crop_regions = [
239
(0, 0, 100, 100), # Top-left corner
240
(100, 0, 100, 100), # Top-right corner
241
(0, 100, 100, 100), # Bottom-left corner
242
(100, 100, 100, 100), # Bottom-right corner
243
]
244
245
# Perform all crops in one operation
246
crops = jpeg.crop_multiple(jpeg_data, crop_regions)
247
248
# Save each crop
249
for i, crop_data in enumerate(crops):
250
with open(f'crop_{i}.jpg', 'wb') as f:
251
f.write(crop_data)
252
```
253
254
### Extended Crops with Background
255
256
```python
257
# Get original image dimensions
258
width, height, _, _ = jpeg.decode_header(jpeg_data)
259
260
# Define crops that extend beyond image boundaries
261
extended_crops = [
262
(0, 0, width + 50, height + 50), # Extend right and bottom
263
(-25, -25, width + 50, height + 50), # Extend all sides
264
]
265
266
# White background fill (luminance = 1.0)
267
white_bg_crops = jpeg.crop_multiple(
268
jpeg_data,
269
extended_crops,
270
background_luminance=1.0
271
)
272
273
# Gray background fill (luminance = 0.5)
274
gray_bg_crops = jpeg.crop_multiple(
275
jpeg_data,
276
extended_crops,
277
background_luminance=0.5
278
)
279
280
# Black background fill (luminance = 0.0)
281
black_bg_crops = jpeg.crop_multiple(
282
jpeg_data,
283
extended_crops,
284
background_luminance=0.0
285
)
286
```
287
288
### Grayscale Cropping
289
290
```python
291
# Convert to grayscale during crop
292
gray_crop = jpeg.crop(
293
jpeg_data,
294
100, 100, 200, 200,
295
gray=True
296
)
297
298
# Multiple grayscale crops
299
gray_crops = jpeg.crop_multiple(
300
jpeg_data,
301
[(0, 0, 100, 100), (100, 100, 100, 100)],
302
gray=True
303
)
304
```
305
306
### Scale with Quality
307
308
```python
309
# Scale to half size with quality 70
310
half_size = jpeg.scale_with_quality(
311
jpeg_data,
312
scaling_factor=(1, 2),
313
quality=70
314
)
315
316
# Scale to quarter size with high quality
317
quarter_size = jpeg.scale_with_quality(
318
jpeg_data,
319
scaling_factor=(1, 4),
320
quality=90
321
)
322
323
# Available scaling factors
324
print("Available scaling factors:", jpeg.scaling_factors)
325
# Common factors: (1,8), (1,4), (3,8), (1,2), (5,8), (3,4), (7,8), (1,1), (9,8), (5,4), (11,8), (3,2), (13,8), (7,4), (15,8), (2,1)
326
```
327
328
### Strip Metadata
329
330
```python
331
# Remove EXIF and other metadata during crop
332
no_metadata = jpeg.crop(
333
jpeg_data,
334
0, 0, width, height, # Full image crop
335
copynone=True # Strip all metadata
336
)
337
338
# Compare file sizes
339
print(f"Original size: {len(jpeg_data)} bytes")
340
print(f"No metadata size: {len(no_metadata)} bytes")
341
```
342
343
### Advanced Transform Example
344
345
```python
346
# Combine multiple operations
347
def create_thumbnail_gallery(jpeg_data, thumb_size=(150, 150)):
348
"""Create multiple thumbnail crops with different qualities."""
349
350
# Get image info
351
width, height, subsample, _ = jpeg.decode_header(jpeg_data)
352
353
# Calculate grid positions for thumbnails
354
thumb_w, thumb_h = thumb_size
355
cols = width // thumb_w
356
rows = height // thumb_h
357
358
crop_params = []
359
for row in range(rows):
360
for col in range(cols):
361
x = col * thumb_w
362
y = row * thumb_h
363
crop_params.append((x, y, thumb_w, thumb_h))
364
365
# Extract all thumbnails
366
thumbnails = jpeg.crop_multiple(jpeg_data, crop_params)
367
368
# Scale and adjust quality for web use
369
web_thumbs = []
370
for thumb in thumbnails:
371
web_thumb = jpeg.scale_with_quality(
372
thumb,
373
scaling_factor=(1, 2), # Half size
374
quality=60 # Web quality
375
)
376
web_thumbs.append(web_thumb)
377
378
return web_thumbs
379
380
# Usage
381
with open('large_image.jpg', 'rb') as f:
382
image_data = f.read()
383
384
thumbnails = create_thumbnail_gallery(image_data)
385
386
for i, thumb in enumerate(thumbnails):
387
with open(f'thumb_{i:03d}.jpg', 'wb') as f:
388
f.write(thumb)
389
```