Files
openMVS/cartoon_texture/fuse_textures.py
T
2026-11-15 15:34:47 +08:00

172 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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()