跳到主要内容
线扫描成像里最难的那一环,其实是测速

线扫描成像里最难的那一环,其实是测速

abanana
abanana

· 阅读约 5 分钟

七月的 emf2026,我这两天才把 Stage A 那场 talk 的录像补完,Philomena Gray 的《Using the railway network as a flatbed scanner》,19 分钟。名字起得很准:火车就是平板扫描仪里那张往下滚的纸,铁路网就是那块玻璃板。做法不神秘,把一台工业线阵相机架在车窗外面,车一边跑它一边出图,每秒几千行,跑完再把这些行按顺序摞成一张极宽的图。

我一开始是当奇观看的,看完留下来的却是另外一件事:这套东西里最难的既不是相机也不是拼接,是测速。

线阵相机一次只出一行像素,这点同行都清楚。也正因为一次只出一行,图像的横向那一维来自传感器像元数,是个固定值;纵向那一维完全由行数决定——最后那张图有多「长」,不是光学问题,是个计数问题,你出了多少行,图就有多少行。

这直接带出那个比值。要让纵向和横向的比例尺一致、图既不被拉伸也不被压缩,就得满足:

线速率(行/秒) = 车速(米/秒) / 每行对应的地面距离(米/行)

拿数字跑一遍,火车按 30 m/s 算,也就是 108 km/h,希望每行覆盖 1 cm 的地面:

3000 行/秒 = 30 (m/s) / 0.01 (m/行)

正好落在几千行每秒这个量级上。也就是说这个规格不是随便定的,是车速决定的。想拍细一点,每行 5 mm,线速率就得翻倍到 6000 行/秒。顺着往下推一步还会碰到另一件事:行速率的上限其实不是相机能不能出这么快,而是每行的曝光窗口被压到了 1/6000 秒,实际曝光还得比这个更短,不然相邻的行会互相糊上——所以真正卡住你的是光够不够、快门能不能在这么短的窗口里收进足够的曝光。这段我绕了一圈才想明白,一开始以为瓶颈在传感器读出速度上。

然后才是真正卡人的地方。上面那个 0.01 m/行 是事后拼图用的常数,相机在车上跑的时候并不知道自己走多快。如果线速率一直按设定值跑,而车速变了——进站、爬坡、过弯、轮子打滑——图上的比例尺就跟着漂,而且这种偏差是沿纵向累积的,一小段速度对不上,几万行之后就是整片被压扁或者整片被拉长,事后修不回来。

talk 里提到测速难,我一开始没当回事,心想 GPS 就能给速度,有什么难的。后来把事情放到两个时间尺度上比了一下才明白:相机一秒出三千行,意味着每几百行的窗口里车速基本不能变,速度信号的更新频率得跟这个尺度对得上才行;而 GPS 的速度输出是平滑过的,更新频率差着好几个数量级,进隧道更是直接没信号。火车上现成的、延迟够低的速度来源不多,轮轴转速这类信号是常见选择,但它有自己的麻烦——轮径会磨损会变,刹车时轮子锁死给出的速度是错的,这些都得单独处理。录像里他没展开最后用的是哪种方案,这一段属于我也没完全搞清楚的,记下来算留个坑。

想自己动手的话,不用真去架相机,从一段普通视频里抽一列像素就能模拟。步骤大概是这样:

  1. 用手机或相机对着窗外,水平方向拍一段稳定的视频。别开防抖,防抖会让画面整体在动,把「横向第 x0 列」这句话直接废掉。
  2. ffmpeg 把视频拆成帧序列:
ffmpeg -i window.mp4 -vsync 0 frames/%06d.png
  1. 固定取每一帧的某一列像素,按帧的顺序横向摞起来。拼接本身真的就是几行代码:
import glob
import numpy as np
from PIL import Image

col = 800
frames = sorted(glob.glob("frames/*.png"))
lines = [np.asarray(Image.open(f))[:, col, :] for f in frames]
Image.fromarray(np.stack(lines, axis=1)).save("stitched.png")

跑完你会得到一张时间横轴、空间纵轴的图。匀速平移的时候,画面里的东西被拉成横向的条纹;速度变化的时候,条纹跟着扭。这张图不好看,但它的形变是可解释的,这一点比好看有用。

我一开始以为难点在存储和后期拼接,跑完这段发现拼接反而是最省事的一环。麻烦的是两处。

一是取列的时机。视频帧率是固定的,而你想要的「每行进 d 米出一行」不是固定的,除非能把帧的时间和位移对上,否则取列时机一错 → 图上的比例尺就整体偏。

二是怎么验证比例尺对不对。方法很朴素:路线上找一个长度已知的固定物,量它在成品图里占了多少行,反推每行对应的实际距离,跟设定的 d 比。对不上就说明速度估计有问题。

划重点:第一,线阵成像的纵向维是个计数问题,不是光学问题,出多少行就有多长;第二,规格是被车速决定的,30 m/s 配 1 cm 一行,就得到 3000 行/秒这个量级,想拍细就得让快门和光跟上;第三,GPS 的更新频率喂不动几千行每秒的相机,轮轴信号是更常见的来源,但轮径磨损和锁死得单独处理;第四,验证比例尺不用高级工具,找个长度已知的固定物,量它占多少行,反推一下就行。这篇就记到这里,你可以拿手边一段视频试试抽列拼接,如果踩到别的坑,欢迎回来留言,我一起补一条笔记。

abanana
abanana

把自己踩过的坑整理成一篇能复现的笔记,写给三个月前的自己看。

查看主页 →