---
title: 期末综合实战：电子时钟数码管识别与 GUI 封装
breadcrumb: 期末综合实战：电子时钟数码管识别
description: MATLAB 计算机视觉综合大作业。结合电子、通信与光电专业背景，从图像预处理、形态学缝合、连通域特征提取，一直到七段译码器解码，最终封装为带 GUI 交互的智能识别桌面软件。
head:
  - - meta
    - name: keywords
      content: MATLAB计算机视觉, 数码管识别, 7-Segment, 形态学处理, 图像分割, App Designer, 期末大作业
---

# 期末综合实战：电子时钟数码管识别与 GUI 封装

这是本课程的期末大作业。我们将编写一个程序，自动识别照片中的电子时钟七段数码管（7-Segment Display）数值。

硬件开发中常通过单片机控制数码管显示数字。这个项目则是反向操作：输入一张数码管照片，用 MATLAB 提取并识别屏幕上的数值。

<img :src="'/p/clock.png'" alt="数码管原始图像" />

*(测试素材：可右键保存上图作为本次实验的标准输入图像。)*

---

## 1. 图像预处理与阈值分割

图像识别的第一步，我们需要将目标对象（红色的 LED 发光管）从黑色的背景中单独提取出来。提取后的结果将是一张只有黑白两色的“二值化掩膜（Mask）”图像，其中白色的区域代表数字，黑色的区域代表背景。

### 1.1 原理铺垫

在看代码前，我们需要先理解两个底层机制：

1. **图像在内存中的表示 (`imread`)**：
   使用 `imread` 读入一张彩色数字照片时，MATLAB 会将其解析为一个三维矩阵（长 $\times$ 宽 $\times$ 3）。这第三个维度代表了红 (R)、绿 (G)、蓝 (B) 三个色彩通道。这也是为什么稍后我们可以使用 `img(:,:,1)` 单独把红色层提取出来。
2. **逻辑矩阵生成**：
   当对一个矩阵进行大小比较运算时，MATLAB 不会报错，而是会返回一个同等大小的、只包含 `0` 和 `1` 的“逻辑型矩阵”。例如，对矩阵 `A = [10 50 100]` 执行 `mask = A > 90`，就会得到结果 `[0 0 1]`。我们可以利用这个特性，判断哪些像素的色彩值满足我们的要求。

### 1.2 算法实现

由于目标数码管是高亮红色的，它的红色通道 (R) 像素值必然显著高于绿色 (G) 和蓝色 (B) 通道。我们可以设定合适的阈值，利用逻辑运算符（`>` 和 `&`）将红色区域筛选出来：

```matlab
% 读取图像文件，img 是一个 MxNx3 的三维矩阵
img = imread('digital_clock.png');

% 提取 R、G、B 通道分量矩阵
R = img(:,:,1);
G = img(:,:,2);
B = img(:,:,3);

% 色彩差值分割：提取红色分量显著大于绿色和蓝色的区域
% 设定阈值：红通道绝对值大于 150，且与蓝绿通道的差值大于 50
bw = (R > 150) & (R - G > 50) & (R - B > 50);

figure;
imshow(bw);
title('预处理二值图像');
```

### 闯关题目 1

1. **举一反三**：如果我们面对的是一张蓝色的指示灯照片，想要把蓝色的发光区域提取出来，那么上面的阈值提取逻辑代码（即 `bw = ...` 这一行）应该做怎样的思路调整？
2. **算子辨析**：在上面的代码中，我们使用了 `&` 符号连接三个条件。在 MATLAB 中，表示“逻辑与”和“逻辑或”的符号分别是什么？

<details>
<summary>▶ 查看答案</summary>

1. **举一反三**：需要以蓝色通道为主进行比较。代码逻辑应调整为提取“蓝色通道绝对值较大，且显著大于红色和绿色通道差值”的区域。例如可能写为：`bw = (B > 150) & (B - R > 50) & (B - G > 50);`。
2. **算子辨析**：“逻辑与”的符号是 `&`，“逻辑或”的符号是 `|`。在进行矩阵维度的条件筛选时，这两个算子非常常用。

</details>

---

## 2. 形态学运算与连通域分析

提取二值化图像后，因为七段数码管的相邻线段在物理上是断开的，如果直接查找连通域，一个数字会被拆成多个碎片。

### 2.1 核心知识点

*   **形态学闭运算 (`imclose`)**：形态学中的闭运算是指“先膨胀后腐蚀”的过程。它能够在不明显改变目标区域总面积的前提下，填补目标内部的细小空洞，并“缝合”断裂的微小间隙。
*   **结构元素 (`strel`)**：用于定义形态学运算的邻域范围和形状，如矩形、圆形等。
*   **连通域特征提取 (`regionprops`)**：MATLAB 用于测量图像区域属性的函数，可快速获取目标的面积、质心坐标以及用于裁切的边界框 (`BoundingBox`)。

### 2.2 算法实现

通过构建合适的结构元素并执行闭运算，我们将同一个数字内断开的段缝合为单一的连通域。

```matlab
% 1. 构造结构元素：采用 15x15 的矩形窗口以覆盖线段间的缝隙
se = strel('rectangle', [15, 15]); 

% 2. 执行闭运算：缝合物理断裂
bw_closed = imclose(bw, se);       

% 3. 噪声过滤：移除面积小于 500 像素的孤立噪点
bw_clean = bwareaopen(bw_closed, 500); 

% 4. 特征提取：获取连通域的边界框 (BoundingBox) 和面积 (Area)
stats = regionprops(bw_clean, 'BoundingBox', 'Area');

% 5. 坐标排序（关键防坑点）
% regionprops 找出的连通域不一定是严格从左到右的。
% 为了保证最后读出来的时间顺序不乱套，我们得按 X 坐标给它们重新排个序：
num_regions = length(stats);
x_coords = zeros(1, num_regions);
for i = 1:num_regions
    x_coords(i) = stats(i).BoundingBox(1); % 提取每个框的 X 坐标
end
[~, sort_idx] = sort(x_coords); % 从小到大排序
stats = stats(sort_idx);

figure;
imshow(bw_clean); hold on;

% 遍历并在处理后的二值图像上绘制定位框
for i = 1:length(stats)
    bbox = stats(i).BoundingBox;
    % 依据先验知识过滤非目标区域（如中间的冒号，其高度通常较小）
    if bbox(4) > 50  
        rectangle('Position', bbox, 'EdgeColor', 'g', 'LineWidth', 2);
    end
end
title('形态学运算与目标定位结果');
```

### 闯关题目 2

1. **结构元素的尺度**：在上述代码中，如果将结构元素修改为 `se = strel('rectangle', [3, 3])`，最可能导致什么负面结果？
2. **特征属性应用**：除了使用高度 (`bbox(4)`) 过滤中间的冒号外，结合 `regionprops` 提供的信息，还可以使用什么特征参数来剔除面积远小于正常数字的噪点？

<details>
<summary>▶ 查看答案</summary>

1. **结构元素的尺度**：`3x3` 的结构元素尺寸过小，可能无法完全覆盖并“跨越”数码管各段之间的物理缝隙。这将导致闭运算失败，同一个数字依然保持为多个独立的碎片连通域。
2. **特征属性应用**：可以直接使用 `stats(i).Area` 属性。由于冒号的像素面积远小于正常数字，通过设定 `if stats(i).Area > 阈值` 即可有效剔除。

</details>

---

## 3. 区域采样与译码算法

找到每一个数字所在的精确区域后，就可以提取图像数据并识别数字了。

### 3.1 核心知识点

*   **矩阵局部切片**：结合 `BoundingBox` 的坐标与尺寸，使用矩阵索引提取局部图像块。
*   **网格采样与逻辑映射**：模拟硬件的“七段译码器”。将数字区域划分为特定的网格。通过判定这 7 个对应区域（A 至 G 段）内的像素白点分布，生成一个形如 `[1 1 1 1 1 1 0]` 的数组，再对应到具体的数字。

### 3.2 算法实现思路

由于要求学生独立完成该核心算法模块，在此提供算法伪代码与设计指引：

```matlab
% 提取单个数字图像块的伪代码
% bbox 的格式为 [x, y, width, height]
digit_img = bw_clean(round(y) : round(y+height), round(x) : round(x+width));

% ==============================================
% 核心识别流程：切图 -> 提特征 -> 译码
% ==============================================

% 【前置特判】：单独处理数字 1
% 数字 1 太窄了，按常规方法切片容易切乱。
% 可以加个特判：如果算出来宽高比 w/h < 0.35，直接判定它是数字 1，然后 continue 跳过后续步骤就行。

% 【第 1 步：切图】划定采样范围（避开干扰和斜体）
% 先用 size 拿到切片的高 H 和宽 W。
% 取横向线段（上、中、下）时，宽度别取满，只抠中间一小块（比如 W 的 40% 到 60%），不然容易把左右两边的竖线也框进来。
% 取纵向线段（左上、右上、左下、右下）时刚好相反，高度要避开横线，宽度可以往中间放宽点到 40% 左右，这样即使数码管带点斜体也不怕切歪。

% 【第 2 步：提特征】算出这 7 个区域是不是亮的
% 按照上一步的范围，把你切出来的 7 个小方块存好。
% 用 mean(region(:)) 算一下里面白色像素的比例。
% 阈值不用设太高，大于 0.15 基本就算亮起（逻辑 1）了，否则就是灭的（逻辑 0）。
% 最后把这 7 个结果拼成一个特征码数组，比如 code = [1, 1, 1, 1, 1, 1, 0]。

% 【第 3 步：译码】查表对暗号
% 拿到 code 后，用 isequal 函数去跟 0 到 9 的标准特征码比对。
% 比如 isequal(code, [1,1,1,1,1,1,0]) 就是数字 0。
% 写一排 if-elseif 搞定。

% 💡 进阶小挑战：
% 嫌全写在主循环里太长？你可以试着把“抠图取特征”和“查表认字”这两步，
% 分别封装成两个独立的辅助函数（比如 extract_7seg_features 和 seg7_decode），
% 你的主程序会变得非常清爽和专业！
```

---

## 4. 综合实践项目：GUI 智能识别应用

在前三节的代码跑通后，你需要把它们整合成一个带有交互界面的桌面程序。

### 作业要求与交付标准

1. **前端界面设计**：在 MATLAB 命令行启动 `appdesigner`，设计一个合理的桌面应用程序布局。需至少包含：
   * 图像导入触发按钮。
   * 核心算法执行触发按钮。
   * 两个以上的 `UIAxes` 用于比对显示原始图像与图像分割定位中间过程图。
   * 一个 `Label` 文本标签，用于以大号字体显示最终拼接出的时间字符串结果（例如 `11:58 19`）。
2. **后端逻辑封装**：将第 1 至 3 节实现的过程代码封装入自定义函数，并绑定至相应的组件回调函数 (`Callback`) 内。
3. **交付物要求**：
   * `*.mlapp` 源程序文件。
   * 独立的作业报告（PDF 格式）。需包含：程序流程图、0-9 的特征码定义列表，以及形态学操作前后的效果对比。
