You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
469 lines
15 KiB
469 lines
15 KiB
import os.path |
|
import shutil |
|
import time |
|
import argparse |
|
import cv2 |
|
import numpy as np |
|
from scipy.interpolate import CubicSpline |
|
import sys, os |
|
sys.path.append(os.path.dirname(os.path.abspath(__file__))) |
|
from fix_up_color_two_a import remove_gray_and_sharpening |
|
from ps_image_shadow_up_ag_two_a import photoshop_actions_emulation |
|
|
|
|
|
# def perceptual_adjustment(img, threshold=220, reduction=0.5): |
|
# hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) |
|
# h, s, v = cv2.split(hsv) |
|
# |
|
# saturation_weights = 1 - (s.astype(np.float32) / 255 * 0.01) |
|
# |
|
# adjusted_v = np.where( |
|
# v > threshold, |
|
# threshold + (v - threshold) * (1 - reduction * saturation_weights), |
|
# v |
|
# ) |
|
# |
|
# return cv2.cvtColor(cv2.merge([h, s, adjusted_v.astype(np.uint8)]), cv2.COLOR_HSV2BGR) |
|
|
|
|
|
# def perceptual_smooth_adjustment(img, threshold=220, reduction=0.5,margin=5, saturation_sensitivity=0.3): |
|
# """ |
|
# 感知式亮度压制 + 过渡区平滑(防止硬边) |
|
# |
|
# 参数: |
|
# - threshold: 高光压制起点 |
|
# - margin: 过渡带宽度(像素值差) |
|
# - reduction: 压制比例(1 表示最多降低100%) |
|
# - saturation_sensitivity: 饱和度影响权重 |
|
# """ |
|
# hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) |
|
# h, s, v = cv2.split(hsv) |
|
# |
|
# v = v.astype(np.float32) |
|
# s = s.astype(np.float32) |
|
# |
|
# # 1. 饱和度感知权重(饱和度越高,压制越弱) |
|
# sat_weight = 1.0 - (s / 255.0 * saturation_sensitivity) |
|
# sat_weight = np.clip(sat_weight, 0.0, 1.0) |
|
# |
|
# # 2. 构建平滑过渡权重(根据 V 值) |
|
# transition_mask = np.zeros_like(v, dtype=np.float32) |
|
# transition_mask[v <= threshold] = 0.0 |
|
# transition_mask[v >= threshold + margin] = 1.0 |
|
# |
|
# # 线性过渡区域 |
|
# in_between = (v > threshold) & (v < threshold + margin) |
|
# transition_mask[in_between] = (v[in_between] - threshold) / margin |
|
# |
|
# # 3. 计算最终压制权重(融合过渡 + 饱和度感知) |
|
# weight = reduction * transition_mask * sat_weight |
|
# |
|
# # 4. 应用压制 |
|
# v_adjusted = v - (v - threshold) * weight |
|
# v_adjusted = np.clip(v_adjusted, 0, 255).astype(np.uint8) |
|
# |
|
# # 5. 合成并返回 |
|
# adjusted_hsv = cv2.merge([h, s.astype(np.uint8), v_adjusted]) |
|
# result_bgr = cv2.cvtColor(adjusted_hsv, cv2.COLOR_HSV2BGR) |
|
# |
|
# return result_bgr |
|
|
|
def smootherstep(x): |
|
"""五次平滑插值函数:更加平滑过渡""" |
|
return x**3 * (x * (x * 6 - 15) + 10) |
|
|
|
|
|
def perceptual_smooth_adjustment_color_blend(img, threshold=220, reduction=0.5, margin=10, saturation_sensitivity=0.3, blur_radius=5, color_blend_strength=0.5): |
|
""" |
|
更平滑、颜色融合感知亮度压制 |
|
|
|
- threshold: 压制起始亮度(V 通道) |
|
- reduction: 压制强度(0-1) |
|
- margin: 阈值过渡区间(像素亮度差) |
|
- saturation_sensitivity: 饱和度高时减弱压制 |
|
- blur_radius: 用于颜色融合的模糊半径 |
|
- color_blend_strength: 颜色融合程度(0~1) |
|
""" |
|
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) |
|
h, s, v = cv2.split(hsv) |
|
|
|
v = v.astype(np.float32) |
|
s = s.astype(np.float32) |
|
|
|
# 饱和度感知压制减弱 |
|
sat_weight = 1.0 - (s / 255.0 * saturation_sensitivity) |
|
sat_weight = np.clip(sat_weight, 0.0, 1.0) |
|
|
|
# 平滑压制权重计算 |
|
delta = v - threshold |
|
transition = np.zeros_like(v, dtype=np.float32) |
|
|
|
in_range = (delta > 0) & (delta < margin) |
|
transition[in_range] = smootherstep(delta[in_range] / margin) |
|
transition[delta >= margin] = 1.0 |
|
|
|
# 压制权重融合 |
|
weight = reduction * transition * sat_weight |
|
|
|
# 应用压制 |
|
v_new = v - (v - threshold) * weight |
|
v_new = np.clip(v_new, 0, 255).astype(np.uint8) |
|
|
|
# 合成压制后的图像 |
|
adjusted_hsv = cv2.merge([h, s.astype(np.uint8), v_new]) |
|
adjusted = cv2.cvtColor(adjusted_hsv, cv2.COLOR_HSV2BGR) |
|
|
|
# ------------------- |
|
# 融合原图模糊版 → 减少颜色突兀 |
|
# ------------------- |
|
blurred = cv2.GaussianBlur(img, (blur_radius | 1, blur_radius | 1), 0) |
|
|
|
# 构建融合权重 mask,仅对过渡区域起作用 |
|
color_blend_mask = np.clip(weight, 0, 1) * color_blend_strength |
|
color_blend_mask = color_blend_mask[..., None] # 扩展为 (H,W,1) 用于通道融合 |
|
|
|
# 融合颜色(让压制后的颜色更靠近周围环境) |
|
final = adjusted.astype(np.float32) * (1 - color_blend_mask) + blurred.astype(np.float32) * color_blend_mask |
|
final = np.clip(final, 0, 255).astype(np.uint8) |
|
|
|
return final |
|
|
|
# def perceptual_smooth_adjustment(img, threshold=220, reduction=0.5, margin=10, saturation_sensitivity=0.3, blur_radius=3): |
|
# """ |
|
# 感知式亮度压制 + 平滑过渡 + 边缘柔化 |
|
# |
|
# 参数: |
|
# - threshold: 开始压制的亮度阈值 |
|
# - reduction: 最大压制比例(1 表示压到底) |
|
# - margin: 过渡宽度(单位是亮度值差) |
|
# - saturation_sensitivity: 饱和度越高,压制越弱 |
|
# - blur_radius: 最终压制结果边缘模糊程度(建议 3) |
|
# """ |
|
# # 转 HSV 获取亮度与饱和度 |
|
# hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) |
|
# h, s, v = cv2.split(hsv) |
|
# |
|
# v = v.astype(np.float32) |
|
# s = s.astype(np.float32) |
|
# |
|
# # 1. 饱和度影响(越高压制越少) |
|
# sat_weight = 1.0 - (s / 255.0 * saturation_sensitivity) |
|
# sat_weight = np.clip(sat_weight, 0.0, 1.0) |
|
# |
|
# # 2. 构造更平滑的过渡权重(使用 smootherstep) |
|
# delta = v - threshold |
|
# transition = np.zeros_like(v, dtype=np.float32) |
|
# |
|
# in_range = (delta > 0) & (delta < margin) |
|
# transition[in_range] = smootherstep(delta[in_range] / margin) |
|
# transition[delta >= margin] = 1.0 # 完全压制区域 |
|
# |
|
# # 3. 亮度压制权重 |
|
# weight = reduction * transition * sat_weight |
|
# |
|
# # 4. 应用压制 |
|
# v_new = v - (v - threshold) * weight |
|
# v_new = np.clip(v_new, 0, 255).astype(np.uint8) |
|
# |
|
# # 5. 合并回 HSV 并转回 BGR |
|
# adjusted_hsv = cv2.merge([h, s.astype(np.uint8), v_new]) |
|
# result = cv2.cvtColor(adjusted_hsv, cv2.COLOR_HSV2BGR) |
|
# |
|
# # 6. 进一步边缘模糊(仅作用于过渡区域) |
|
# blur_mask = (transition > 0.0) & (transition < 1.0) |
|
# blur_mask = blur_mask.astype(np.uint8) * 255 |
|
# blur_mask = cv2.GaussianBlur(blur_mask, (blur_radius|1, blur_radius|1), 0) |
|
# |
|
# # 创建模糊版本 |
|
# blurred = cv2.GaussianBlur(result, (blur_radius|1, blur_radius|1), 0) |
|
# |
|
# # 混合:边缘模糊区域取模糊图,其他保持原图 |
|
# blur_mask = blur_mask.astype(np.float32) / 255.0 |
|
# result = result.astype(np.float32) |
|
# blurred = blurred.astype(np.float32) |
|
# |
|
# final = result * (1 - blur_mask[..., None]) + blurred * blur_mask[..., None] |
|
# final = np.clip(final, 0, 255).astype(np.uint8) |
|
# |
|
# return final |
|
|
|
def process_image(input_path, output_path, threshold=210, reduction=0.6): |
|
""" |
|
""" |
|
try: |
|
img = cv2.imread(input_path) |
|
if img is None: |
|
raise ValueError("无法读取图像,请检查路径是否正确") |
|
|
|
#result = perceptual_adjustment(img, threshold, reduction) |
|
result = perceptual_smooth_adjustment_color_blend(img, threshold, reduction) |
|
|
|
cv2.imwrite(output_path, result) |
|
print(f"处理成功,结果已保存到: {output_path}") |
|
|
|
return True |
|
|
|
except Exception as e: |
|
print(f"处理失败: {str(e)}") |
|
return False |
|
|
|
def sigmoid(x, center=0.0, slope=10.0): |
|
return 1 / (1 + np.exp(-slope * (x - center))) |
|
|
|
|
|
def reduce_highlights_lab_advanced_hsvmask( |
|
img, |
|
highlight_thresh=220, |
|
strength=30, |
|
sigma=15, |
|
detail_boost=1.0, |
|
preserve_local_contrast=True |
|
): |
|
""" |
|
LAB高光压制 + HSV感知蒙版 + 细节保留 |
|
""" |
|
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) |
|
V = hsv[:, :, 2].astype(np.float32) |
|
|
|
# 1. 生成高光 mask,过渡平滑 |
|
mask = sigmoid(V, center=highlight_thresh, slope=0.05) |
|
mask = np.clip(mask, 0, 1) |
|
mask = cv2.GaussianBlur(mask, (0, 0), sigmaX=2) |
|
|
|
mask_vis = (mask * 255).astype(np.uint8) |
|
|
|
# 2. LAB 空间亮度压制 |
|
img_lab = cv2.cvtColor(img, cv2.COLOR_BGR2Lab) |
|
L, a, b = cv2.split(img_lab) |
|
L = L.astype(np.float32) |
|
|
|
# 3. 模糊和细节 |
|
L_blur = cv2.GaussianBlur(L, (0, 0), sigma) |
|
L_detail = L - L_blur |
|
|
|
# 4. 替代方案:压制 L,但融合方式更柔和 |
|
L_target = L_blur - strength * mask |
|
L_target = np.clip(L_target, 0, 255) |
|
|
|
if preserve_local_contrast: |
|
# 保留细节 + 局部对比度(避免过度平滑) |
|
L_new = L_target + detail_boost * L_detail |
|
else: |
|
# 单纯压制亮度 |
|
L_new = L_target |
|
|
|
L_new = np.clip(L_new, 0, 255).astype(np.uint8) |
|
|
|
# 5. 合成回去 |
|
lab_new = cv2.merge([L_new, a, b]) |
|
result = cv2.cvtColor(lab_new, cv2.COLOR_Lab2BGR) |
|
|
|
return result, mask_vis |
|
|
|
def suppress_highlights_keep_texture(image_bgr, v_thresh=225, target_v=215, sigma=1): |
|
"""""" |
|
|
|
image_hsv = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2HSV) |
|
h, s, v = cv2.split(image_hsv) |
|
v = v.astype(np.float32) |
|
|
|
v_blur = cv2.GaussianBlur(v, (0, 0), sigmaX=sigma) |
|
detail = v - v_blur |
|
|
|
# 构建 soft mask(0~1),用于动态压制 |
|
mask = (v_blur > v_thresh).astype(np.float32) |
|
# weight 越大压得越狠 |
|
weight = np.clip((v_blur - v_thresh) / 20.0, 0, 1) * mask # 20 是压制带宽 |
|
#weight =weight*1.2 |
|
# 将亮度压到 target_v 的线性混合: |
|
v_compress = v_blur * (1 - weight) + target_v * weight |
|
|
|
v_new = v_compress + detail |
|
v_new = np.clip(v_new, 0, 255).astype(np.uint8) |
|
|
|
hsv_new = cv2.merge([h, s, v_new]) |
|
result_bgr = cv2.cvtColor(hsv_new, cv2.COLOR_HSV2BGR) |
|
|
|
return result_bgr |
|
|
|
def correct_light_again_hsv(image_path): |
|
img = cv2.imread(image_path) |
|
result, mask_vis = reduce_highlights_lab_advanced_hsvmask( |
|
img, |
|
highlight_thresh=225, |
|
strength=15, |
|
sigma=10, |
|
detail_boost=1.2 |
|
) |
|
result_bgr= suppress_highlights_keep_texture(result) |
|
output_image_path = image_path.replace(".jpg", "_light02.jpg") |
|
cv2.imwrite( |
|
output_image_path, |
|
result_bgr |
|
) |
|
return output_image_path |
|
|
|
def generate_curve_lut(x_points, y_points): |
|
""" |
|
输入采样点,生成 256 长度的查找表(LUT) |
|
""" |
|
cs = CubicSpline(x_points, y_points, bc_type='natural') |
|
x = np.arange(256) |
|
y = cs(x) |
|
y = np.clip(y, 0, 255).astype(np.uint8) |
|
return y |
|
|
|
def apply_curve(img, lut): |
|
""" |
|
对图像的每个通道应用曲线 LUT(复合通道) |
|
""" |
|
result = cv2.LUT(img, lut) |
|
return result |
|
|
|
|
|
def apply_curve_up_image(image_path,image_cache_dir): |
|
"""提亮""" |
|
x_points = [0, 124, 255] |
|
y_points = [0, 131, 255] |
|
lut = generate_curve_lut(x_points, y_points) |
|
#adjusted = apply_curve(img, lut) |
|
|
|
image_name_result = image_path.split("/")[-1].replace(".jpg", "_up.jpg") |
|
result_path= os.path.join(image_cache_dir,image_name_result) |
|
count=0 |
|
while True: |
|
# image_path="/data/datasets_20t/Downloads_google/correct_show_obj/265340/265340Tex1.jpg" |
|
# result_path = "/data/datasets_20t/Downloads_google/correct_show_obj/265340/265340Tex1_new.jpg" |
|
|
|
if os.path.exists(result_path): |
|
image_bgr = cv2.imread(result_path) |
|
else: |
|
image_bgr = cv2.imread(image_path) |
|
|
|
image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB) |
|
image_hsv = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2HSV).astype(np.float32) |
|
h, s, v = cv2.split(image_hsv) |
|
h_mean = np.mean(h) |
|
s_mean = np.mean(s) |
|
v_mean = np.mean(v) |
|
print(f"v_mean{v_mean}") |
|
if v_mean<60: |
|
adjusted = apply_curve(image_bgr, lut) |
|
cv2.imwrite(result_path, adjusted) |
|
time.sleep(1) |
|
count=count+1 |
|
else: |
|
break |
|
if count>=1: |
|
cv2.imwrite(result_path, adjusted) |
|
time.sleep(1) |
|
break |
|
if os.path.exists(result_path): |
|
return result_path |
|
else: |
|
return None |
|
|
|
def apply_curve_down_image(image_path,image_cache_dir): |
|
"""压暗""" |
|
x_points = [0, 131, 255] |
|
y_points = [0, 124, 255] |
|
lut = generate_curve_lut(x_points, y_points) |
|
# adjusted = apply_curve(img, lut) |
|
image_name_result = image_path.split("/")[-1].replace(".jpg", "_down.jpg") |
|
result_path= os.path.join(image_cache_dir,image_name_result) |
|
count = 0 |
|
while True: |
|
# image_path="/data/datasets_20t/Downloads_google/correct_show_obj/265340/265340Tex1.jpg" |
|
# result_path = "/data/datasets_20t/Downloads_google/correct_show_obj/265340/265340Tex1_new.jpg" |
|
|
|
if os.path.exists(result_path): |
|
image_bgr = cv2.imread(result_path) |
|
else: |
|
image_bgr = cv2.imread(image_path) |
|
|
|
image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB) |
|
image_hsv = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2HSV).astype(np.float32) |
|
h, s, v = cv2.split(image_hsv) |
|
h_mean = np.mean(h) |
|
s_mean = np.mean(s) |
|
v_mean = np.mean(v) |
|
print(f"v_mean{v_mean}") |
|
if v_mean > 110: |
|
adjusted = apply_curve(image_bgr, lut) |
|
cv2.imwrite(result_path, adjusted) |
|
time.sleep(1) |
|
count=count+1 |
|
else: |
|
break |
|
|
|
if count >= 1: |
|
cv2.imwrite(result_path, adjusted) |
|
time.sleep(1) |
|
break |
|
if os.path.exists(result_path): |
|
return result_path |
|
else: |
|
return None |
|
|
|
|
|
def correct_texture_image(input_path,image_result_dir,output_path): |
|
"""""" |
|
#input_path = os.path.join(image_in_dir, image_name) |
|
|
|
params = { |
|
'threshold': 220, |
|
'reduction': 0.6 |
|
} |
|
image_cache_dir= os.path.join(image_result_dir,"cache") |
|
os.makedirs(image_cache_dir, exist_ok=True) |
|
|
|
|
|
input_path_cure_up = apply_curve_up_image(input_path,image_cache_dir) |
|
if input_path_cure_up: |
|
input_path_cure_down = input_path_cure_up |
|
else: |
|
input_path_cure_down = input_path |
|
|
|
input_path_cure_down_result = apply_curve_down_image(input_path_cure_down,image_cache_dir) |
|
if input_path_cure_down_result: |
|
input_path_correct = input_path_cure_down_result |
|
else: |
|
input_path_correct = input_path_cure_down |
|
|
|
print("input_path_correct", input_path_correct) |
|
#image_name = input_path_correct.split("/")[-1] |
|
#out_put_path = os.path.join(image_cache_dir, image_name) |
|
image_light_down_fix_up_path = remove_gray_and_sharpening(input_path_correct, image_cache_dir) |
|
|
|
shadow_up_path = image_light_down_fix_up_path.replace(".jpg", "_shadow_up.jpg") |
|
photoshop_actions_emulation(image_light_down_fix_up_path, shadow_up_path) |
|
|
|
output_light_up_path = shadow_up_path.replace(".jpg", "_light_down.jpg") |
|
process_image(shadow_up_path, output_light_up_path, **params) |
|
#output_result_image_path=correct_light_again_hsv(output_light_up_path) |
|
shutil.copy(output_light_up_path,output_path) |
|
time.sleep(1) |
|
try: |
|
shutil.rmtree(image_cache_dir) |
|
except: |
|
print("删除文件错误") |
|
|
|
return output_light_up_path |
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
arg = argparse.ArgumentParser() |
|
arg.add_argument("-i","--image_path", type=str, default="") |
|
args = arg.parse_args() |
|
image_result_dir=os.path.dirname(args.image_path) |
|
os.makedirs(image_result_dir, exist_ok=True) |
|
|
|
start_time= time.time() |
|
correct_texture_image(args.image_path,image_result_dir,args.image_path) |
|
end_time = time.time() |
|
total_time = round(end_time - start_time, 2) |
|
print(f"处理成功,耗时 {total_time} 秒,") |
|
""" |
|
1、暗部提亮->白色提纯(220)->高光压暗->二次亮度调整 |
|
"""
|
|
|