第四章 星点提取算法:从图像到质心的完整链路

星敏感器这东西,说白了就是卫星的「眼睛」。它拍到的星空图像,我们得从中找到星星的位置。这个过程的专业叫法,就是星点提取。

我个人习惯把星点提取拆成三步走:图像预处理 → 连通域分析 → 质心定位。每一步都有坑,我踩过不少,今天一并讲清楚。

4.1 图像预处理:去噪与背景抑制

星敏感器拍到的原始图像,噪声其实挺多的。CMOS传感器的暗电流噪声、读出噪声,还有空间高能粒子带来的椒盐噪声……嗯,这些都会干扰星点提取。

4.1.1 去噪滤波

我常用的去噪方法有两种:

  • 中值滤波:对椒盐噪声特别有效。窗口大小我一般选3×3或5×5,太大容易把星点抹平。
  • 高斯滤波:适合处理高斯噪声。但要注意,高斯滤波会模糊星点边缘,影响后续质心精度。

这里有个经验:先中值滤波,再高斯滤波。我在项目中试过很多组合,这个顺序效果最稳。

核心原则:去噪不能过度。星点本身可能只有几个像素大小,滤波窗口太大,星点就没了。

4.1.2 背景抑制

星空背景不是均匀的。地气光、杂散光、甚至卫星本体反射的光,都会让背景有起伏。背景抑制的目的,就是把这种起伏去掉,只留下星点信号。

我常用的方法是背景估计与减除

  1. 将图像分成若干小块(比如32×32像素)
  2. 统计每个块的灰度均值或中值
  3. 用双线性插值得到全图背景估计
  4. 原图减去背景估计

为什么用中值而不是均值?因为星点本身是亮斑,如果块内有星点,均值会被拉高,导致背景估计偏大。中值更鲁棒。

避坑指南:我曾经在强杂散光环境下,背景估计出现「块效应」——块与块之间有明显边界。后来改用重叠分块+加权插值,问题就解决了。

4.2 连通域分析:找到星点候选区域

预处理之后,图像里亮的地方就是星点候选。但怎么把每个星点单独拎出来?这就需要连通域分析。

4.2.1 二值化

先要把灰度图变成二值图。阈值怎么选?

  • 固定阈值:简单,但适应性差。不同星等、不同曝光时间,阈值都得调。
  • 自适应阈值:根据局部灰度统计动态计算。我常用的是「全局均值 + 3倍标准差」作为阈值。

说实话,阈值选择是星点提取里最玄学的部分。我一般会预留一个可调参数,在轨测试时根据实际图像微调。

4.2.2 连通域标记

二值图里,相邻的亮像素组成一个连通域。常用的算法有:

  • 两遍扫描法:第一遍给每个像素临时标号,记录等价关系;第二遍合并等价标号。效率高,适合嵌入式实现。
  • 种子填充法:递归或栈实现,简单但容易栈溢出。我一般不推荐在星上使用。

连通域分析后,我们会得到每个星点的像素集合。接下来就可以算质心了。

注意:连通域面积太小的(比如小于3像素)可能是噪声,直接丢弃。面积太大的(比如超过100像素)可能是热像素或卫星本体反射,也要排除。

4.3 质心定位:从像素到亚像素精度

星敏感器的精度要求通常是角秒级。如果只取像素中心,精度最多到像素级(比如1/2像素)。所以我们需要亚像素定位。

我常用的方法有两种:灰度重心法高斯曲面拟合法

4.3.1 灰度重心法

这个方法简单粗暴:把星点区域的灰度值当作「质量」,求加权平均位置。

// 灰度重心法计算质心
// (x0, y0) 为星点区域左上角坐标
// w, h 为区域宽高
// img 为灰度图像数据

float sum_x = 0.0f, sum_y = 0.0f, sum_g = 0.0f;
for (int j = 0; j < h; j++) {
    for (int i = 0; i < w; i++) {
        float g = (float)img[(y0 + j) * width + (x0 + i)];
        sum_x += (x0 + i) * g;
        sum_y += (y0 + j) * g;
        sum_g += g;
    }
}
float cx = sum_x / sum_g;
float cy = sum_y / sum_g;

优点:计算快,适合嵌入式实时处理。
缺点:对噪声敏感,星点不对称时精度下降。

4.3.2 高斯曲面拟合法

星点的能量分布理论上符合高斯函数。我们可以用二维高斯模型去拟合星点区域,得到更精确的中心位置。

二维高斯模型:

G(x, y) = A * exp(-((x - x0)^2 / (2 * σx^2) + (y - y0)^2 / (2 * σy^2))) + B

其中:

  • A:星点峰值强度
  • (x0, y0):质心位置(我们要的就是这个)
  • σx, σy:高斯宽度参数
  • B:背景偏移

拟合方法我常用最小二乘法。具体实现时,可以先取对数,把非线性问题转化为线性问题,再用最小二乘求解。

精度对比:灰度重心法一般能达到0.1~0.2像素精度,高斯拟合法可以到0.01~0.05像素。但高斯拟合法计算量大约是重心法的10倍。

我在项目中遇到过这样的情况:星点太暗(信噪比低),高斯拟合会发散。这时候我会退回到灰度重心法,或者先做一次重心法得到初值,再用高斯拟合做一次精化。

4.4 本章知识体系

下面这张图,是我自己总结的星点提取完整流程。你可以把它当作一个检查清单:

星点提取算法完整流程 图像预处理 去噪 + 背景抑制 连通域分析 二值化 + 标记 质心定位 亚像素精度 预处理方法 • 中值滤波(去椒盐噪声) • 高斯滤波(去高斯噪声) • 背景估计与减除 • 自适应阈值二值化 连通域方法 • 两遍扫描法 • 种子填充法 • 面积筛选(3~100像素) • 形状筛选(圆形度) 质心方法 • 灰度重心法 • 高斯曲面拟合 • 精度:0.01~0.2像素 • 速度:重心法更快 输出结果:星点质心坐标 (x, y) + 灰度信息 用于后续星图识别与姿态解算

4.5 工程实现要点

最后,我总结几个工程实现时的关键点:

环节 常见问题 我的建议
去噪 滤波过度导致星点丢失 先做星点检测,再局部滤波
背景抑制 块效应、背景估计不准 用重叠分块+中值统计
连通域 星点粘连、噪声误检 加形态学开运算分离粘连
质心定位 信噪比低时精度下降 重心法粗定位+高斯拟合精化

个人经验:星点提取的精度,70%取决于预处理,30%取决于质心算法。把预处理做好了,后面就轻松了。我曾经在一个项目里花了两周调质心算法,精度死活上不去。后来发现是背景抑制没做好,改完之后精度直接翻倍。

嗯,星点提取这部分就讲到这里。下一章我们会聊星图识别——怎么从一堆星点坐标里认出这是哪片星空。


公众号:蓝海资料掘金营,微信deep3321