TianmouCV 是天眸(Tianmouc)互补视觉传感器的官方算法库。天眸是全球首款多通路仿生视觉传感器,具有以下特点:
| 特性 | 参数 |
|---|---|
| 最高帧率 | 10000 fps |
| 动态范围 | 130 dB |
| 带宽压缩 | 90% (相比传统高速相机) |
| 灵敏度 | 72%@530nm (可见光) |
| 数据模态 | 3种:RGB + 时间差分TD + 空间差分SD |
sim/ 模块是天眸传感器的软件仿真器,主要用途:
sim/
├── __init__.py # 导出接口
├── simpleSim_params.json # 仿真参数配置
├── simple_tmc_sim.py # 简单版:单图像仿真
└── simple_tmc_sim_advance.py # 高级版:序列仿真,完整噪声模拟
┌─────────────────────────────────────────────────────┐
│ 天眸传感器像素阵列 │
│ │
│ ┌──────┐┌──────┐┌──────┐┌──────┐ │
│ │ Cone││ Cone││ Cone││ Cone│ ← RGB输出(低帧率) │
│ └──────┘└──────┘└──────┘└──────┘ │
│ ┌──────┐┌──────┐ │
│ │ Rod ││ Rod │ ← TSD输出(高帧率) │
│ └──────┘└──────┘ │
└─────────────────────────────────────────────────────┘
Rod工作方式:每个Rod接收2×2像素 → 输出差分数据
原始图像 (H × W)
┌───┬───┬───┬───┐
│a11│a12│a13│a14│
├───┼───┼───┼───┤
│a21│a22│a23│a24│ ← 每个2×2块合并为一个rod像素
├───┼───┼───┼───┤
│a31│a32│a33│a34│
├───┼───┼───┼───┤
│a41│a42│a43│a44│
└───┴───┴───┴───┘
↓
ROD输出 (H/2 × W/4)
┌─────────┬─────────┐
│ (a11+a12│ │
│ a21+a22)/4 │
└─────────┴─────────┘
| 参数 | 默认值 | 说明 | 仿真硬件 |
|---|---|---|---|
adc_bit_prec |
8 | ADC量化精度 | 模数转换器 |
dark_fpn_stat.td_odd_mean |
0.12 | TD奇帧固定噪声均值 | 固定模式噪声 |
dark_fpn_stat.td_odd_std |
0.75 | TD奇帧固定噪声标准差 | 工艺不均匀性 |
dark_fpn_stat.td_even_mean |
0.32 | TD偶帧固定噪声均值 | |
dark_fpn_stat.td_even_std |
0.64 | TD偶帧固定噪声标准差 | |
dark_fpn_stat.sdl_odd_mean |
0.002 | SDL奇帧固定噪声均值 | |
dark_fpn_stat.sdl_odd_std |
0.897 | SDL奇帧固定噪声标准差 | |
dark_fpn_stat.sdr_odd_mean |
0.014 | SDR奇帧固定噪声均值 | |
dark_fpn_stat.sdr_odd_std |
1.01 | SDR奇帧固定噪声标准差 | |
dark_fpn_stat.sdl_even_mean |
-0.014 | SDL偶帧固定噪声均值 | |
dark_fpn_stat.sdl_even_std |
0.9 | SDL偶帧固定噪声标准差 | |
dark_fpn_stat.sdr_even_mean |
-0.002 | SDR偶帧固定噪声均值 | |
dark_fpn_stat.sdr_even_std |
1.02 | SDR偶帧固定噪声标准差 | |
sensor_fixed_noise_prob |
0.0 | 额外固定噪声概率 | 坏点/列噪声 |
sensor_random_noise_prob |
0.0 | 随机噪声概率 | 电路随机噪声 |
sensor_fixed_noise_mean_ch0 |
0.2 | TD通道额外固定噪声均值 | 系统偏移 |
sensor_fixed_noise_std_ch0 |
0.00390625 | TD通道额外固定噪声标准差 | 工艺偏差 |
sensor_fixed_noise_mean_ch12 |
0.0 | SDL/SDR通道额外固定噪声均值 | 系统偏移 |
sensor_fixed_noise_std_ch12 |
0.003125 | SDL/SDR通道额外固定噪声标准差 | 工艺偏差 |
sensor_random_noise_std |
0.0078125 | 随机噪声标准差 | 电路热噪声 |
sensor_poisson_lambda |
4 | 泊松噪声参数 | 光子散粒噪声 |
gray_weight_jitter |
0.0 | 灰度权重抖动 | 光照变化 |
gray_gain_min |
0.78 | 灰度增益最小值 | 亮度变化 |
gray_gain_max |
0.88 | 灰度增益最大值 | 亮度变化 |
sim_threshold_range |
[0.005, 0.02] | 差分阈值范围 | 比较器阈值 |
功能:
代码位置: simple_tmc_sim_advance.py:243-257
# 灰度化公式
gray_weights = np.array([0.299, 0.587, 0.114], dtype=np.float32)
img_gray = np.tensordot(img_rgb, gray_weights, axes=([-1], [0]))
数据变换:
[H, W, 3] uint8 BGR[rod_height, rod_width] float32 (0.0-1.0)仿真硬件: Rod光敏单元 → Rod只对亮度敏感,不需要色彩
功能: 模拟真实传感器的ROD阵列结构,将2×2像素合并为一个rod像素
代码位置: simple_tmc_sim_advance.py:259-269
# 2×2像素平均合并
etron_img_bin = (img_diff_sim[0::2, 0::2] +
img_diff_sim[1::2, 0::2] +
img_diff_sim[0::2, 1::2] +
img_diff_sim[1::2, 1::2]) / 4
# 交错排列
etron_img_rod[0::2, :] = etron_img_bin[0::2, 0::2]
etron_img_rod[1::2, :] = etron_img_bin[1::2, 1::2]
尺寸变换:
| 输入 | 输出 |
|---|---|
高度: sensor_height |
高度: sensor_height // 2 |
宽度: sensor_width |
宽度: sensor_width // 4 |
示例:
仿真硬件: ROD (Receptive-Field Organized Detector) 阵列 → 合并像素提高灵敏度,降低带宽
img_gray_tensor = torch.poisson(img_gray_tensor)
仿真硬件: 光子量子特性 → 光子到达服从泊松分布
etron_img_rod = etron_img_rod + torch.normal(mean=0, std=0.008, ...)
仿真硬件: 读出电路热噪声 → 电路电子热扰动
# 根据奇帧偶帧选择不同噪声
if sim_cnt % 2 == 0:
td_fpn = fpn['td_even']
sdl_fpn = fpn['sdl_even']
sdr_fpn = fpn['sdr_even']
else:
td_fpn = fpn['td_odd']
sdl_fpn = fpn['sdl_odd']
sdr_fpn = fpn['sdr_odd']
temp_diff += td_fpn
仿真硬件: 制造工艺不均匀性 → 每个比较器的固有偏置不同。奇帧偶帧由不同电路读出。TD/SDL/SDR每个通道都有独立FPN。
p1 = torch.poisson(...)
p2 = torch.poisson(...)
skellam_noise = (p1 - p2) * scale
Skellam分布 = 两个独立泊松分布之差 → 模拟多个独立噪声源的综合效应
代码位置: simple_tmc_sim_advance.py:271-291
def apply_sensor_noise(td, sdl, sdr, fixed_noise_map,
sensor_random_noise_prob, sensor_poisson_lambda, sensor_random_noise_std,
rod_height, rod_width, device):
# 添加固定噪声
if fixed_noise_map is not None:
td = td + (fixed_noise_map[0] * 128.0)
sdl = sdl + (fixed_noise_map[1] * 128.0)
sdr = sdr + (fixed_noise_map[2] * 128.0)
# 添加Skellam随机噪声
if sensor_random_noise_prob > 0 and random.random() < sensor_random_noise_prob:
scale = (std_target / np.sqrt(2.0 * mu)) * 128.0
p1 = torch.poisson(torch.full((3, rod_height, rod_width), mu, device=device))
p2 = torch.poisson(torch.full((3, rod_height, rod_width), mu, device=device))
skellam_noise = (p1 - p2) * scale
td = td + skellam_noise[0]
sdl = sdl + skellam_noise[1]
sdr = sdr + skellam_noise[2]
return td, sdl, sdr
功能:
仿真硬件: 坏点/列驱动电路噪声 → 实际传感器中可能存在的额外干扰源
代码位置: simple_tmc_sim_advance.py:221-241
def sample_sensor_fixed_noise(...):
fixed_noise_map = np.zeros((3, rod_height, rod_width), dtype=np.float32)
fixed_noise_map[0] = np.random.normal(...) # TD通道
shared_map = np.random.normal(...)
fixed_noise_map[1] = shared_map # SDL通道
fixed_noise_map[2] = shared_map # SDR通道
return fixed_noise_map
特点:
功能: 对输入灰度图像添加扰动,模拟真实场景中的光照变化和灰度偏移。
代码位置: simple_tmc_sim_advance.py:243-257
def apply_gray_jitter(img, gray_weight_jitter, gray_gain_min, gray_gain_max):
if gray_weight_jitter > 0:
delta = np.random.uniform(-gray_weight_jitter, gray_weight_jitter, size=3)
gray_weights = np.clip(gray_weights + delta, 1e-6, None)
gray_weights = gray_weights / np.sum(gray_weights)
gray_gain = float(np.random.uniform(gray_gain_min, gray_gain_max))
img_gray = np.tensordot(img_rgb, gray_weights, axes=([-1], [0]))
img_gray = (img_gray * gray_gain * 255.0).clip(0, 255).astype(np.uint8)
else:
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
return img_gray
扰动类型:
[0.299, 0.587, 0.114]添加随机扰动,模拟色彩平衡变化仿真硬件: 光照/曝光变化 → 真实场景中光照不是恒定的
功能: 保持深度为2的缓冲区,存储当前帧和前一帧
代码位置: simple_tmc_sim_advance.py:49-59
def push_to_fifo(tensor, x):
return torch.cat((tensor[1:], x))
使用方式:
rod_v_buf = torch.zeros(size=(2, rod_height, rod_width))
...
rod_v_buf = push_to_fifo(rod_v_buf, img_diff_sim)
# 现在 rod_v_buf[0] = 前一帧, rod_v_buf[1] = 当前帧
仿真硬件: 行缓存/移位寄存器 → 硬件需要存储前一帧数据来计算差分
代码位置: simple_tmc_sim_advance.py:107-187
if sim_cnt == 0 :
x_s, y_s = int(random.uniform(1,5)),int(random.uniform(1,5))
response_org = pix_v_out[1, :, :].cpu().numpy()
matShift = np.float32([[1,0,x_s],[0,1,y_s]])
response_shift = cv2.warpAffine(response_org, matShift,...)
temp_diff = response_shift - response_org
else:
temp_diff = pix_v_out[1, :, :] - pix_v_out[0, :, :]
首帧特殊处理:
sim_cnt == 0(第一帧),由于没有前一帧,对当前帧做微小随机平移来生成差分FPN注入:
if fpn is not None:
if sim_cnt % 2 == 0:
td_fpn = fpn['td_even']
else:
td_fpn = fpn['td_odd']
temp_diff += td_fpn
SD(Spatial Difference)计算模块负责模拟天眸传感器交错式像素阵列的空间梯度提取逻辑。基于前一帧与当前帧的Rod像素,分别计算SDL(左向空间差分)与SDR(右向空间差分),最终输出与硬件格式完全一致的int8差分图。
核心作用:提取图像边缘与纹理特征,为后续视觉任务提供高帧率、高压缩率的空间结构信息。
硬件映射:对应传感器内SDL/SDR专用比较器阵列,处理奇偶行交错布局的像素差分。
基于传感器交错行布局(ROD 采样时:奇数行取奇数列,偶数行取偶数列且向左平移 1-pixel),在 sd_cal 矩阵中的垂直和对角操作在物理空间上映射为 Center - Left 和 Center - Right。
| 差分类型 | 物理含义 | 行类型 | 差分公式(像素索引 (i,j)) | 说明 |
|---|---|---|---|---|
| SDL | Center - Left | 偶数行(0, 2, 4...) | SDL[i,j] = Gray[i+1,j] - Gray[i,j] | Gray[i+1,j] 为奇数行(中心) |
| 奇数行(1, 3, 5...) | SDL[i,j] = Gray[i,j] - Gray[i+1,j] | Gray[i,j] 为奇数行(中心) | ||
| SDR | Center - Right | 偶数行(0, 2, 4...) | SDR[i,j] = Gray[i+1,j] - Gray[i,j+1] | Gray[i+1,j] 为奇数行(中心) |
| 奇数行(1, 3, 5...) | SDR[i,j] = Gray[i,j] - Gray[i+1,j+1] | Gray[i,j] 为奇数行(中心) |
注:由于 ROD 奇偶行错位,
sd_cal矩阵中的垂直相邻像素在物理上并非完全垂直,而是包含了水平偏移。因此,垂直减法等同于水平梯度提取。
SD计算模块位于FIFO缓冲区之后、FPN噪声注入之前,输入为经过ROD下采样、添加光子噪声与读出噪声的Rod灰度图(rod_img),输出为原始空间差分图,后续流程如下:
sim_cnt % 2 == 0 → 使用偶帧FPN(sdl_even, sdr_even)sim_cnt % 2 != 0 → 使用奇帧FPN(sdl_odd, sdr_odd)xy=True,直接将SDL/SDR转换为SDX/SDY梯度并量化输出| 输出张量 | 数据类型 | 形状 | 值域 | 硬件对应关系 |
|---|---|---|---|---|
| SDL | int8 | (H/2, W/4) | -128 ~ +127 | 传感器左向空间差分比较器输出 |
| SDR | int8 | (H/2, W/4) | -128 ~ +127 | 传感器右向空间差分比较器输出 |
| 组合输出 | int8 | (H/2, W/4, 2) | -128 ~ +127 | 与真实硬件TSD数据格式完全一致,可直接用于算法测试 |
| SDX | int8 | (H/2, W/2) | -128 ~ +127 | x方向梯度(由SDL+SDR变换得到) |
| SDY | int8 | (H/2, W/2) | -128 ~ +127 | y方向梯度(由SDL+SDR变换得到) |
(注:文档部分内容可能由 AI 生成)
功能:
代码位置: simple_tmc_sim_advance.py:93-105
def diff_quant(diff, th):
max_digi_num = 2 ** (adc_bit_prec - 1)
lin_lsb = 1.0 / max_digi_num
diff_th = (diff - th) * (diff > th) + (diff + th) * (diff < -th)
diff_quantized = diff_th / lin_lsb
diff_quantized = diff_quantized.clip(min=-max_digi_num, max=max_digi_num)
diff_quantized = diff_quantized.ceil() * (diff > 0) + diff_quantized.floor() * (diff < 0)
diff_quantized = diff_quantized.char() if digital_type == torch.int8 \
else diff_quantized.short() if digital_type == torch.int16 \
else diff_quantized.int() if digital_type == torch.int32 \
else diff_quantized
return diff_quantized
改进点:
ceil()对正数向上取整,floor()对负数向下取整, quantization更准确adc_bit_prec自动选择输出数据类型量化原理:
| 差分范围 | 输出int8 (8bit ADC) |
|---|---|
| diff > th | 正整数 |
| -th ≤ diff ≤ th | 0 |
| diff < -th | 负整数 |
[H, W] float32[H, W] int8,范围 [-128, 127]将TD的正负值分离到两个通道。
代码位置: simple_tmc_sim.py:23-29
def tdiff_split(td_,cdim = 0):
td_pos = td_.clone()
td_pos[td_pos<0] = 0
td_neg = td_.clone()
td_neg[td_neg>0] = 0
td = torch.stack([td_pos,td_neg],dim=cdim)
return td
[H, W] td[2, H, W] → [正差分通道, 负差分通道]功能: 将输出从原始rod尺寸(H/2, W/4)上采样到目标尺寸(H, W/2),与输入图像分辨率对齐。
代码位置: simple_tmc_sim_advance.py:391-402
if interp:
tsdiff = torch.stack([td, sdl, sdr], dim=0) # [3, H, W]
tsdiff_expand = upsample_cross_conv(tsdiff.unsqueeze(1)).squeeze(1) # [3, H, W*2]
tsdiff_full = F.interpolate(tsdiff_expand.unsqueeze(0), size=(sensor_height, sensor_width),
mode='bilinear', align_corners=False).squeeze(0)
td, sdl, sdr = tsdiff_full[0], tsdiff_full[1], tsdiff_full[2]
cur_rod_h, cur_rod_w = sensor_height, sensor_width // 2
上采样流程:
upsample_cross_conv(来自tianmoucv ISP)将宽度从W/4扩展到W/2,保持交错布局(sensor_height, sensor_width)使用场景: 需要输出与原始输入同分辨率的差分数据时启用,方便直接与原图对齐处理。
功能: 在单帧仿真模式下,如果只提供一帧图像,自动通过随机平移旋转生成另一帧,用于生成差分。
代码位置: simple_tmc_sim_advance.py:494-505
random_x = int(random.uniform(-32,32))
random_y = int(random.uniform(-32,32))
random_degrees = random.uniform(-4,4)
img_target_tensor = shift_rotate_tensor(img_ref_tensor.unsqueeze(0),
random_x, random_y, random_degrees).squeeze(0)
参数范围:
用途: 单帧输入时也能生成TD差分数据,方便快速测试。
┌─────────────────────────────────────────────────────────────────────────┐
│ 输入: 单帧RGB图像 │
│ Shape: [H, W, 3] dtype: uint8 (0-255) │
└────────────┬────────────────────────────────────────────────────────────┘
↓
┌───────▼─────────┐
│ 灰度扰动预处理 │
│ 添加权重抖动+增益抖动 │
│ [H, W] float32 │
└───────┬─────────┘
↓
┌───────▼──────────────┐
│ ROD 2×2分簇下采样 │
│ [H/2, W/4] float32 │
└───────┬──────────────┘
↓
┌───────┴──────────────┐
│ 添加光子噪声+读出噪声 │
│ [H/2, W/4] float32 │
└───────┬──────────────┘
↓
┌───────▼──────────────┐
│ FIFO push_to_fifo │
│ [2, H/2, W/4] │ ← 缓冲区: [前一帧, 当前帧]
└───────┬──────────────┘
↓
┌───────┼──────────────┐
│ ↓ ↓
│ TD/SDL/SDR计算 + FPN注入 │
│ (TD/sdl/sdr各有独立FPN) │
│ [H/2, W/4] × 3 → float │
└───────┬──────────────┘
↓
┌───────▼──────────────┐
│ 阈值 + ADC量化 │
│ [H/2, W/4] int8 × 3 │ ← TD + SDL + SDR
└───────┬──────────────┘
↓
┌───────▼──────────────┐
│ 额外传感器噪声增强 │
│ 添加固定噪声+Skellam随机 │
│ 输出: TD, sdl, sdr │
└───────┬──────────────┘
↓
┌───────▼──────────────┐
│ (可选)上采样插值 │
│ [H, W/2] int8 × 3 │ ← 与输入分辨率对齐
└───────┬──────────────┘
↓
┌───────▼──────────────┐
│ 保存到文件 │
┌─────────────────────────────────────────────────────────────────────────┐
│ 输出: tsdiff_{sim_cnt}.npy │
│ Shape: [rod_height, rod_width, 3] dtype: int8 │
│ [:,:,0] → TD, [:,:,1] → SDL, [:,:,2] → SDR │
└─────────────────────────────────────────────────────────────────────────┘
| 阶段 | 高度 | 宽度 | 通道 | 数据类型 | 说明 |
|---|---|---|---|---|---|
| 输入RGB | H = sensor_height | W = sensor_width | 3 | uint8 | 原始输入 |
| 灰度扰动 | H | W | 1 | float32 | 添加权重+增益扰动 |
| ROD下采样 | H//2 | W//4 | 1 | float32 | 2×2分簇合并 |
| FIFO缓冲区 | 2 × (H//2) | W//4 | 2 | float32 | 存储前一帧+当前帧 |
| 差分计算+FPN注入 | H//2 | W//4 | 1 | float32 | TD/SDL/SDR各自独立FPN注入 |
| 阈值+ADC量化 | H//2 | W//4 | 1 | int8 | 稀疏编码 |
| 额外传感器噪声增强 | H//2 | W//4 | 1 | int8 | 添加固定噪声+Skellam随机噪声 |
| interp开启 → | 上采样到原始分辨率 | ||||
| TD输出(interp) | H | W//2 | 1 | int8 | 与输入高度一致 |
| SDL输出(interp) | H | W//2 | 1 | int8 | 与输入高度一致 |
| SDR输出(interp) | H | W//2 | 1 | int8 | 与输入高度一致 |
| 保存文件(interp关闭) | H//2 | W//4 | 3 | int8 | 原始rod尺寸 |
| 保存文件(interp开启) | H | W//2 | 3 | int8 | 上采样后尺寸 |
| SDXY输出 | H//2 | W//2 | 2 | float32 | 梯度格式输出 |
示例: sensor_width = 640, sensor_height = 320
| 阶段 | 高度 | 宽度 |
|---|---|---|
| 输入 | 320 | 640 |
| ROD输出 | 160 | 160 |
| TD/SDL/SDR (interp关闭) | 160 | 160 |
| TD/SDL/SDR (interp开启) | 320 | 320 |
| SDXY | 160 | 320 |
from tianmoucv.sim import run_sim
run_sim(
datapath='/path/to/images', # 图像文件夹
sensor_width=640, # 目标宽度
sensor_height=320, # 目标高度
device=device, # cuda/cpu
display=False, # 是否显示
save=True, # 是否保存
save_path='./output', # 保存路径
interp=False, # 是否上采样到原始分辨率
# 传感器噪声参数
sensor_fixed_noise_prob=0.0, # 额外固定噪声概率
sensor_random_noise_prob=0.0, # 额外随机噪声概率
# 灰度扰动参数
gray_weight_jitter=0.0, # 灰度权重抖动幅度
gray_gain_min=0.78, # 灰度增益最小值
gray_gain_max=0.88, # 灰度增益最大值
# 仿真参数
sim_threshold_range=[0.005, 0.02] # 差分阈值范围
)
输入要求:
datapath 文件夹中包含 .png 或 .jpg 图像新增参数说明:
interp: 如果为True,输出尺寸为(sensor_height, sensor_width//2),与输入分辨率对齐;False保持原始rod尺寸(sensor_height//2, sensor_width//4)sensor_fixed_noise_prob: 按概率添加额外固定噪声,模拟坏点sensor_random_noise_prob: 按概率添加额外Skellam随机噪声gray_weight_jitter: >0时启用灰度权重抖动,模拟色彩平衡变化gray_gain_min/gray_gain_max: 整体增益抖动范围,模拟曝光变化sim_threshold_range: 每次仿真随机采样阈值,模拟不同比较器阈值偏差from tianmoucv.sim import run_sim_singleimg
result = run_sim_singleimg(
img_target=None, # 当前帧 [H, W, 3] numpy array
img_ref=None, # 前一帧 [H, W, 3] numpy array
sensor_width=640, # 目标宽度
sensor_height=320, # 目标高度
xy=False, # 是否直接输出SDX/SDY梯度
interp=False, # 是否上采样到原始分辨率
device=device, # cuda/cpu
# 传感器噪声参数
sensor_fixed_noise_prob=0.0,
sensor_random_noise_prob=0.0,
# 灰度扰动参数
gray_weight_jitter=0.0,
gray_gain_min=0.78,
gray_gain_max=0.88,
# 仿真参数
sim_threshold_range=[0.005, 0.02]
)
输入说明:
img_target 或 img_ref随机生成规则(当只提供一帧时):
save_path/
├── rgb/ # RGB参考帧
│ └── frame_0000.png # 每cop_cone_skip(=10)帧保存一个
│ └── frame_0001.png
│
├── tsdiff/ # 时间空间差分数据
│ └── tsdiff_0.npy # 每个帧一个文件
│ └── tsdiff_1.npy
│
├── noisy_gt/ # 带噪声的ground truth
│ └── noisyGT_0.npy
│ └── noisyGT_1.npy
│
├── viz/ # 可视化结果
│ └── sim_viz_0.png
│ └── sim_viz_1.png
│
└── fpn.npy # 本次仿真的固定模式噪声
(1) tsdiff/{sim_cnt}.npy
# 读取后shape: [rod_height, rod_width, 3]
tsdiff = np.load('tsdiff_0.npy')
td = tsdiff[:, :, 0] # 时间差分 → int8
sdl = tsdiff[:, :, 1] # 左向空间差分 → int8
sdr = tsdiff[:, :, 2] # 右向空间差分 → int8
int8-128 ∼ +127(2) noisy_gt/noisyGT_{sim_cnt}.npy
# shape: [rod_height, rod_width]
gt = np.load('noisyGT_0.npy')
float32(3) rgb/frame_xxxx.png
[sensor_height, sensor_width, 3]uint8 RGB(4) fpn.npy
# shape: [rod_height, rod_width, 6]
fpn = np.load('fpn.npy')
# 通道顺序:
# [:, :, 0] → td_odd
# [:, :, 1] → sdl_odd
# [:, :, 2] → sdr_odd
# [:, :, 3] → td_even
# [:, :, 4] → sdl_even
# [:, :, 5] → sdr_even
当 xy=True:
img_ref_tensor, img_target_tensor, td, sd0, sd1 = run_sim_singleimg(...)
| 返回值 | Shape | Type | 说明 |
|---|---|---|---|
img_ref_tensor |
[3, H, W] |
float32 | 前一帧(平移后) |
img_target_tensor |
[3, H, W] |
float32 | 当前帧 |
td |
[2, H/2, W/2] |
int8 | TD分离正负 → [正TD, 负TD] |
sd0 |
[2, H/2, W/2] |
float32 | 前一帧SD → [sdl, sdr] |
sd1 |
[2, H/2, W/2] |
float32 | 当前帧SD → [sdl, sdr] |
| 软件模块 | 对应天眸硬件 | 功能 |
|---|---|---|
simpleSim_params.json 配置 |
配置寄存器 | 存储阈值、ADC精度等参数 |
| 灰度化+灰度扰动 | Rod光敏单元 | Rod只响应亮度,模拟光照变化 |
| 2×2分簇下采样 | ROD感光阵列 | 合并像素提高灵敏度,降低分辨率 |
| 泊松噪声 | 光子量子特性 | 光子散粒噪声 |
| 高斯读出噪声 | 读出电路 | 电路热噪声 |
| FIFO缓存 | 移位寄存器/行缓存 | 存储前一帧数据 |
| TD计算 | 时间差分比较器 | 检测帧间强度变化 |
| SDL计算 | 左向空间差分比较器 | 检测左向梯度 |
| SDR计算 | 右向空间差分比较器 | 检测右向梯度 |
| FPN独立噪声(TD/sdl/sdr) | 固定模式噪声 | 制造工艺不均匀性,每个通道独立 |
| 额外固定噪声 | 坏点/列驱动电路 | 系统固定偏移与坏点干扰 |
| Skellam随机噪声 | 混合噪声源 | 多个独立噪声源的综合效应 |
| 阈值处理 | 比较器 | 稀疏编码,只输出显著变化 |
| ADC量化 | 模数转换器 | 模拟差分 → 数字编码 |
| 上采样插值 | 后处理单元 | 分辨率对齐,方便算法处理 |
| 随机平移旋转生成第二帧 | 运动模拟 | 单帧输入也能生成差分 |
| TD符号分离 | 双比较器设计 | 正负差分分开处理 |
| SD2XY变换 | 梯度计算单元 | 原生格式 → XY梯度格式 |
from tianmoucv.sim import run_sim
import torch
# 设备选择
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 参数设置
data_path = './input_frames' # 输入图像文件夹
sensor_width = 640 # 传感器宽度
sensor_height = 320 # 传感器高度
# 启用噪声增强和灰度扰动
run_sim(
data_path,
sensor_width,
sensor_height,
device,
display=False,
save=True,
save_path='./output_tianmouc',
interp=False,
sensor_fixed_noise_prob=0.1,
sensor_random_noise_prob=0.1,
gray_weight_jitter=0.05,
gray_gain_min=0.7,
gray_gain_max=1.3
)
from tianmoucv.sim import run_sim_singleimg
import cv2
# 读取图像
img = cv2.imread('test.jpg')
# 只提供一帧,自动生成另一帧
img_pre, img_curr, td, sd0, sd1 = run_sim_singleimg(
img_target=img,
sensor_width=640,
sensor_height=320,
xy=True,
interp=False
)
print(f"TD shape: {td.shape}") # → [2, 160, 160]
print(f"SD0 shape: {sd0.shape}") # → [2, 160, 160]
print(f"SD1 shape: {sd1.shape}") # → [2, 160, 160]
from tianmoucv.sim import run_sim_singleimg
import cv2
# 读取两帧
img_ref = cv2.imread('frame_0.png')
img_target = cv2.imread('frame_1.png')
# 运行仿真
img_pre, img_curr, td, sd0, sd1 = run_sim_singleimg(
img_target=img_target,
img_ref=img_ref,
sensor_width=640,
sensor_height=320,
xy=True,
interp=True
)
from tianmoucv.isp import SD2XY
import torch
# sd shape: [2, H, W] → [sdl, sdr]
sd = sd1 # 当前帧SD
sdx, sdy = SD2XY(sd)
print(f"sdx shape: {sdx.shape}") # → [H/2, W/2]
print(f"sdy shape: {sdy.shape}") # → [H/2, W/2]
# sdx = x方向梯度,sdy = y方向梯度
| 特性 | simple_tmc_sim.py | simple_tmc_sim_advance.py |
|---|---|---|
| 输入 | 单张图像 | 图像序列文件夹 |
| 输出 | 直接返回tensor | 保存文件到磁盘 |
| FPN噪声 | ❌ 不支持 | ✅ 支持TD/SDL/SDR各通道独立FPN |
| 额外噪声 | ❌ 不支持 | ✅ 支持固定噪声+Skellam随机噪声 |
| 灰度扰动 | ❌ 不支持 | ✅ 支持权重抖动+增益抖动 |
| 上采样插值 | ❌ 不支持 | ✅ 可选上采样到原始分辨率 |
| 泊松噪声 | ✅ 可选 | ✅ 默认开启 |
| 使用场景 | 快速测试 | 批量生成数据集 |
| 入口函数 | run_sim_singleimg() |
run_sim() |
该仿真器完整模拟了天眸互补视觉传感器的工作流程:
仿真器生成的TSD数据保留了运动信息(TD)和边缘纹理信息(SD),同时通过稀疏编码实现了90%带宽压缩,这正是天眸传感器能够实现10000fps高速成像的核心原理。