1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
| """ PixExpo 像素级曝光融合算法 """
import numpy as np from typing import List
class PixExpoFusion: """ 像素级曝光融合 输入:多帧不同曝光时间的图像 输出:融合后的单帧图像,每个像素选择最佳曝光 """ def __init__(self, config: dict): self.target_intensity = config.get('target_intensity', 128) self.exposure_schedule = config.get( 'exposure_schedule', [5e-3, 10e-3, 20e-3, 40e-3] ) self.overexposure = config.get('overexposure', 240) self.underexposure = config.get('underexposure', 15) def fuse(self, frames: List[np.ndarray]) -> tuple: """ 像素级融合 Args: frames: 不同曝光时间的图像列表, 每帧 (H, W, C) Returns: fused_image: 融合后图像 (H, W, C) selection_map: 每像素选择的曝光索引 (H, W) """ n_frames = len(frames) if n_frames == 0: return None, None H, W, C = frames[0].shape distances = np.zeros((n_frames, H, W)) for i, frame in enumerate(frames): gray = np.mean(frame, axis=2) distances[i] = np.abs(gray - self.target_intensity) over_mask = gray > self.overexposure under_mask = gray < self.underexposure distances[i][over_mask] += 100 distances[i][under_mask] += 100 selection_map = np.argmin(distances, axis=0) fused_image = np.zeros_like(frames[0]) for i in range(n_frames): mask = selection_map == i fused_image[mask] = frames[i][mask] return fused_image, selection_map def extract_rppg_signal(self, fused_sequence: List[np.ndarray], skin_mask: np.ndarray) -> np.ndarray: """ 从融合图像序列提取 rPPG 信号 Args: fused_sequence: 融合后的图像序列 skin_mask: 皮肤区域mask Returns: rppg_signal: 心率信号 (T,) """ signals = [] for frame in fused_sequence: green = frame[:, :, 1] skin_pixels = green[skin_mask > 0] if len(skin_pixels) > 0: signals.append(np.mean(skin_pixels)) else: signals.append(0) signal = np.array(signals) signal = signal - np.mean(signal) from scipy.signal import butter, filtfilt fs = len(signal) / (len(signal) * 0.05) nyq = fs / 2 b, a = butter(4, [0.7/nyq, 3.5/nyq], btype='band') signal = filtfilt(b, a, signal) return signal
if __name__ == "__main__": fusion = PixExpoFusion({ 'target_intensity': 128, 'exposure_schedule': [5e-3, 10e-3, 20e-3, 40e-3], 'overexposure': 240, 'underexposure': 15 }) np.random.seed(42) frames = [ (np.random.rand(64, 64, 3) * 50 + 10).astype(np.uint8), (np.random.rand(64, 64, 3) * 80 + 50).astype(np.uint8), (np.random.rand(64, 64, 3) * 100 + 100).astype(np.uint8), (np.random.rand(64, 64, 3) * 50 + 200).astype(np.uint8), ] fused, selection = fusion.fuse(frames) print(f"融合图像 shape: {fused.shape}") print(f"选择图分布:") for i in range(4): ratio = np.sum(selection == i) / selection.size * 100 print(f" 曝光{i+1}: {ratio:.1f}%") skin_mask = np.zeros((64, 64), dtype=np.uint8) skin_mask[20:40, 20:40] = 1 signal = fusion.extract_rppg_signal([fused] * 30, skin_mask) print(f"rPPG信号长度: {len(signal)}") print(f"信号标准差: {np.std(signal):.4f}")
|