OpenCV 在 MicroLED 评价中的使用心得:从图像采集到工程判断
OpenCV 在 MicroLED 评价中的使用心得:从图像采集到工程判断
我做 MicroLED 相关开发这些年,一个体会越来越明确:很多关键指标,最后都不是“算出来”的,而是先“看见了”,再想办法把它变成可计算、可比较、可复现的数据。
亮度均匀不均匀、有没有ドット抜け(Dead Pixel)、配光图案有没有跑掉、某个模式切换后是不是出现异常热点——这些问题本质上都和图像有关。所以对半导体工程师,尤其是做 MicroLED、光学评价、显示/照明系统的人来说,图像处理不是附属技能,而是把现象转成工程判断的基础能力。
但我也想先说清楚一件事:OpenCV 不是万能工具,更不是量产主力。 在我的日常工作里,它最重要的价值不在“最终上线”,而在 R&D 阶段的快速验证。我所在的链路里,真正承担高速、稳定、可追溯判定的是 FPGA + BIST;而 OpenCV 更像是一个试验场,用来验证算法原型、确认评价逻辑、探索失效模式。
如果把这件事说得更直白一点:我不是把 OpenCV 当成“图像课程”,而是把它当成工程开发中的一把快刀。它的意义,是让我在问题还没有被完全定义清楚的时候,先用最短路径把思路跑通。
在我这组“图像处理”相关文章里,这篇更接近一篇实践篇:
- 如果你想看我在 MicroLED 评价现场到底怎么用图像处理,这篇最具体;
- 如果你想看 OpenCV 之外还有哪些工具值得关注,可以继续读《OpenCV 之外,还有哪些值得关注的开源图像处理工具?》;
- 如果你想看从实验室原型走向量产系统时,工具应该怎么组合,可以接着读《图像处理工具怎么选:从实验室原型到量产系统的开源工具组合》。
我的工作流:FPGA 驱动,OpenCV 判断,硬件负责落地
我现在的日常工作流,大致可以概括成下面这条链:
FPGA 驱动 MicroLED 阵列 → 相机采集图像 → Python + OpenCV 做分析 → 逻辑稳定后迁移到 FPGA / C++ / BIST
这条链路里,OpenCV 的位置非常明确。
第一,它离实验很近。只要样品能点亮、相机能稳定取像,我就能很快开始分析,不需要先搭一个很重的软件系统。第二,它和 Python 配合得很好,适合快速试错。现在再叠加 AI/LLM 的辅助,很多统计、可视化、掩膜处理、批量脚本都能更快搭起来,这对研发阶段特别有价值。第三,它足够透明。阈值怎么设、ROI 怎么切、连通域怎么判、热力图怎么生成,全部都看得见,适合和工艺、驱动、光学团队一起讨论。
我很少把 OpenCV 当成“最终答案”。我更常把它当成一个中间层:先验证这个评价逻辑是不是靠谱,先搞清楚异常长什么样、判定边界在哪里、环境因素会不会误导结果。等这些事情稳定了,再决定哪些部分值得搬到实时硬件里。
也正因为这样,我在研发阶段最看重的不是代码优雅,而是:能不能尽快把一个模糊问题变成清晰指标。
我最常用的三个场景
下面这三个场景,是我在 MicroLED 评价里最常用 OpenCV 的地方。代码我刻意写得很短,因为真正重要的不是 API,而是背后的判断思路。
1. 亮度均匀性:先把“看起来不均”变成热力图
做阵列评价时,我一般不会先看整张图的平均值。平均值太容易掩盖问题。更实用的方法,是先确定发光 ROI,再按网格切分,把每个小区域的平均亮度做成二维矩阵,最后看它的空间分布。
这样做的好处是,很多问题会立刻从“感觉不对”变成“模式很明显”。如果是整片偏暗,可能要看驱动或工艺;如果是边缘系统性变暗,要怀疑镜头暗角、光轴偏移或拍摄姿态;如果是按行按列出现周期变化,往往就不是单点缺陷,而更像扫描或供电问题。
import cv2, numpy as np
img = cv2.imread("capture.png", cv2.IMREAD_GRAYSCALE)
roi = img[120:760, 180:820]
rows, cols = 16, 16
gh, gw = roi.shape[0] // rows, roi.shape[1] // cols
grid = np.array([
[roi[r*gh:(r+1)*gh, c*gw:(c+1)*gw].mean() for c in range(cols)]
for r in range(rows)
], dtype=np.float32)
cv = grid.std() / (grid.mean() + 1e-6)
heat = cv2.applyColorMap(
cv2.normalize(grid, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8),
cv2.COLORMAP_JET
)
print("uniformity_cv =", round(float(cv), 4))
cv2.imwrite("uniformity_heatmap.png", cv2.resize(heat, None, fx=24, fy=24, interpolation=cv2.INTER_NEAREST))
这段脚本很简单,但已经能回答研发阶段最关键的问题:当前不均匀到底是随机散点,还是带有方向性、周期性、区域性。对我来说,热力图不是“可视化锦上添花”,而是找根因的第一入口。
2. 死像素检测:不是只找“不亮”,而是找“异常”
很多人一说 Dead Pixel,就默认是“应该亮的地方完全不亮”。但在实际评价里,异常往往没有这么干脆。更常见的是偏暗、面积异常、亮度漂移,甚至只在某些驱动条件下偶发。
所以我做这类检测时,通常不会只写一个二值化阈值就结束,而是把它当成一个“异常筛选”流程:先把候选区域提出来,再结合连通域面积、强度、位置规则性去判定。这样虽然还是传统方法,但在 R&D 阶段非常高效。
import cv2, numpy as np
img = cv2.imread("pixel_on.png", cv2.IMREAD_GRAYSCALE)
blur = cv2.GaussianBlur(img, (3, 3), 0)
_, th = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
num, labels, stats, _ = cv2.connectedComponentsWithStats(th, 8)
areas = stats[1:, cv2.CC_STAT_AREA]
base = np.median(areas) if len(areas) else 0
bad = np.zeros_like(img)
for i in range(1, num):
area = stats[i, cv2.CC_STAT_AREA]
if area < 0.5 * base or area > 1.8 * base:
bad[labels == i] = 255
print("suspect_count =", int((bad > 0).sum() > 0))
cv2.imwrite("dead_pixel_mask.png", bad)
这当然不是完整量产算法,但它足够快,适合拿来验证:当前成像条件下,缺陷能不能被稳定分离;阈值是不是过敏;异常到底更像工艺问题、污染、还是对焦/曝光问题。
我自己的经验是,Dead Pixel 检测最难的地方不在“会不会写连通域”,而在 你是不是清楚什么才叫异常。如果参考样本、曝光条件、点亮模式都不稳定,算法再复杂也只是在放大噪声。
3. 配光图案分析:关注的不是像素,而是区域是否满足设计意图
当对象从芯片表面发光,转到远场光型时,分析逻辑也会变化。这个阶段我不再先盯单颗像素,而是先看分区结果:热点是否落在该亮的地方,防眩区是不是足够暗,截止线附近是否稳定,模板比对有没有越界。
这类问题非常适合先用 OpenCV 做原型,因为掩膜、区域统计、轮廓提取都很成熟,而且可以很快和法规模板或设计目标对上。
import cv2, numpy as np
img = cv2.imread("farfield.png", cv2.IMREAD_GRAYSCALE).astype(np.float32)
img /= img.max() + 1e-6
zones = {
"hot": cv2.imread("mask_hot.png", 0),
"glare": cv2.imread("mask_glare.png", 0),
"cutoff": cv2.imread("mask_cutoff.png", 0),
}
limits = {"hot": (0.45, None), "glare": (None, 0.08), "cutoff": (0.10, 0.30)}
for name, mask in zones.items():
v = cv2.mean(img, mask=mask)[0]
lo, hi = limits[name]
ok = (lo is None or v >= lo) and (hi is None or v <= hi)
print(name, round(v, 4), "PASS" if ok else "FAIL")
这类分析的关键,不是代码本身,而是你有没有把“设计意图”或“法规模板”翻译成可比较的区域指标。只要这一步定义清楚,OpenCV 就非常适合做快速验证。
我在用 OpenCV 时最常踩的坑
和代码相比,我觉得下面这些经验更值钱。很多误判,不是算法不够高级,而是采集条件从一开始就错了。
自动曝光和白平衡,会直接毁掉定量分析
如果你的目标是做定量评价,那自动曝光、自动增益、自动白平衡基本都应该先关掉。因为一旦相机在每次拍摄时自己调整,你看到的差异就不再只来自样品,也来自相机算法。
我见过很多“亮度改善”或者“颜色变稳”的结果,最后追溯发现其实只是相机自己补偿了。那种结果对工程判断几乎没有意义。
镜头暗角和环境光,是最常见的误判来源
阵列边缘偏暗,并不一定是器件问题;热点位置偏了,也不一定是图案本身跑掉。镜头暗角、光轴偏斜、样品倾斜、环境杂散光,这些都很容易把系统误差伪装成样品缺陷。
所以我现在做分析,习惯上会先问三个问题:
- 这张图是不是在固定曝光下拍的?
- 成像链路有没有做过基本校正?
- 现在看到的模式,是否可能由光学系统本身造成?
如果这三件事没确认,我不会太早相信算法输出。
单帧分析,抓不到间歇性缺陷
很多缺陷不是一直存在,而是跟驱动模式、温升、时序边界有关。单张图看起来正常,不代表器件真的稳定。尤其是某些偶发掉点、闪烁、亮度抖动,如果不做时间序列统计,基本抓不到。
这也是我很少把单帧结果当最终结论的原因。只要条件允许,我会尽量做多帧采样,再看缺陷出现的概率、持续时间和触发条件。对研发来说,这比单次“判坏”更有信息量。
从 OpenCV 到产线:为什么最后还是要回到 FPGA
我一直很喜欢用 Python + OpenCV 做前期验证,因为它真的快:采图、切 ROI、调阈值、画热力图、导出 CSV,一天内就能跑出很多东西。再加上现在可以借助 LLM 辅助搭脚手架,试错成本比以前更低。
但如果问题进入量产阶段,诉求就会完全变掉。量产关心的是节拍、实时性、可重复性、边界条件覆盖、系统集成、维护成本。到了这里,Python 脚本再灵活,也很难替代 FPGA 或更严格的实时实现。
所以在我的认知里,OpenCV 的角色一直很清楚:
- 它不是主力量产工具;
- 它是研发阶段最快的验证工具;
- 它负责把模糊的想法变成可讨论的逻辑;
- 它帮助我确认哪些判断值得迁移到硬件;
- 一旦判定流程稳定,就应该尽早往 FPGA / C++ / BIST 那一侧收敛。
换句话说,OpenCV 在评价流程里的位置,不是终点,而是 从原型走向量产之间的桥梁。这恰恰也是我最看重它的地方。
结尾:图像处理不是附属技能,而是让光学问题变得可计算
如果让我用一句话总结 OpenCV 在 MicroLED 评价中的价值,我会说:它不是为了把图像处理得更漂亮,而是为了让工程师更快做出判断。
对半导体工程师来说,图像处理的意义不在“会多少库函数”,而在你能不能把原本依赖经验的现象,转成稳定、可复现、可比较的数据流程。只要你在做的是发光阵列、光学评价、缺陷识别、配光判断,这项能力迟早都会变成你的日常工具。
我自己这些年越来越相信,图像处理不是软件团队专属的技能,而是 MicroLED 工程里一项很实用的基础能力。它让很多“看起来像经验”的问题,真正进入可以验证、可以讨论、可以迁移到硬件的工程范畴。
这也是我今天还在持续使用 OpenCV 的原因:它不是最终答案,但它常常是最快抵达答案的路。