import 'dart:convert'; import 'dart:math'; import 'package:fis_common/index.dart'; import 'package:fis_common/logger/logger.dart'; import 'package:fis_i18n/i18n.dart'; import 'package:fis_measure/interfaces/process/standard_line/calibration.dart'; import 'package:fis_measure/interfaces/process/workspace/application.dart'; import 'package:fis_measure/process/language/measure_language.dart'; import 'package:fis_measure/process/visual/visual.dart'; import 'package:fis_measure/process/workspace/third_part/application.dart'; import 'package:fis_measure/process/workspace/third_part/calibration_controller.dart'; import 'package:fis_measure/process/workspace/visual_loader.dart'; import 'package:fis_measure/values/unit_desc.dart'; import 'package:fis_measure/view/paint/ai_patint_controller.dart'; import 'package:fis_measure/view/paint/date_structure.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:vid/us/vid_us_unit.dart'; /// AI诊断结果 class ResultInfo extends StatefulWidget { const ResultInfo(this.aiDetectedObject, {Key? key}) : super(key: key); final List? aiDetectedObject; @override State createState() => _ResultInfoState(); } class _ResultInfoState extends State { late final aiPatintController = Get.find(); IStandardLineCalibrationController? standardLineCalibrationController; bool _isEmptyAITitle = false; /// 测量语言包 final measureLanguage = MeasureLanguage(); late AIDetectedObject aiDetectedObjectItem; double _unitsPhysicalPixels = 0; late final application = Get.find(); // 图像的物理单位 String _xUnit = ''; @override void initState() { _updateImagePhysicalSize(); try { if (application.isThirdPart) { final standradLine = (application as ThirdPartApplication).standardLine; standardLineCalibrationController = StandardLineCalibrationController(application, standradLine); Get.put( standardLineCalibrationController!); if (standardLineCalibrationController != null) { standardLineCalibrationController!.editStateChanged .addListener(onStandardLineUpdated); } } } catch (e) { logger.e('standardLineCalibrationController cannot find ', e); } super.initState(); } @override void didUpdateWidget(ResultInfo oldWidget) { _updateImagePhysicalSize(); super.didUpdateWidget(oldWidget); } @override void dispose() { if (standardLineCalibrationController != null) { standardLineCalibrationController!.editStateChanged .removeListener(onStandardLineUpdated); } super.dispose(); } void onStandardLineUpdated(_, e) { _updateImagePhysicalSize(); } @override Widget build(BuildContext context) { _isEmptyAITitle = ![ DiagnosisOrganEnum.Breast, DiagnosisOrganEnum.Liver, DiagnosisOrganEnum.Thyroid, DiagnosisOrganEnum.CarotidArtery ].contains(aiPatintController.diagnosisOrgan); if (_isEmptyAITitle) { return const SizedBox(); } if (_unitsPhysicalPixels <= 0) { _updateImagePhysicalSize(); } final description = widget .aiDetectedObject?[aiPatintController.state.aiResultIndex].descriptions; var lesionSizeDescription = description?.firstWhereOrNull( (element) => element.type == DiagnosisDescriptionEnum.LesionSize); var lesionSize = lesionSizeDescription?.value ?? ''; late final lesionSizeMap = (description?.length ?? 0) > 1 ? jsonDecode(lesionSize) : ''; return Container( decoration: BoxDecoration( border: Border.all( color: Colors.grey, ), borderRadius: BorderRadius.circular(4), color: Colors.transparent, ), child: Column( children: [ Row( mainAxisSize: MainAxisSize.max, children: [ Expanded( child: Container( decoration: const BoxDecoration( borderRadius: BorderRadius.only( topLeft: Radius.circular(4), topRight: Radius.circular(4), ), color: Color.fromRGBO(54, 169, 206, 1), ), padding: const EdgeInsets.only( top: 4, bottom: 4, left: 8, right: 8, ), child: Text( i18nBook.measure.aiDiagnosticResults.t, style: const TextStyle( color: Colors.white, ), ), ), ) ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Container( padding: const EdgeInsets.only( left: 10, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ _buildTitle( i18nBook.measure.analysisConclusion.t, _buildAITitle(), ), if (lesionSizeMap != '' && lesionSizeMap != null) _buildTitle( i18nBook.measure.isLesionSize.t, _buildLesionSize( lesionSizeMap?['HorizontalLengthInPixel'] ?? 0, lesionSizeMap?['VerticalLengthInPixel'] ?? 0, _unitsPhysicalPixels, ), ) ], ), ), ), Container( margin: const EdgeInsets.symmetric( vertical: 5, ), padding: const EdgeInsets.only(right: 8), child: Column( children: [ Text( i18nBook.measure.possibility.t, style: const TextStyle( color: Colors.grey, ), ), const SizedBox( height: 8, ), SizedBox( width: 70, height: 70, child: Stack( children: [ SizedBox( width: 70, height: 70, child: Obx(() { final aiDetected = widget.aiDetectedObject?[ aiPatintController.state.aiResultIndex]; return CircularProgressIndicator( valueColor: AlwaysStoppedAnimation( _buildAITextColor( aiDetected?.label ?? 0, ), ), backgroundColor: Colors.grey, value: aiDetected?.confidence, ); }), ), Center( child: SizedBox( width: 65, height: 45, child: Obx( () { final confidence = widget .aiDetectedObject?[aiPatintController .state.aiResultIndex] .confidence; return Center( child: Text( '${((confidence ?? 0) * 100).toStringAsFixed(1)}%', style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), ); }, ), ), ), ], ), ), ], ), ), ], ), ], ), ); } Widget _buildLesionSize( int horizontalLengthInPixel, int verticalLengthInPixel, double unitsPhysicalPixels, ) { return Text( (horizontalLengthInPixel * unitsPhysicalPixels) .toStringAsFixed(2) .toString() + '$_xUnit x' + (verticalLengthInPixel * unitsPhysicalPixels) .toStringAsFixed(2) .toString() + _xUnit, style: const TextStyle(color: Colors.white), ); } /// 更新图像物理尺度信息 void _updateImagePhysicalSize() { if (application.visuals.isEmpty) { return; } if (application.visuals[0].visualAreas.isEmpty) { return; } _unitsPhysicalPixels = (application.visuals[0].visualAreas[0].viewport?.region.width)! / (application.frameData!.width).toDouble(); VidUsUnit targetUnit = application.visuals[0].visualAreas[0].viewport?.xUnit ?? VidUsUnit.cm; _xUnit = UnitDescriptionMap.getDesc(targetUnit); } Widget _buildTitle(String label, Widget value) { return Column( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: const TextStyle( color: Color.fromRGBO(54, 169, 206, 1), ), ), value, const SizedBox( height: 5, ), ], ); } Color _buildAITextColor(int label) { switch (aiPatintController.diagnosisOrgan) { /// 乳腺是0:未见异常; 1、2、3良性; 4、5、6、7恶性; case DiagnosisOrganEnum.Breast: switch (label) { case 0: return Colors.lightBlue; case 1: case 2: case 3: return Colors.greenAccent; case 4: case 5: case 6: case 7: return Colors.redAccent; default: return Colors.lightBlue; } /// 肝脏是0:未见异常; 1、2、3、4、5、6、7、8良性; case DiagnosisOrganEnum.Liver: switch (label) { case 0: return Colors.lightBlue; case 4: return Colors.redAccent; case 1: case 2: case 3: case 5: case 6: case 7: case 8: return Colors.greenAccent; default: return Colors.lightBlue; } /// 甲状腺是0:未见异常; 1、2、3、4、5、6、7、8良性; case DiagnosisOrganEnum.Thyroid: switch (label) { case 0: return Colors.lightBlue; case 1: case 2: case 7: return Colors.greenAccent; case 3: case 4: case 5: case 6: return Colors.redAccent; default: return Colors.lightBlue; } default: return Colors.lightBlue; } } Widget _buildAITitle() { switch (aiPatintController.diagnosisOrgan) { case DiagnosisOrganEnum.Breast: return Obx(() { aiDetectedObjectItem = widget .aiDetectedObject?[aiPatintController.state.aiResultIndex] ?? AIDetectedObject(); return _buildBreastDescription(aiDetectedObjectItem.label); }); case DiagnosisOrganEnum.Liver: return Obx(() { aiDetectedObjectItem = widget .aiDetectedObject?[aiPatintController.state.aiResultIndex] ?? AIDetectedObject(); return _buildLiverDescription(aiDetectedObjectItem.label); }); case DiagnosisOrganEnum.Thyroid: return Obx(() { aiDetectedObjectItem = widget .aiDetectedObject?[aiPatintController.state.aiResultIndex] ?? AIDetectedObject(); return _buildThyroidDescription(aiDetectedObjectItem.label); }); case DiagnosisOrganEnum.CarotidArtery: return Obx(() { aiDetectedObjectItem = widget .aiDetectedObject?[aiPatintController.state.aiResultIndex] ?? AIDetectedObject(); return _buildCarotidArteryDescription(aiDetectedObjectItem.label); }); default: return const SizedBox(); } } Widget _buildBreastDescription(int label) { switch (label) { case 0: return _buildDescription( i18nBook.measure.noSignificantAbnormalitiesWereSeen.t); case 1: return _buildDescription(i18nBook.measure.lipoma.t); case 2: return _buildDescription('BI-RADS 2'); case 3: return _buildDescription('BI-RADS 3'); case 4: return _buildDescription('BI-RADS 4a'); case 5: return _buildDescription('BI-RADS 4b'); case 6: return _buildDescription('BI-RADS 4c'); case 7: return _buildDescription('BI-RADS 5'); case 8: return _buildDescription( i18nBook.measure.noSignificantAbnormalitiesWereSeen.t); default: return _buildDescription(null); } } Widget _buildLiverDescription(int label) { switch (label) { case 0: return _buildDescription( i18nBook.measure.noSignificantAbnormalitiesWereSeen.t); case 1: return _buildDescription(i18nBook.measure.intrahepaticStrongEchoFoci.t); case 2: return _buildDescription(i18nBook.measure.hepaticHemangioma.t); case 3: return _buildDescription(i18nBook.measure.liverCysts.t); case 4: return _buildDescription(i18nBook.measure.liverCancerMayOccur.t); case 5: return _buildDescription(i18nBook.measure.fattyLiver.t); case 6: return _buildDescription( i18nBook.measure.panisodicChangesLiverDiffuseLesions.t); case 7: return _buildDescription(i18nBook.measure.cirrhosis.t); case 8: return _buildDescription(i18nBook.measure.polycysticLiver.t); default: return _buildDescription(null); } } Widget _buildThyroidDescription(int label) { switch (label) { case 0: return _buildDescription( i18nBook.measure.noSignificantAbnormalitiesWereSeen.t); case 1: return _buildDescription(measureLanguage.t('ai', "TIRADS2")); case 2: return _buildDescription(measureLanguage.t('ai', "TIRADS3")); case 3: return _buildDescription(measureLanguage.t('ai', "TIRADS4a")); case 4: return _buildDescription(measureLanguage.t('ai', "TIRADS4b")); case 5: return _buildDescription(measureLanguage.t('ai', "TIRADS4c")); case 6: return _buildDescription(measureLanguage.t('ai', "TIRADS5")); case 7: return _buildDescription(i18nBook.measure.presenceDiffuseDisease.t); default: return _buildDescription(null); } } Widget _buildDescription( String? title, ) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (title != null) ...[ const SizedBox( height: 5, ), Text( title, style: const TextStyle(color: Colors.white), ), ], ], ); } Widget _buildCarotidArteryDescription(int label) { switch (label) { case 0: return _buildDescription( i18nBook.measure.noSignificantAbnormalitiesWereSeen.t); case 1: return _buildDescription(i18nBook.measure.patch.t); default: return _buildDescription(null); } } }