0
# Image Operations and Transformations
1
2
Advanced image manipulation functions including automatic contrast adjustment, color space operations, geometric transformations, and specialized processing operations provided by the ImageOps module.
3
4
## Capabilities
5
6
### Automatic Image Enhancement
7
8
Functions for automatic image quality improvement and adjustment.
9
10
```python { .api }
11
def autocontrast(image, cutoff=0, ignore=None, mask=None, preserve_tone=False):
12
"""
13
Maximize image contrast by stretching histogram.
14
15
Parameters:
16
- image (Image): Input image
17
- cutoff (float | tuple): Percentage to cut from histogram ends (0-100)
18
- ignore (int | sequence): Pixel values to ignore in histogram
19
- mask (Image): Optional mask for selective processing
20
- preserve_tone (bool): Preserve image tone in Photoshop-like style
21
22
Returns:
23
Image: Auto-contrast adjusted image
24
"""
25
26
def equalize(image, mask=None):
27
"""
28
Equalize image histogram for improved contrast distribution.
29
30
Parameters:
31
- image (Image): Input image
32
- mask (Image): Optional mask for selective equalization
33
34
Returns:
35
Image: Histogram equalized image
36
"""
37
38
def posterize(image, bits):
39
"""
40
Reduce the number of bits for each color channel.
41
42
Parameters:
43
- image (Image): Input image
44
- bits (int): Number of bits to keep per channel (1-8)
45
46
Returns:
47
Image: Posterized image
48
"""
49
50
def solarize(image, threshold=128):
51
"""
52
Invert all pixel values above threshold (solarization effect).
53
54
Parameters:
55
- image (Image): Input image
56
- threshold (int): Threshold value (0-255)
57
58
Returns:
59
Image: Solarized image
60
"""
61
```
62
63
### Geometric Transformations and Sizing
64
65
Functions for resizing, fitting, and transforming images.
66
67
```python { .api }
68
def fit(image, size, method=3, bleed=0.0, centering=(0.5, 0.5)):
69
"""
70
Fit image within given size, cropping as needed.
71
72
Parameters:
73
- image (Image): Input image
74
- size (tuple): Target size as (width, height)
75
- method (int): Resampling method (Resampling.LANCZOS, etc.)
76
- bleed (float): Remove a border around fitted image (0.0-0.5)
77
- centering (tuple): Crop centering as (horizontal, vertical) (0.0-1.0)
78
79
Returns:
80
Image: Fitted image
81
"""
82
83
def contain(image, size, method=3):
84
"""
85
Resize image to fit within size while maintaining aspect ratio.
86
87
Parameters:
88
- image (Image): Input image
89
- size (tuple): Maximum size as (width, height)
90
- method (int): Resampling method
91
92
Returns:
93
Image: Contained image
94
"""
95
96
def cover(image, size, method=3):
97
"""
98
Resize image to cover the entire size, cropping if necessary.
99
100
Parameters:
101
- image (Image): Input image
102
- size (tuple): Target size as (width, height)
103
- method (int): Resampling method
104
105
Returns:
106
Image: Covered image (may be cropped)
107
"""
108
109
def pad(image, size, method=3, color=None, centering=(0.5, 0.5)):
110
"""
111
Resize and pad image to exact size.
112
113
Parameters:
114
- image (Image): Input image
115
- size (tuple): Target size as (width, height)
116
- method (int): Resampling method
117
- color (int | tuple | str): Padding color
118
- centering (tuple): Image centering in padded area
119
120
Returns:
121
Image: Padded image
122
"""
123
124
def scale(image, factor, resample=3):
125
"""
126
Scale image by a factor.
127
128
Parameters:
129
- image (Image): Input image
130
- factor (float): Scale factor (>1 enlarges, <1 shrinks)
131
- resample (int): Resampling method
132
133
Returns:
134
Image: Scaled image
135
"""
136
137
def crop(image, border=0):
138
"""
139
Remove border pixels from image.
140
141
Parameters:
142
- image (Image): Input image
143
- border (int | tuple): Border size to remove
144
145
Returns:
146
Image: Cropped image
147
"""
148
149
def expand(image, border=0, fill=0):
150
"""
151
Add border pixels around image.
152
153
Parameters:
154
- image (Image): Input image
155
- border (int | tuple): Border size to add
156
- fill (int | tuple | str): Border color
157
158
Returns:
159
Image: Expanded image
160
"""
161
```
162
163
### Color and Tone Operations
164
165
Functions for color manipulation and tone adjustment.
166
167
```python { .api }
168
def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoint=127):
169
"""
170
Colorize a grayscale image with specified colors.
171
172
Parameters:
173
- image (Image): Grayscale input image
174
- black (int | tuple | str): Color for black pixels
175
- white (int | tuple | str): Color for white pixels
176
- mid (int | tuple | str): Color for mid-tone pixels
177
- blackpoint (int): Pixel value mapped to black color (0-255)
178
- whitepoint (int): Pixel value mapped to white color (0-255)
179
- midpoint (int): Pixel value mapped to mid color (0-255)
180
181
Returns:
182
Image: Colorized image
183
"""
184
185
def grayscale(image):
186
"""
187
Convert image to grayscale using ITU-R 601-2 luma transform.
188
189
Parameters:
190
- image (Image): Input image
191
192
Returns:
193
Image: Grayscale image
194
"""
195
196
def invert(image):
197
"""
198
Invert image colors (negative effect).
199
200
Parameters:
201
- image (Image): Input image
202
203
Returns:
204
Image: Inverted image
205
"""
206
```
207
208
### Orientation and Flipping
209
210
Functions for rotating and flipping images.
211
212
```python { .api }
213
def flip(image):
214
"""
215
Flip image vertically (top to bottom).
216
217
Parameters:
218
- image (Image): Input image
219
220
Returns:
221
Image: Vertically flipped image
222
"""
223
224
def mirror(image):
225
"""
226
Flip image horizontally (left to right).
227
228
Parameters:
229
- image (Image): Input image
230
231
Returns:
232
Image: Horizontally flipped image
233
"""
234
235
def exif_transpose(image, in_place=False):
236
"""
237
Transpose image according to EXIF orientation data.
238
239
Parameters:
240
- image (Image): Input image with EXIF data
241
- in_place (bool): Modify image in place if True
242
243
Returns:
244
Image | None: Transposed image (or None if in_place=True)
245
"""
246
```
247
248
### Advanced Transformations
249
250
Functions for complex geometric transformations.
251
252
```python { .api }
253
def deform(image, deformer, resample=1):
254
"""
255
Deform image using a deformer object.
256
257
Parameters:
258
- image (Image): Input image
259
- deformer: Deformer object implementing getmesh() method
260
- resample (int): Resampling method
261
262
Returns:
263
Image: Deformed image
264
"""
265
```
266
267
## Usage Examples
268
269
### Automatic Image Enhancement Workflow
270
271
```python
272
from PIL import Image, ImageOps
273
274
def enhance_image_auto(image_path, save_path):
275
"""Automatically enhance an image using multiple operations."""
276
img = Image.open(image_path)
277
278
# Step 1: Auto contrast adjustment
279
enhanced = ImageOps.autocontrast(img, cutoff=2)
280
281
# Step 2: Equalize histogram for better distribution
282
enhanced = ImageOps.equalize(enhanced)
283
284
# Step 3: Convert to grayscale and back to remove color casts
285
if img.mode == "RGB":
286
gray = ImageOps.grayscale(enhanced)
287
# Apply colorization to remove color casts
288
enhanced = ImageOps.colorize(gray, black="black", white="white")
289
290
enhanced.save(save_path)
291
return enhanced
292
293
# Process an image
294
enhanced = enhance_image_auto("photo.jpg", "enhanced_photo.jpg")
295
```
296
297
### Professional Photo Resizing
298
299
```python
300
from PIL import Image, ImageOps
301
302
def resize_for_web(image_path, max_size=(800, 600), quality=85):
303
"""Resize image for web use with proper aspect ratio handling."""
304
img = Image.open(image_path)
305
306
# Method 1: Contain - fits within bounds
307
contained = ImageOps.contain(img, max_size, method=Image.Resampling.LANCZOS)
308
contained.save("web_contained.jpg", "JPEG", quality=quality, optimize=True)
309
310
# Method 2: Cover - fills entire area, may crop
311
covered = ImageOps.cover(img, max_size, method=Image.Resampling.LANCZOS)
312
covered.save("web_covered.jpg", "JPEG", quality=quality, optimize=True)
313
314
# Method 3: Fit with cropping control
315
fitted = ImageOps.fit(img, max_size, method=Image.Resampling.LANCZOS,
316
centering=(0.5, 0.4)) # Center horizontally, slightly above center vertically
317
fitted.save("web_fitted.jpg", "JPEG", quality=quality, optimize=True)
318
319
# Method 4: Pad to exact size
320
padded = ImageOps.pad(img, max_size, method=Image.Resampling.LANCZOS,
321
color="white", centering=(0.5, 0.5))
322
padded.save("web_padded.jpg", "JPEG", quality=quality, optimize=True)
323
324
resize_for_web("original_photo.jpg")
325
```
326
327
### Creative Color Effects
328
329
```python
330
from PIL import Image, ImageOps
331
332
def create_artistic_effects(image_path):
333
"""Create various artistic effects using ImageOps."""
334
img = Image.open(image_path)
335
336
# Convert to grayscale first for colorization effects
337
gray = ImageOps.grayscale(img)
338
339
# Sepia tone effect
340
sepia = ImageOps.colorize(gray, black="black", white="#FFF8DC", mid="#CD853F")
341
sepia.save("sepia_effect.jpg")
342
343
# Cool blue tone
344
cool = ImageOps.colorize(gray, black="#000080", white="#E6F3FF", mid="#4169E1")
345
cool.save("cool_effect.jpg")
346
347
# Warm orange tone
348
warm = ImageOps.colorize(gray, black="#2F1B14", white="#FFF8DC", mid="#FF8C00")
349
warm.save("warm_effect.jpg")
350
351
# High contrast black and white
352
high_contrast = ImageOps.autocontrast(gray, cutoff=5)
353
high_contrast.save("high_contrast.jpg")
354
355
# Posterized effect
356
posterized = ImageOps.posterize(img, bits=4)
357
posterized.save("posterized.jpg")
358
359
# Solarization effect
360
solarized = ImageOps.solarize(img, threshold=100)
361
solarized.save("solarized.jpg")
362
363
# Inverted colors (negative)
364
inverted = ImageOps.invert(img)
365
inverted.save("inverted.jpg")
366
367
create_artistic_effects("portrait.jpg")
368
```
369
370
### Batch Image Processing
371
372
```python
373
from PIL import Image, ImageOps
374
import os
375
import glob
376
377
def batch_process_images(input_dir, output_dir, target_size=(1024, 768)):
378
"""Batch process images in a directory."""
379
380
# Create output directory if it doesn't exist
381
os.makedirs(output_dir, exist_ok=True)
382
383
# Find all image files
384
image_extensions = ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.tiff"]
385
image_files = []
386
for ext in image_extensions:
387
image_files.extend(glob.glob(os.path.join(input_dir, ext)))
388
image_files.extend(glob.glob(os.path.join(input_dir, ext.upper())))
389
390
for image_path in image_files:
391
try:
392
filename = os.path.basename(image_path)
393
name, ext = os.path.splitext(filename)
394
395
# Open and process image
396
img = Image.open(image_path)
397
398
# Fix orientation based on EXIF
399
if hasattr(img, '_getexif') and img._getexif():
400
img = ImageOps.exif_transpose(img)
401
402
# Resize while maintaining aspect ratio
403
processed = ImageOps.contain(img, target_size, method=Image.Resampling.LANCZOS)
404
405
# Auto-enhance
406
processed = ImageOps.autocontrast(processed, cutoff=1)
407
408
# Save processed image
409
output_path = os.path.join(output_dir, f"{name}_processed{ext}")
410
if ext.lower() in ['.jpg', '.jpeg']:
411
processed.save(output_path, "JPEG", quality=90, optimize=True)
412
else:
413
processed.save(output_path)
414
415
print(f"Processed: {filename}")
416
417
except Exception as e:
418
print(f"Error processing {filename}: {e}")
419
420
# Process all images in a directory
421
batch_process_images("input_photos", "output_photos", target_size=(800, 600))
422
```
423
424
### Image Borders and Frames
425
426
```python
427
from PIL import Image, ImageOps
428
429
def add_decorative_border(image_path, border_size=20, border_color="white"):
430
"""Add decorative border to image."""
431
img = Image.open(image_path)
432
433
# Simple border
434
bordered = ImageOps.expand(img, border=border_size, fill=border_color)
435
bordered.save("simple_border.jpg")
436
437
# Fancy multi-layer border
438
# Inner border
439
bordered = ImageOps.expand(img, border=5, fill="black")
440
# Middle border
441
bordered = ImageOps.expand(bordered, border=15, fill="white")
442
# Outer border
443
bordered = ImageOps.expand(bordered, border=3, fill="gray")
444
bordered.save("fancy_border.jpg")
445
446
# Asymmetric border (different sizes for each side)
447
# Using crop with negative values adds border
448
width, height = img.size
449
expanded = Image.new(img.mode, (width + 100, height + 60), "lightgray")
450
expanded.paste(img, (50, 20)) # Offset creates border
451
expanded.save("asymmetric_border.jpg")
452
453
add_decorative_border("photo.jpg")
454
```
455
456
### Smart Cropping and Composition
457
458
```python
459
from PIL import Image, ImageOps
460
461
def smart_crop_portraits(image_path):
462
"""Demonstrate different cropping strategies for portraits."""
463
img = Image.open(image_path)
464
465
# Square crop for profile pictures - center on upper portion
466
square_size = min(img.size)
467
square = ImageOps.fit(img, (square_size, square_size),
468
method=Image.Resampling.LANCZOS,
469
centering=(0.5, 0.3)) # Center horizontally, upper third vertically
470
square.save("profile_square.jpg")
471
472
# 16:9 crop for social media
473
aspect_16_9 = (1920, 1080)
474
social = ImageOps.fit(img, aspect_16_9,
475
method=Image.Resampling.LANCZOS,
476
centering=(0.5, 0.4))
477
# Scale down to reasonable size
478
social = ImageOps.contain(social, (800, 450), method=Image.Resampling.LANCZOS)
479
social.save("social_media.jpg")
480
481
# 4:5 crop for Instagram
482
instagram_ratio = (800, 1000)
483
instagram = ImageOps.fit(img, instagram_ratio,
484
method=Image.Resampling.LANCZOS,
485
centering=(0.5, 0.35))
486
instagram.save("instagram_crop.jpg")
487
488
smart_crop_portraits("portrait.jpg")
489
```
490
491
### Orientation Handling
492
493
```python
494
from PIL import Image, ImageOps
495
496
def handle_image_orientation(image_path):
497
"""Properly handle image orientation using EXIF data."""
498
img = Image.open(image_path)
499
500
print(f"Original size: {img.size}")
501
print(f"Original mode: {img.mode}")
502
503
# Check for EXIF orientation data
504
if hasattr(img, '_getexif') and img._getexif():
505
exif = img._getexif()
506
if exif and 274 in exif: # 274 is the orientation tag
507
orientation = exif[274]
508
print(f"EXIF orientation: {orientation}")
509
510
# Fix orientation automatically
511
corrected = ImageOps.exif_transpose(img)
512
if corrected is not None:
513
print(f"Corrected size: {corrected.size}")
514
corrected.save("orientation_corrected.jpg")
515
else:
516
print("No orientation correction needed")
517
img.save("orientation_corrected.jpg")
518
519
# Manual orientation operations
520
flipped_v = ImageOps.flip(img) # Vertical flip
521
flipped_h = ImageOps.mirror(img) # Horizontal flip
522
523
flipped_v.save("flipped_vertical.jpg")
524
flipped_h.save("flipped_horizontal.jpg")
525
526
handle_image_orientation("photo_with_exif.jpg")
527
```
528
529
### Image Quality Enhancement Pipeline
530
531
```python
532
from PIL import Image, ImageOps
533
534
def enhance_image_quality(image_path, output_path):
535
"""Professional image enhancement pipeline."""
536
img = Image.open(image_path)
537
538
# Step 1: Fix orientation
539
img = ImageOps.exif_transpose(img) or img
540
541
# Step 2: Crop unwanted borders (remove 1% from each edge)
542
width, height = img.size
543
crop_amount = min(width, height) * 0.01
544
img = ImageOps.crop(img, border=int(crop_amount))
545
546
# Step 3: Gentle auto-contrast (preserve most of the original look)
547
img = ImageOps.autocontrast(img, cutoff=0.5)
548
549
# Step 4: Subtle histogram equalization on luminance only
550
if img.mode == "RGB":
551
# Convert to HSV, equalize V channel, convert back
552
import colorsys
553
# This is a simplified approach - in practice you'd use proper color space conversion
554
img = ImageOps.autocontrast(img, cutoff=1.0)
555
556
# Step 5: Resize to target resolution if needed
557
max_dimension = 2048
558
if max(img.size) > max_dimension:
559
img = ImageOps.contain(img, (max_dimension, max_dimension),
560
method=Image.Resampling.LANCZOS)
561
562
# Save with optimal quality
563
img.save(output_path, "JPEG", quality=95, optimize=True)
564
return img
565
566
# Enhance image quality
567
enhanced = enhance_image_quality("raw_photo.jpg", "enhanced_photo.jpg")
568
```