# C++ 尿检项目 JSON 数据格式识别逻辑分析 > 基于 `/home/ubuntu/nanocode/urinecheck/UrineCheck/` 源码分析 > 分析日期:2026-05-13 --- ## 1. 概述 C++ 尿检项目支持两种输入模式: - **图片模式**:传入试纸图片(JPG/PNG),从图像中定位色块并识别 - **JSON数据模式**:直接传入已提取的色块颜色数据(H,S,V,L,A,B),跳过图像处理直接进行ML推理 本文档专注于 **JSON数据模式** 的完整处理流程。 --- ## 2. 入口函数 ### 2.1 `ProcessJsonDataRequest` (新版接口,匹配txt文件格式) **位置**:`urinecheck_class.cpp:2052` **输入JSON格式**: ```json { "extend": { "color_14i": 2, // 试纸颜色类型(1=绿, 2=红, 3=蓝4联, 5=蓝14联...) "method": 1, // 1=干化学, 2=胶体金 "type": 2 // 可选,优先级高于color_14i }, "data": [ [ // 第1帧(多帧采集,通常2-5帧) {"id": 0, "value": "7.62,146.43,37.76,23.22,135.71,134.46"}, {"id": 1, "value": "24.73,38.34,155.7,159.45,126.1,138.93"}, ... {"id": 13, "value": "36.38,100.93,155.55,157.81,113.17,158.8"} ], [...] // 第2帧 ] } ``` **value字段含义**:6个逗号分隔的浮点数,对应: `H, S, V, L, A, B` | 位置 | 符号 | 含义 | 取值范围 | |------|------|------|----------| | 0 | H | HSV 色相 | 0-360(但OpenCV存储为0-180) | | 1 | S | HSV 饱和度 | 0-255 | | 2 | V | HSV 明度 | 0-255 | | 3 | L | LAB 亮度 | 0-255 | | 4 | A | LAB a通道 | 0-255 | | 5 | B | LAB b通道 | 0-255 | > **注意**:txt文件中的value数据已经是 **原始手机端提取的像素颜色值**,与C++图片模式中 `GetHsvLabVs()` 提取的格式一致。这些值在C++中未经白平衡处理就直接送入ML模型。 ### 2.2 `analJsonData` (旧版接口) **位置**:`urinecheck_class.cpp:746` 旧版使用不同的data结构——`data` 是一个对象而非数组: ```json { "data": { "data0": [{"id": 0, "value": "..."}, ...], "data1": [{"id": 0, "value": "..."}, ...] } } ``` 旧版的 `id` 直接对应试纸上的物理位置(position),而新版 `id` 就是色块索引。 --- ## 3. 完整处理流程 ``` ProcessJsonDataRequest(jsonBody) │ ├── 1. 解析JSON │ └── 提取 data 数组、extend 对象 │ ├── 2. 确定试纸类型 │ ├── extend.type 优先 │ ├── extend.color_14i 其次 │ └── paperType = paperType值 (1-20) │ X = paperType - 1 (用于查表索引) │ ├── 3. 转换项目类型 │ └── projectId = getProjectType(paperType) │ ├── 1 (绿) → projectType=10 │ ├── 2,5 (红) → projectType=2 │ ├── 3,4 (蓝) → projectType=1 │ └── ... │ ├── 4. 逐帧处理 ────────────────────────────┐ │ for each frame in data: │ │ │ │ │ │ 4a. 解析每帧颜色数据 │ │ │ mapData = { │ │ │ id: [H,S,V,L,A,B] // 6个float │ │ │ } │ │ │ │ │ │ 4b. 调用 ML 推理 │ │ │ frameResult = │ │ │ urineCheck14.Get14Results( │ │ │ mapData, X) │ │ │ │ │ │ 4c. 收集结果 │ │ │ results[position].push_back(idx) │ │ └────────────────────────────────────────┘ │ ├── 5. 多帧投票 │ └── lastResult = CountResultsModal(results) │ 对每个position,取多帧结果中出现次数最多的index │ ├── 6. 关联修正 │ ├── PRO/MAU 修正 (见 §6.2) │ └── SG/PH 修正 (见 §6.3) │ ├── 7. 索引映射输出 │ ├── itemsStatIds[X][position] → 输出id │ └── itemsResultIdxMapTbl[X][position][rawIndex] → 输出index │ └── 8. 构建输出JSON └── {"score":"100","status":"0","data":[{id, index}, ...]} ``` --- ## 4. 核心ML推理:`UrineCheck14::Get14Results(mapData, papertypeIndex)` **位置**:`UrineCheck14.cpp:1041` ### 4.1 算法流程 ``` for each model in rtsArray[papertypeIndex]: ① 从 mapData 取出该 position 的6个颜色值 ② 数据不足6个 → 跳过 ③ GetModelInstance(papertypeIndex, position) → 获取 RTrees 模型 ④ 模型为空 → 跳过 ⑤ 构造 float data[6] = {H, S, V, L, A, B} ⑥ p = GetPredictResultVal(data) └── RTrees::predict(1×6 Mat) → 浮点数预测值 ⑦ 获取该 position 对应的结果索引函数 ⑧ pi = resultIndexFunc(data, p) └── 大部分直接 round(p),部分有HSV修正 ⑨ results[position] = pi ``` ### 4.2 ML模型配置 模型配置从 `/usr/local/Models/ml.json` 加载。 **ml.json 结构**: ```json { "ml": [ { "name": "glu", // 检测项目名 "model": [ { "positon": 0, // 色块位置(0-based) "id": 35, // 输出id "filepath": "/usr/local/Models/glu_0.data", "algorithm": 0, // 0=Red, 1=Green/Blue "indexmap": "0,1,2,3,4" // rawIndex → outputIndex映射 }, { "positon": 3, "filepath": "/usr/local/Models/glu-rts-M1.data", ... } ] }, ... ] } ``` **关键设计**: - 同一个检测项目(如glu)在不同试纸类型(绿/红/蓝)上可能位于不同 position - 同一position在不同试纸类型上使用不同的 RTrees 模型文件 - 总共支持 21 种试纸类型(MAX_PAPER_TYPE=21),每种最多15个色块(MAX_PAPER_BLOCK=15) ### 4.3 `GetPredictResultVal` ```cpp float UrineCheck14::GetPredictResultVal(float* sample) { cv::Mat sampleMat(1, 6, CV_32FC1, sample); return rtsPtr->predict(sampleMat); // OpenCV RTrees 预测 } ``` 输入:6维特征向量 (H,S,V,L,A,B) 输出:浮点数预测值(代表颜色等级,如 0=阴性, 1=弱阳, 2=中阳...) --- ## 5. 各检测项目的索引修正函数 所有修正函数都先调用 `round(p)` 取四舍五入整数,然后根据 HSV 值进行二次修正。 ### 5.1 红色试纸(algorithm=0)修正逻辑 | 项目 | 修正函数 | 修正规则 | |------|----------|----------| | **PRO (蛋白质)** | `GetRedProResultIndex` | 若 ret>0 且 (H<25 或 H>110) → 强制返回0 | | **MA (微量白蛋白)** | `GetRedMauResultIndex` | 若 ret>0 且 (H<30 或 H>115) → 强制返回0 | | **KET (酮体)** | `GetRedKetResultIndex` | 若 ret>1 且 (450 且 (400 且 (250)修正逻辑 大部分直接 `round(p)`,无额外修正。 --- ## 6. 关联修正规则 ### 6.1 配置来源 `positionsPro[X]`、`positionsMau[X]`、`positionsSg[X]`、`positionsPh[X]` 从 `ml.json` 中提取,记录了各试纸类型上 PRO/MAU/SG/pH 对应的色块位置。 ### 6.2 PRO(蛋白质)与 MAU(微量白蛋白)关联 ``` maxMau = (X > 0) ? 3 : 2 // 红色3级,其他2级 情况1: mauval == 0 → proVal = 0 // MA阴性时PRO必定阴性 情况2: proVal == 0 且 mauval == maxMau → mauval = maxMau - 1 // PRO阴性时MA不能满级 情况3: proVal >= 1 → mauval = maxMau // PRO阳性时MA设为满级 ``` ### 6.3 SG(比重)与 pH 关联 ``` maxSg = (X > 0) ? 6 : 5 // 红色6级,其他5级 条件: projectType < 20 且 phVal >= 2 且 sgVal < maxSg → sgVal += 1 // 在特定pH下上调比重一级 ``` --- ## 7. 索引映射(indexmap) ### 7.1 映射表结构 `itemsResultIdxMapTbl[MAX_PAPER_TYPE][MAX_PAPER_BLOCK][MAX_ALG_TAG]` 三维查表:`[试纸类型][色块位置][算法原始输出index]` → 最终输出index ### 7.2 indexmap示例 ```json "indexmap": "0,1,2,3,4" ``` 含义:算法输出的 index=0 映射为输出0,index=1 映射为输出1... ```json "indexmap": "5,6,2,3,4" ``` 含义:算法输出的 index=0→5, index=1→6, index=2→2... **用途**:不同试纸类型可能使用不同的等级映射,通过 indexmap 灵活配置。 --- ## 8. 输出JSON格式 ```json { "score": "100", "status": "0", "message": "OK", "imgid": 0, "project_id": 2, "paper_id": 2, "versioninfo": "...", "data": [ {"id": 35, "index": 1}, // 葡萄糖: 弱阳性 {"id": 36, "index": 0}, // 白细胞: 阴性 {"id": 37, "index": 0}, // 亚硝酸盐: 阴性 ... ] } ``` **data数组**:每个元素包含 `id`(项目ID,如35=GLU)和 `index`(等级,0=阴性)。 --- ## 9. 新旧JSON格式差异总结 | 特性 | 旧版 (analJsonData) | 新版 (ProcessJsonDataRequest) | |------|---------------------|-------------------------------| | 入口函数 | `analJsonData` | `ProcessJsonDataRequest` | | data结构 | `{"data0": [...], "data1": [...]}` | `[[...], [...]]` (直接数组) | | 试纸类型 | 从 `project_id` 或 `extend` 推断 | 从 `extend.type` 或 `extend.color_14i` | | seekey | `id` 是物理位置 | `id` 是色块索引(0-13) | | 数据格式 | 整数
(如 `"7,146,37,23,135,134"`) | 浮点数
(如 `"7.62,146.43,37.76..."`) | | 对应txt | 不匹配当前txt | **匹配** txt文件格式 | | pro/mau修正 | 在analJsonData内联 | 在ProcessJsonDataRequest内联 | --- ## 10. 关键文件清单 | 文件 | 功能 | |------|------| | `urinecheck_class.cpp:2052-2217` | `ProcessJsonDataRequest` - 新版JSON处理入口 | | `urinecheck_class.cpp:746-1078` | `analJsonData` - 旧版JSON处理 | | `urinecheck_class.cpp:54-172` | `initconfig()` - 加载 ml.json 配置 | | `UrineCheck14.cpp:1041-1104` | `Get14Results(mapData)` - JSON数据的ML推理 | | `UrineCheck14.cpp:1107-1176` | `Get14Results(mat, pts)` - 图片数据的ML推理 | | `UrineCheck14.cpp:32-230` | `initconfig()` - 加载模型文件和注册结果函数 | | `UrineCheck14.cpp:1195-1199` | `GetPredictResultVal` - RTrees预测 | | `UrineCheck14.cpp:1413-1746` | Red系列结果索引修正函数 |