import os.path import numpy as np import cv2 import argparse import matplotlib.pyplot as plt def adjust_levels_image(img): """""" img = img.astype(np.float32) img = 255 * ((img - 20) / (241 - 20)) img[img < 0] = 0 img[img > 255] = 255 img = 255 * np.power(img / 255.0, 1.0 / 1.34) img = (img / 255) * (255- 0) + 0 img[img < 0] = 0 img[img > 255] = 255 img = img.astype(np.uint8) return img def photoshop_style_feather(image, mask, radius=150): """ """ if mask.dtype != np.uint8: mask = (mask * 255).astype(np.uint8) if len(mask.shape) > 2: mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY) kernel_size = max(3, int(2 * np.ceil(2 * radius) + 1)) expanded_size = (mask.shape[0] + 2 * radius, mask.shape[1] + 2 * radius) expanded_mask = np.zeros(expanded_size, dtype=np.uint8) center_y, center_x = radius, radius expanded_mask[center_y:center_y + mask.shape[0], center_x:center_x + mask.shape[1]] = mask blurred_expanded_mask = cv2.GaussianBlur( expanded_mask, (kernel_size, kernel_size), sigmaX=radius, sigmaY=radius, borderType=cv2.BORDER_REFLECT_101 ) feathered_mask = blurred_expanded_mask[center_y:center_y + mask.shape[0], center_x:center_x + mask.shape[1]] feathered_mask = feathered_mask.astype(np.float32) / 255.0 feathered_mask = np.power(feathered_mask, 1.1) # 轻微增强对比度 feathered_mask = np.clip(feathered_mask * 255, 0, 255).astype(np.uint8) return feathered_mask def calculate_luminance(img): """""" if len(img.shape) == 3: ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb) return ycrcb[:, :, 0].astype(np.float32) / 255.0 else: return img.astype(np.float32) / 255.0 def photoshop_feather_blend(adjusted_img, original_img, mask, feather_radius=150, brightness_factor=0.95): """ """ if mask.dtype != np.uint8: mask = (mask * 255).astype(np.uint8) if len(mask.shape) > 2: mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY) # plt.figure(figsize=(10, 8)) # plt.imshow(mask, cmap='gray') # plt.title("Feathered Mask") # plt.axis('off') # plt.colorbar(label='Opacity') # plt.show() feathered_mask = photoshop_style_feather(original_img, mask, feather_radius) # plt.figure(figsize=(10, 8)) # plt.imshow(feathered_mask, cmap='gray') # plt.title("Feathered Mask") # plt.axis('off') # plt.colorbar(label='Opacity') # plt.show() feathered_mask_float = feathered_mask.astype(np.float32) / 255.0 if len(original_img.shape) == 3 and len(feathered_mask_float.shape) == 2: feathered_mask_float = np.stack([feathered_mask_float] * 3, axis=-1) def to_linear(img): img_linear = img.astype(np.float32) / 255.0 return np.where(img_linear <= 0.04045, img_linear / 12.92, ((img_linear + 0.055) / 1.055) ** 2.4) def to_srgb(img_linear): return np.where(img_linear <= 0.0031308, img_linear * 12.92, 1.055 * (img_linear ** (1 / 2.4)) - 0.055) adjusted_linear = to_linear(adjusted_img) original_linear = to_linear(original_img) luminance_adjustment = np.mean(original_linear, axis=-1, keepdims=True) * (1.0 - brightness_factor) adjusted_linear_corrected = adjusted_linear - luminance_adjustment blended_linear = (adjusted_linear_corrected * feathered_mask_float + original_linear * (1 - feathered_mask_float)) blended_srgb = to_srgb(blended_linear) blended_img = np.clip(blended_srgb * 255, 0, 255).astype(np.uint8) return blended_img def rgb2lab_image(rgb_img): """""" rgb = rgb_img.astype(np.float32) / 255.0 mask = rgb > 0.04045 rgb = np.where(mask, np.power((rgb + 0.055) / 1.055, 2.4), rgb / 12.92) XYZ = np.dot(rgb, [ [0.436052025, 0.222491598, 0.013929122], [0.385081593, 0.716886060, 0.097097002], [0.143087414, 0.060621486, 0.714185470] ]) XYZ *= np.array([100.0, 100.0, 100.0]) / [96.4221, 100.0, 82.5211] epsilon = 0.008856 kappa = 903.3 XYZ_norm = np.where(XYZ > epsilon, np.power(XYZ, 1 / 3), (kappa * XYZ + 16) / 116) L = 116 * XYZ_norm[..., 1] - 16 a = 500 * (XYZ_norm[..., 0] - XYZ_norm[..., 1]) b = 200 * (XYZ_norm[..., 1] - XYZ_norm[..., 2]) return np.stack([L, a, b], axis=-1) def photoshop_lab_color_range_optimized(bgr_img, target_lab, tolerance=59, anti_alias=True): """""" rgb_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2RGB) lab_img = rgb2lab_image(rgb_img) L, a, b = lab_img[:, :, 0], lab_img[:, :, 1], lab_img[:, :, 2] target_L, target_a, target_b = target_lab diff_L = np.abs(L - target_L) diff_a = np.abs(a - target_a) diff_b = np.abs(b - target_b) dark_boost = np.ones_like(L) dark_mask = L <40 dark_boost[dark_mask] = 1.2 weighted_diff = np.sqrt( 0.25 * (diff_L / 100) ** 2 + 0.75 * ((diff_a + diff_b) / 255) ** 2 ) * 100 weighted_diff = weighted_diff / dark_boost threshold = 1.6 * (100 - tolerance) / 100 * 23 normalized_diff = weighted_diff / threshold mask = 0.5 * (np.tanh(4 * (1 - normalized_diff)) + 1) if anti_alias: mask = cv2.GaussianBlur(mask, (5, 5), 0) return mask def photoshop_actions_emulation(input_path, output_path): """""" original_img = cv2.imread(input_path) target_lab = np.array([47.89, 20.31, 20.6], dtype=np.float32) tol= 81 mask = photoshop_lab_color_range_optimized(original_img, target_lab, tol) mask_uint8 = (mask * 255).astype(np.uint8) adjusted_img = adjust_levels_image(original_img) result = photoshop_feather_blend(adjusted_img, original_img, mask_uint8, feather_radius=15, brightness_factor=0.95) cv2.imwrite(output_path, result) if __name__ == '__main__': arg = argparse.ArgumentParser() arg.add_argument('--image_name', type=str, default='274351Tex1_adjusted060518_2_221.jpg') arg.add_argument('--image_name_new', type=str, default='274351Tex1_adjusted060518_2_221_999999.jpg') arg.add_argument('--in_dir', type=str, default='/data/datasets_20t/fsdownload/image_color_timing/output/') arg.add_argument('--out_dir', type=str, default='/data/datasets_20t/fsdownload/image_color_timing/shadow_up/') args = arg.parse_args() os.makedirs(args.out_dir,exist_ok=True) input_path = os.path.join(args.in_dir,args.image_name) output_path = os.path.join(args.out_dir,args.image_name_new) photoshop_actions_emulation(input_path, output_path)