Files
nanocode/urinecheck/docs/cpp-json-recognition-analysis.md
T
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

11 KiB
Raw Blame History

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格式:

{
    "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 是一个对象而非数组:

{
    "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 结构:

{
    "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

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 且 (45<H<145) → 强制返回0
NIT (亚硝酸盐) GetRedNitResultIndex 若 ret>0 且 (40<H<140) → 强制返回0
URO (尿胆原) GetRedUroResultIndex 若 ret>0 且 (25<H<150) → 强制返回0
BLD (潜血) GetRedBldResultIndex 直接 round(p),无修正
VC (抗坏血酸) GetRedVcResultIndex 直接 round(p),无修正
BIL (胆红素) GetRedBilResultIndex 直接 round(p),无修正
GLU (葡萄糖) GetRedGluResultIndex 直接 round(p),无修正
CRE (肌酐) GetRedCreResultIndex 直接 round(p),无修正
Ca (尿钙) GetRedUcaResultIndex 直接 round(p),无修正
SG (比重) GetRedSgResultIndex 直接 round(p),无修正
pH GetRedPhResultIndex 直接 round(p),无修正
WBC (白细胞) GetRedWbcResultIndex 直接 round(p),无修正

5.2 绿色/蓝色试纸(algorithm>0)修正逻辑

大部分直接 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示例

"indexmap": "0,1,2,3,4"

含义:算法输出的 index=0 映射为输出0,index=1 映射为输出1...

"indexmap": "5,6,2,3,4"

含义:算法输出的 index=0→5, index=1→6, index=2→2...

用途:不同试纸类型可能使用不同的等级映射,通过 indexmap 灵活配置。


8. 输出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系列结果索引修正函数