卡通纹理清晰化
This commit is contained in:
1 parent
7a5dfb3ec3
commit
2ccccaaedc
2 files changed
+178
No files matched your search
@@ -0,0 +1,172 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
def detect_stain_mask(img2, img1, diff_thresh=0.12, grad_thresh=0.03, var_thresh=0.005):
|
||||
"""
|
||||
检测图2中的印染区域(相对于图1的差异 + 高梯度 + 高局部方差)
|
||||
|
||||
Args:
|
||||
img2: 图2(清晰但有印染),uint8 RGB
|
||||
img1: 图1(模糊但纯净),uint8 RGB
|
||||
Returns:
|
||||
mask: 浮点掩码 [0,1],1表示印染区域
|
||||
"""
|
||||
# 转浮点
|
||||
f2 = img2.astype(np.float32) / 255.0
|
||||
f1 = img1.astype(np.float32) / 255.0
|
||||
|
||||
# 1. 差异图
|
||||
diff = np.abs(f2 - f1)
|
||||
gray_diff = cv2.cvtColor(diff, cv2.COLOR_RGB2GRAY)
|
||||
|
||||
# 2. 图2的拉普拉斯梯度(印染边缘高梯度)
|
||||
gray2 = cv2.cvtColor(f2, cv2.COLOR_RGB2GRAY)
|
||||
laplacian2 = cv2.Laplacian(gray2, cv2.CV_32F, ksize=3)
|
||||
grad_mag = np.abs(laplacian2)
|
||||
|
||||
# 3. 图2的局部方差(印染区域局部对比度高)
|
||||
mean2 = cv2.GaussianBlur(f2, (0, 0), 5)
|
||||
sqr_mean2 = cv2.GaussianBlur(f2 * f2, (0, 0), 5)
|
||||
local_var = np.mean(sqr_mean2 - mean2 * mean2, axis=2)
|
||||
|
||||
# 4. 组合条件
|
||||
mask_cond = (
|
||||
(gray_diff > diff_thresh) &
|
||||
(grad_mag > grad_thresh) &
|
||||
(local_var > var_thresh)
|
||||
).astype(np.float32)
|
||||
|
||||
# 5. 形态学操作:膨胀连接区域 + 高斯模糊羽化
|
||||
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
|
||||
mask = cv2.dilate(mask_cond, kernel, iterations=2)
|
||||
mask = cv2.GaussianBlur(mask, (15, 15), 0)
|
||||
|
||||
# 归一化到 [0, 1]
|
||||
mask = np.clip(mask, 0, 1)
|
||||
|
||||
return mask
|
||||
|
||||
|
||||
def fuse_textures(img2, img1, mask, sharpen_img1=True):
|
||||
"""
|
||||
融合两张纹理图
|
||||
|
||||
Args:
|
||||
img2: 底图(清晰但有印染)
|
||||
img1: 补丁图(模糊但纯净)
|
||||
mask: 印染掩码 [0,1]
|
||||
sharpen_img1: 是否对图1做轻微锐化以减少"糊"感
|
||||
Returns:
|
||||
fused: 融合结果
|
||||
"""
|
||||
f2 = img2.astype(np.float32) / 255.0
|
||||
f1 = img1.astype(np.float32) / 255.0
|
||||
|
||||
# 可选:对图1做轻微锐化
|
||||
if sharpen_img1:
|
||||
# 非锐化掩模
|
||||
blurred = cv2.GaussianBlur(f1, (0, 0), 1.5)
|
||||
sharpened = cv2.addWeighted(f1, 1.5, blurred, -0.5, 0)
|
||||
f1_sharp = np.clip(sharpened, 0, 1)
|
||||
else:
|
||||
f1_sharp = f1
|
||||
|
||||
# 融合:mask区域用图1,其余保留图2
|
||||
mask_3ch = np.stack([mask, mask, mask], axis=2)
|
||||
fused = f2 * (1.0 - mask_3ch) + f1_sharp * mask_3ch
|
||||
|
||||
# 转回 uint8
|
||||
fused = np.clip(fused * 255.0, 0, 255).astype(np.uint8)
|
||||
|
||||
return fused
|
||||
|
||||
|
||||
def post_process_clahe(img, clip_limit=1.5, grid_size=(8, 8), mix_ratio=0.3):
|
||||
"""
|
||||
轻微 CLAHE 后处理(仅 V 通道),与原图混合
|
||||
"""
|
||||
hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV).astype(np.float32)
|
||||
h, s, v = cv2.split(hsv)
|
||||
|
||||
# CLAHE 需要 uint8
|
||||
v_u8 = (v / 255.0 * 255).astype(np.uint8)
|
||||
clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=grid_size)
|
||||
v_adjusted = clahe.apply(v_u8).astype(np.float32)
|
||||
|
||||
# 混合
|
||||
v_mixed = v * (1 - mix_ratio) + v_adjusted * mix_ratio
|
||||
v_mixed = np.clip(v_mixed, 0, 255)
|
||||
|
||||
hsv_out = cv2.merge([h, s, v_mixed]).astype(np.uint8)
|
||||
return cv2.cvtColor(hsv_out, cv2.COLOR_HSV2RGB)
|
||||
|
||||
|
||||
def main():
|
||||
# === 参数配置 ===
|
||||
img1_path = "/home/algo/Documents/openMVS/openMVS/cartoon_texture/data/642713/642713.o.jpg" # 图1:模糊但纯净
|
||||
img2_path = "/home/algo/Documents/openMVS/openMVS/cartoon_texture/data/642713/642713.mesh7.png" # 图2:清晰但有印染
|
||||
output_path = "/home/algo/Documents/openMVS/openMVS/cartoon_texture/data/642713/642713.jpg"
|
||||
|
||||
# 印染检测参数(可按实际效果微调)
|
||||
DIFF_THRESH = 0.12 # 差异阈值
|
||||
GRAD_THRESH = 0.03 # 梯度阈值
|
||||
VAR_THRESH = 0.005 # 局部方差阈值
|
||||
|
||||
# 后处理参数
|
||||
APPLY_CLAHE = True # 是否做轻微CLAHE
|
||||
SHARPEN_IMG1 = True # 是否锐化图1
|
||||
|
||||
# === 加载图片 ===
|
||||
img2 = cv2.imread(img2_path)
|
||||
img1 = cv2.imread(img1_path)
|
||||
|
||||
if img2 is None or img1 is None:
|
||||
print("Error: Cannot load images")
|
||||
return
|
||||
|
||||
# 确保尺寸一致(以图2为准)
|
||||
if img1.shape != img2.shape:
|
||||
print(f"Resizing img1 from {img1.shape} to {img2.shape}")
|
||||
img1 = cv2.resize(img1, (img2.shape[1], img2.shape[0]))
|
||||
|
||||
# 转 RGB(OpenCV 默认 BGR)
|
||||
img2_rgb = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)
|
||||
img1_rgb = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
|
||||
|
||||
print("Detecting stain mask...")
|
||||
mask = detect_stain_mask(img2_rgb, img1_rgb,
|
||||
diff_thresh=DIFF_THRESH,
|
||||
grad_thresh=GRAD_THRESH,
|
||||
var_thresh=VAR_THRESH)
|
||||
|
||||
print(f"Mask coverage: {np.mean(mask > 0.5) * 100:.1f}%")
|
||||
|
||||
print("Fusing textures...")
|
||||
fused = fuse_textures(img2_rgb, img1_rgb, mask, sharpen_img1=SHARPEN_IMG1)
|
||||
|
||||
if APPLY_CLAHE:
|
||||
print("Applying CLAHE post-processing...")
|
||||
fused = post_process_clahe(fused)
|
||||
|
||||
# 保存结果
|
||||
fused_bgr = cv2.cvtColor(fused, cv2.COLOR_RGB2BGR)
|
||||
cv2.imwrite(output_path, fused_bgr)
|
||||
|
||||
# 保存掩码可视化(调试用)
|
||||
mask_viz = (mask * 255).astype(np.uint8)
|
||||
cv2.imwrite("stain_mask_debug.png", mask_viz)
|
||||
|
||||
print(f"Done! Result saved to {output_path}")
|
||||
print(f"Debug mask saved to stain_mask_debug.png")
|
||||
|
||||
# 显示结果
|
||||
cv2.imshow("Original (mesh7.1)", img2)
|
||||
cv2.imshow("Reference (n1)", img1)
|
||||
cv2.imshow("Stain Mask", mask_viz)
|
||||
cv2.imshow("Fused Result", fused_bgr)
|
||||
cv2.waitKey(0)
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user