Day 1 需求分析和计划

概述

软件开发的第一步是业务和需求分析,明确软件的使用场景,业务目标,并在此基础上总结处软件的需求。这一过程有各种名称,各种方法,取决于不同的软件,领域,内外部环境等。一般而言,承接的外部需求,或者需要对外分包的系统,需求分析的要求就很高。对于复杂的系统,变更代价高昂的产品,前期需求分析的要求也比较高,而对于简单的,企业内部的项目,软件需求分析通常没有太高的,过于形式化的要求。而对于需求变化充满了不确定性的产品,则需要考虑原型技术。我们这个系统本身是一个大的系统的一部分,有明确的上级需求输入,规模也不大,因此并不需要太正规和形式化的需求分析。需求分析和软件架构设计通常是耦合在一起的。尤其是,当规划者,设计者,实现者是同一个/批人的时候,这些工作通常是在一起进行的。

简单的来说,我们需要做下面的几项工作:

  • 明确外部功能和性能需求
  • 明确业务场景和使用模式
  • 明确硬件设备约束
  • 明确子系统间的接口

首先是明确外部需求和使用场景/模式。
外部需求一般是从产品规划处得到。通常,市场规划和产品分析会给出产品要实现哪些功能和特性,要达到多高的性能,可靠性,成本等要求。
使用场景与使用模式则需要和潜在的用户,领域专业人士进行沟通交流得到。

这里,比较推荐利用use case来分析需求的方法。use case,国内译作用例用况,是Ivar Jacobson在1986年前后提出的技术。它是对系统如何反应外界请求的描述,是一种通过用户和使用场景来获取需求的技术。它是在不展现一个系统或子系统内部结构的情况下,对系统或子系统的某个连贯的功能单元的定义和描述。更详细的内容读者有兴趣可以去看一下机械工业出版社的《编写有效用例》一书。

比如,就我们的玻片扫描,我们通过阅读书籍和教材,了解到基本的操作要求,还需要和病理科医生沟通,学习和了解他们的工作方式和方法。比如,经过沟通和观察,我们得到了如下一些关键信息:

  • 医生拿到一张玻片,会先看一眼玻片上涂层的形状,大致判断出应该在那里观察计数。对于手推的玻片,就白细胞而言,观察区域是涂片区域接近尾部的部分,在低倍镜下应该看到细胞单层,没有重叠。如果太靠近头部,血细胞厚度太后,细胞重叠,不利于区分和观察;如果太靠近尾部,白细胞很容易在推片的过程中被破坏,导致观察到大量无效的退化细胞。
  • 白细胞的计数必须是一个连续区域内的所有细胞,不管是骨髓还是外周血。在这个区域中,不能有被遗漏的细胞。也就是说,不能挑着“好看”的细胞采集,以确保可信息
  • 细胞的分类在很多时候还需要参考它的周围
  • 对健康人的外周血涂片,白细胞是比较稀疏的,一般要移动几个百倍视野才能找到一个白细胞,而在骨髓涂片中,白细胞的密度会很高,通常一个百倍视野中能看到数个甚至数十个白细胞。然而,对于病患,则不是这样。例如,急性白血病的外周血中会有大量的白细胞存在,而化疗晚期病人,即使是骨髓,白细胞密度也极为稀疏,甚至找遍整张玻片都凑不到100个数目要求。
  • 等等等等

这些信息都是进行需求分析的输入,基于这些信息,我们会形成我们的系统用例。

Use Case示例:单张手工扫描

例如,我们考虑一个简单的场景,用户手工进行一张玻片的扫描。我们最终整理出它的用例来。

用例描述有各种方法和工具。我们这里使用最简单的,文字描述的方式,只描述最重要的内容

Actor:

操作员

前置条件:

  • 扫描仪已开机
  • 扫描软件已经启动,已经与扫描仪连接,已经与后台服务器连接
  • 扫描软件处于等待状态

操作步骤:

  1. 操作员手工将一张玻片放到载物台上

  2. 操作员在界面上启动单张扫描功能

  3. 扫描仪进片,拍摄玻片的标签图和涂片区域的整体照片

  4. 本软件分析标签图,寻找二维码并进行解析。如果是符合要求的二维码,可以从中解析得到样本编号(sampleID),样本类型(sampleType)(外周血还是骨髓),病人识别号(caseNo)。如果从标签中识别二维码失败,或者内容格式非法,则sampleType为默认值外周血,其他字段为空

  5. 弹出界面让用户输入或确认扫描的参数。包括:

    • 样本类型
    • 样本编号(Optional)
    • 病例编号(Optional)
    • 是否扫描各种细胞(白细胞,红细胞,巨核细胞)及目标个数
    • 白细胞的扫描方式(红细胞和巨核细胞是固定的不可选择)
    • 等等
  6. 用户确认之后,开始扫描。在扫描过程中,在本软件的界面上应当能够显示扫描的过程。包括动态的对焦和最终结果的显示。

    • 若在前面用户指定了审核扫描区域,则弹出界面让用户修改十倍扫描的范围
    • 在扫描过程中,扫描得到的照片在主界面上依次更新呈现
    • 扫描的照片会被打包到zip格式的归档文件中
    • 视野信息要能记录到索引文件中
  7. 软件对得到的十倍图进行分析,计算出要做百倍扫描的区域。如果是连续扫描,那么是一个或几个矩形的区域;如果是非连续扫描(外周血这种),则是每个要拍摄的位置的坐标。

    • 若在前面指定了审核扫描区域,则弹出界面让用户审核和调整百倍扫描的范围。
      • 这里,连续扫描时的范围指的是要扫描的区域(矩形区域)
      • 非连续扫描时指的是扫描这个区域内的白细胞
  8. 执行百倍扫描。在扫描过程中,应当在主程序界面上连续显示对焦过程和得到的每一张照片

  9. 对于百倍扫描的照片,如果是非连续扫描,每张照片独立保存;如果是连续扫描,则要对这些照片做无缝拼接,并按照指定大小切割成小图片保存

  10. 扫描的所有图片和其他的信息最终上传给后台服务器做分析处理。

  11. 完成扫描之后,载物台立即退出,此时用户可以取走扫描完成的玻片,并放入另一张玻片进行扫描。而这个时候,上一张玻片的的拼接,打包,上传等工作应当在后台进行。即扫描工作不应当被数据处理阻塞

后置条件:

非功能需求和约束

软件除了功能需求之外,还有各种非功能的需求,比如性能需求,可靠性需求,可测试性需求,可维护性需求,部署需求,安全需求,成本需求,可获得性需求等等。这些需求通常无法在用例中表述,但同时又是项目成败的关键因素,并且对开发有着至关重要的影响。

比如,商品规格中有吞吐量规格,即全自动化模式下,一天能够扫描XX张标准玻片。针对这个需求,我们计算出每张玻片的全流程时间,其中,自动化送片机的送片,取片,扫描仪的进片,退片要化多少时间,这是死的时间消耗,硬件确定之后就无法改进了。剩下的时间是留给扫描仪的扫描时间。这个时间我们又进一步估计出低倍扫描的时间,高倍扫描的时间,这些时间够不够,如果不够,又能采取哪些方法来优化满足。

再比如,我们和医生沟通,了解他们的工作模式,了解他们如果使用我们的设备做扫描分析时,从开始扫描到得到结果能够容忍的时间。这个时间不仅仅是扫描的时间,还有上传,分析,结果输出的时间。我们首先要分析当前的硬件能否达到设计要求,如果不能,那么首要的问题是更改硬件选型或调整产品规格;如果硬件能够达到要求,就要分析软件设计是否受到性能约束的影响。比如是否要做多线程并发,等等。

再比如,我们需要和技术支持工程师了解设备进医院的流程,了解出现硬件问题后的处理流程。比如,我们得到如下的可靠性和可维护性要求:

  • 要求玻片扫描过程中出现了异常(例如,对焦失败,找不到细胞)后能够正常返回,并继续下一张玻片的扫描
  • 送片机要求连续取还片XX张无失败,要求取还片过程中损坏玻片的概率不超过1张/XX张
  • 要求XX%的故障可以软件处理,XX%的故障可以通过简单的软件复位解决,XX%的故障可以通过设备上下电解决,XX%的故障可以通过现场操作人员通过插拔连线解决。需要支持工程师到现场的次数不能超过XX/XX,等等。

所有这些都会对我们的软硬件产生至关重要的影响,甚至会否决硬件方案的选型。因此必须在立项之初就评估清楚。

所有这些东西我们这里不再赘述。在后面,涉及到具体的特性时,我们会逐步考虑。

系统方案和软件架构的确定

明确了需求之后,就是确定软件方案和架构的时候了。

  • 首先,这是一个本地应用程序,因为涉及到图像,硬件,AI,性能等因素,我们排除了所有的基于Web的方案。
  • 因为商务等方面的原因,我们必须使用C++开发,排除C#/WPF/WinForm方案,因为开发人员技能原因,排除MFC,选择C++/Qt方案。
  • 因为选择的扫描仪只有Windows下驱动,所以软件运行环境为Windows10/11中英文版
  • 综合考虑成本等因素,电脑配置等级为i7级别,内存不低于16G,空闲硬盘不低于1G,最低N1660显卡,显存不低于8G

这些内容也会对最后的软件架构和实现方案产生影响。例如,硬件限制了内存不得大于16G,而400万像素的一张照片在内存中的大小大约为15M,我们可以大致估计出内存中最多同时存放500张照片,如果再保守一些(比如,医生会用这台电脑做别的事情),那么就是200-300张照片。而我们对玻片做10倍的连续全片扫描时,照片的数量是大约1000张,而对一个3mmx3mm的连续区域做百倍连续扫描时,得到的照片数量也大约是1000张这个数量级。所以,在处理算法上,我们必须实现图片的随用随放,1000张400万像素的细胞照片在中等图像质量的JPG文件大约占用1.5~2G的磁盘空间,按照1T的可用磁盘空间来算,只要不到500张玻片就会将整个磁盘耗尽,所以本地的临时图像数据文件需要自动清除,同时软件要能够识别磁盘空间不足的问题,并提示用户进行处理。甚至,它还会对我们采用机械硬盘还是固态硬盘产生影响。

讨论了这些之后,对于这些细节我们就不会再详细讨论了,我们会直接进行下一步的工作。在后面的开发过程中,涉及到上述内容时,我们再会讨论它的影响和对策。

所有这些内容完成之后,我们得到了软件的总体架构设计和模块划分。那么,接下来,才是我们的软件计划和开发工作。

制订开发计划

接下来需要制定开发计划。这里的“计划”,是一种很轻量级的计划,或称称为Build计划。它是一个自顶向下的分解结构,定义了软件的构建计划。例如,假定我们只需要考虑单张扫描和批量扫描两个基本功能,则顶层计划如下:

1
2
3
graph LR
玻片扫描 --> 单张扫描 & 批量扫描 --> 数据上传
批量扫描 --> 送片机操作

对“单张扫描”,我们继续分解:

1
2
3
4
5
6
7
8
9
10
11
graph TB
A1[单张扫描]
B1[基本项目]
B3[配置参数读取]
B2[连接扫描仪]
B4[预览图扫描]
B5[低倍图扫描]
B6[高倍图扫描]
B7[数据上传]

A1 --> B1 & B3 & B2 & B4 & B5 & B6 & B7

基本项目结构的工作进一步分解为:

1
2
3
4
5
graph TB
单张扫描 --> 基本项目
基本项目 --> 定义项目结构
基本项目 --> 界面框架 & 驱动桩
基本项目 --> 单元测试QTest

<**预览图扫描**>功能可以分解为:

1
2
3
4
5
6
graph TB
预览图扫描 --> 数据保存 --> 保存图像 & 保存索引
保存图像 --> Zip操作
保存索引 --> SQLite
预览图扫描 --> 预览图选区
预览图扫描 --> 界面更新显示 --> 显示预览图 & 显示进度图

真正的扫描活动分解:

1
2
3
4
5
6
graph TB
单张扫描 --> 低倍图扫描 & 高倍图扫描
低倍图扫描 --> 低图选区 & 低倍图显示 & 数据保存
低倍图选区 --> 低倍图分析 & 细胞寻找
高倍图扫描 --> 连续扫描拼图 & 数据保存 & 高倍图显示
数据上传 --> 网络访问

我们接下来就会按照上面的计划进行开发活动。