点云强度如何增强显示
核心思想:把强度值(0-255)映射成不同颜色,让斑马线和道路在视觉上区分开。
算法原理(3 步)
第 1 步:归一化
强度值 80-180 → 变成 0-1 之间的 t 值
t = (当前强度 - 最小值) / (最大值 - 最小值)
举例:范围 [80, 180]
- 强度 80 → t = 0.0
- 强度 130 → t = 0.5
- 强度 180 → t = 1.0
第 2 步:根据 t 值分配颜色
t = 0.0 → 蓝色 (0, 0, 1)
t = 0.33 → 青色 (0, 1, 1)
t = 0.66 → 黄色 (1, 1, 0)
t = 1.0 → 红色 (1, 0, 0)
第 3 步:中间值线性过渡
t 在 0 到 0.33 之间:R 从 0→0, G 从 0→1, B=1
t 在 0.33 到 0.66 之间:R 从 0→1, G=1, B 从 1→0
t 在 0.66 到 1 之间:R=1, G 从 1→0, B=0
可视化
强度值: 0 ────── 85 ────── 170 ────── 255
t 值: 0 ────── 0.33 ──── 0.66 ────── 1
颜色: 蓝 ────→ 青 ────→ 黄 ────→ 红
████ ████ ████ ████
为什么能区分斑马线和道路?
斑马线和道路的反射强度不同:
道路(沥青):强度低 → 显示蓝色/青色 斑马线(白色涂料):强度高 → 显示黄色/红色 滑块的作用:调整minI和maxI,让斑马线的强度刚好落在黄色/红色区域,道路落在蓝色区域,这样颜色对比就出来了!
代码对应
// 第 1 步:归一化
const clipped = Math.max(minI, Math.min(maxI, intensity))
let t = (clipped - minI) / (maxI - minI)
// 第 2-3 步:分段赋值 RGB
if (t < 0.33) { // 蓝→青段
r = 0; g = t*3; b = 1
} else if (t < 0.66) { // 青→黄段
r = (t-0.33)*3; g = 1; b = 1-(t-0.33)*3
} else { // 黄→红段
r = 1; g = 1-(t-0.66)*3; b = 0
}
拖动 GUI 中的两个滑块来区分不同物体:
- 强度中心:决定哪个强度值显示为黄色(中间色)
- 强度范围:控制对比度,范围越小颜色分界越明显
滑块实现原理
强度中心
GUI 控制(第 193-195 行):
this.gui.add(params, 'intensityCenter', 0, 255, 1)
.name('强度中心')
.onChange((value) => {
this.updateColorRange(value, params.intensityRange)
})
更新逻辑(第 222-227 行):
updateColorRange(center, range) {
const min = Math.max(0, center - range / 2) // 最小值 = 中心 - 范围/2
const max = Math.min(255, center + range / 2) // 最大值 = 中心 + 范围/2
this.colorRange = [min, max]
this.applyIntensityColorMap()
}
为什么 center 决定黄色对应的强度值?
归一化公式:t = (intensity - min) / (max - min)
当 intensity = center 时:
t = (center - (center - range/2)) / ((center + range/2) - (center - range/2))
= (range/2) / range
= 0.5
结论:无论 range 是多少,只要 intensity == center,结果永远是 t = 0.5,对应黄色区域(青→黄段)。
举例:
center = 128, range = 100→min=78, max=178→ 强度 128 的点t = 50/100 = 0.5→ 黄色center = 150, range = 60→min=120, max=180→ 强度 150 的点t = 30/60 = 0.5→ 黄色
强度范围
GUI 控制(第 198-200 行):
this.gui.add(params, 'intensityRange', 10, 255, 5)
.name('强度范围')
.onChange((value) => {
this.updateColorRange(params.intensityCenter, value)
})
原理:范围决定归一化公式的分母
t = (intensity - minI) / (maxI - minI)
↑
范围 = range
| 范围大小 | 效果 |
|---|---|
| 范围大(如 200) | 分母大,同样的强度差ΔI 产生的Δt 更小 → 颜色过渡平缓 |
| 范围小(如 30) | 分母小,同样的强度差ΔI 产生的Δt 更大 → 颜色对比强烈 |
建议操作
- 先点"自动调整范围"获取基准
- 调小"强度范围"(如 30-80)增强对比
- 移动"强度中心"直到斑马线和道路颜色差异最大
伽马值(Gamma)
作用
调整中间调亮度,让斑马线和道路的顏色对比更明显,而不影响最暗和最亮部分。
原理
在归一化得到 t 值后,应用伽马校正:
// gamma > 1:提亮中间调
// gamma < 1:压暗中间调
if (enableGamma.value) {
t = Math.pow(t, 1 / gammaValue.value)
}
伽马变换公式:t_out = t_in ^ (1/γ)
| 伽马值 | 效果 | 适用场景 |
|---|---|---|
| γ = 1.0 | 无变化,线性映射 | 原始对比 |
| γ > 1.0(如 1.5-2.5) | 提亮中间调,低强度值变化小,高强度值变化大 | 道路和斑马线强度差异小,需要拉开中间色调 |
| γ < 1.0(如 0.5-0.9) | 压暗中间调,高强度值变化小,低强度值变化大 | 高光区域过曝,需要压缩 |
为什么有效?
伽马校正改变的是 t 值的分布密度:
- γ > 1:更多
t值被推向 1,中间调和高光区域的颜色变化更细腻 - γ < 1:更多
t值被推向 0,暗部区域的颜色变化更细腻
举例(γ = 1.5):
原 t 值 → 伽马校正后
t = 0.1 → 0.1^(1/1.5) = 0.21 (轻微提升)
t = 0.5 → 0.5^(1/1.5) = 0.63 (明显提升)
t = 0.9 → 0.9^(1/1.5) = 0.93 (变化很小)
使用顺序
完整的颜色映射流程:
1. 强度值 → 裁剪到 [minI, maxI]
2. 归一化 → t = (clipped - minI) / (maxI - minI)
3. 伽马校正 → t = t ^ (1/γ) ← 这一步调整中间调
4. 分段配色 → 根据校正后的 t 分配 RGB
GUI 控制
// 伽马值滑块 (0.5-2.5)
gui.add(params, 'gammaValue', 0.5, 2.5, 0.1)
.name('亮度值')
// 开关
gui.add(params, 'enableGamma')
.name('亮度调节')
操作建议
- 先用"强度中心"和"强度范围"确定基本的颜色分界
- 调节"亮度值"滑块(推荐从 1.0 开始,逐渐增大到 1.5-2.0)
- 观察斑马线和道路的对比度,找到最清晰的平衡点