如何用Python实现Photoshop式色阶调整(黑场值设为13)
Absolutely! You can totally replicate Photoshop's Levels adjustment (specifically setting the black point to 13) using scikit-image, OpenCV, or even plain NumPy—all perfect for batch processing. Let's walk through each approach with practical code examples you can adapt for bulk edits.
1. 基础方案:NumPy + Pillow
This approach gives you full control over the pixel math, mirroring exactly what Photoshop does when you drag the black point slider to 13: all pixels darker than 13 get crushed to pure black, and the remaining pixel range (13–255) gets stretched linearly to fill 0–255.
import numpy as np from PIL import Image import os def set_black_point(image_path, output_path, black_point=13): # Open image and convert to numpy array img = Image.open(image_path).convert("RGB") img_array = np.array(img, dtype=np.float32) # Crush pixels below black point to 0 img_array[img_array < black_point] = 0 # Stretch remaining values to fill 0-255 scale_factor = 255 / (255 - black_point) img_array = img_array * scale_factor # Ensure values stay within valid pixel range and convert back to uint8 img_array = np.clip(img_array, 0, 255).astype(np.uint8) # Save processed image Image.fromarray(img_array).save(output_path) # Batch processing example input_folder = "your_input_directory" output_folder = "your_output_directory" os.makedirs(output_folder, exist_ok=True) for filename in os.listdir(input_folder): if filename.lower().endswith(('.png', '.jpg', '.jpeg')): input_path = os.path.join(input_folder, filename) output_path = os.path.join(output_folder, filename) set_black_point(input_path, output_path)
2. OpenCV方案
If your workflow already uses OpenCV, this implementation follows the same logic—just remember to handle the BGR-to-RGB color channel conversion (OpenCV reads images in BGR by default).
import cv2 import numpy as np import os def adjust_black_point_cv2(image_path, output_path, black_point=13): img = cv2.imread(image_path) # Convert BGR to RGB for consistent processing (optional, skip if working with BGR) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img_float = img.astype(np.float32) # Apply black point adjustment img_float[img_float < black_point] = 0 scale_factor = 255 / (255 - black_point) img_float = img_float * scale_factor img_processed = np.clip(img_float, 0, 255).astype(np.uint8) # Convert back to BGR for saving with OpenCV img_processed_bgr = cv2.cvtColor(img_processed, cv2.COLOR_RGB2BGR) cv2.imwrite(output_path, img_processed_bgr) # Batch processing input_folder = "your_input_directory" output_folder = "your_output_directory" os.makedirs(output_folder, exist_ok=True) for filename in os.listdir(input_folder): if filename.lower().endswith(('.png', '.jpg', '.jpeg')): adjust_black_point_cv2(os.path.join(input_folder, filename), os.path.join(output_folder, filename))
3. scikit-image方案
scikit-image has a built-in function exposure.rescale_intensity that handles the pixel stretching automatically, making this the most concise option.
from skimage import io, exposure import numpy as np import os def adjust_black_point_skimage(image_path, output_path, black_point=13): img = io.imread(image_path) # Convert to uint8 if image is read as float (0-1 range) if img.dtype != np.uint8: img = (img * 255).astype(np.uint8) # Rescale intensity: map [black_point, 255] to [0, 255] img_processed = exposure.rescale_intensity(img, in_range=(black_point, 255), out_range=(0, 255)) io.imsave(output_path, img_processed) # Batch processing input_folder = "your_input_directory" output_folder = "your_output_directory" os.makedirs(output_folder, exist_ok=True) for filename in os.listdir(input_folder): if filename.lower().endswith(('.png', '.jpg', '.jpeg')): adjust_black_point_skimage(os.path.join(input_folder, filename), os.path.join(output_folder, filename))
Quick Notes
- All these methods produce identical results to Photoshop's Levels adjustment with black point set to 13.
- For grayscale images, simply remove any color channel conversion code.
- Add a progress bar (e.g., using
tqdm) to track batch processing for large image sets.
内容的提问来源于stack exchange,提问作者anon

