feature_analysis.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. import 'dart:convert';
  2. import 'package:fis_i18n/i18n.dart';
  3. import 'package:fis_measure/interfaces/process/workspace/application.dart';
  4. import 'package:fis_measure/values/unit_desc.dart';
  5. import 'package:fis_measure/view/paint/ai_patint_controller.dart';
  6. import 'package:fis_measure/view/paint/date_structure.dart';
  7. import 'package:flutter/material.dart';
  8. import 'package:get/get.dart';
  9. import 'package:vid/us/vid_us_unit.dart';
  10. /// 特征分析
  11. class FeatureAnalysis extends StatefulWidget {
  12. const FeatureAnalysis(this.descriptions, {Key? key}) : super(key: key);
  13. final List<AIDiagnosisDescription>? descriptions;
  14. @override
  15. State<StatefulWidget> createState() {
  16. return FeatureAnalysisState();
  17. }
  18. }
  19. class FeatureAnalysisState extends State<FeatureAnalysis> {
  20. late final aiPatintController = Get.find<AiPatintController>();
  21. double _unitsPhysicalPixels = 0;
  22. final application = Get.find<IApplication>();
  23. final ScrollController scrollController = ScrollController();
  24. String _xUnit = '';
  25. @override
  26. void initState() {
  27. _updateImagePhysicalSize();
  28. super.initState();
  29. }
  30. @override
  31. void didUpdateWidget(FeatureAnalysis oldWidget) {
  32. _updateImagePhysicalSize();
  33. super.didUpdateWidget(oldWidget);
  34. }
  35. @override
  36. Widget build(BuildContext context) {
  37. List<AIDiagnosisDescription> newDescriptions = [];
  38. for (var m in widget.descriptions!) {
  39. if (m.type != DiagnosisDescriptionEnum.CarotidIntimaMediaThickness &&
  40. m.type != DiagnosisDescriptionEnum.LesionSize) {
  41. newDescriptions.add(m);
  42. }
  43. }
  44. return Container(
  45. decoration: BoxDecoration(
  46. border: Border.all(
  47. color: const Color.fromRGBO(54, 169, 206, 1),
  48. ),
  49. borderRadius: BorderRadius.circular(4),
  50. color: Colors.transparent,
  51. ),
  52. child: Column(
  53. children: [
  54. Row(
  55. mainAxisSize: MainAxisSize.max,
  56. children: [
  57. Expanded(
  58. child: Container(
  59. decoration: const BoxDecoration(
  60. borderRadius: BorderRadius.only(
  61. topLeft: Radius.circular(4),
  62. topRight: Radius.circular(4),
  63. ),
  64. color: Color.fromRGBO(54, 169, 206, 1),
  65. ),
  66. padding: const EdgeInsets.symmetric(
  67. vertical: 4,
  68. horizontal: 8,
  69. ),
  70. child: Text(
  71. i18nBook.measure.featureAnalysis.t,
  72. style: const TextStyle(
  73. color: Colors.white,
  74. ),
  75. ),
  76. ),
  77. )
  78. ],
  79. ),
  80. Scrollbar(
  81. isAlwaysShown: true,
  82. controller: scrollController,
  83. child: GridView.count(
  84. shrinkWrap: true,
  85. crossAxisCount: 2,
  86. childAspectRatio: 2,
  87. controller: scrollController,
  88. children: newDescriptions!.map((e) {
  89. return Container(
  90. decoration: BoxDecoration(
  91. border: Border.all(
  92. color: const Color.fromRGBO(54, 169, 206, 1),
  93. ),
  94. ),
  95. padding: EdgeInsets.symmetric(
  96. horizontal: 4,
  97. vertical: i18nBook.isCurrentRussian ? 2.5 : 4),
  98. child: Column(
  99. crossAxisAlignment: CrossAxisAlignment.start,
  100. children: [
  101. Tooltip(
  102. message: _buildDescriptionType(e.type),
  103. child: Text(
  104. _buildDescriptionType(e.type),
  105. style: const TextStyle(
  106. color: Color.fromRGBO(54, 169, 206, 1),
  107. ),
  108. overflow: TextOverflow.ellipsis,
  109. maxLines: 1,
  110. ),
  111. ),
  112. Text(
  113. _buildDescriptionValue(e.value ?? '', e.type),
  114. style: const TextStyle(
  115. color: Colors.white,
  116. fontSize: 14,
  117. ),
  118. ),
  119. ],
  120. ),
  121. );
  122. }).toList(),
  123. ),
  124. ),
  125. ],
  126. ),
  127. );
  128. }
  129. String _buildDescriptionType(
  130. DiagnosisDescriptionEnum diagnosisDescriptionType) {
  131. switch (diagnosisDescriptionType) {
  132. case DiagnosisDescriptionEnum.Shape:
  133. return i18nBook.measure.shape.t;
  134. case DiagnosisDescriptionEnum.Orientation:
  135. return i18nBook.measure.orientation.t;
  136. case DiagnosisDescriptionEnum.EchoPattern:
  137. return i18nBook.measure.echoPattern.t;
  138. case DiagnosisDescriptionEnum.LesionBoundary:
  139. return i18nBook.measure.lesionBoundary.t;
  140. case DiagnosisDescriptionEnum.Margin:
  141. return i18nBook.measure.margin.t;
  142. case DiagnosisDescriptionEnum.Calcification:
  143. return i18nBook.measure.calcification.t;
  144. case DiagnosisDescriptionEnum.ThyroidEchoPattern:
  145. return i18nBook.measure.echoPattern.t;
  146. case DiagnosisDescriptionEnum.ThyroidShape:
  147. return i18nBook.measure.shape.t;
  148. case DiagnosisDescriptionEnum.ThyroidMargin:
  149. return i18nBook.measure.margin.t;
  150. case DiagnosisDescriptionEnum.ThyroidEchogenicFoci:
  151. return i18nBook.measure.thyroidEchogenicFoci.t;
  152. case DiagnosisDescriptionEnum.LiverShape:
  153. return i18nBook.measure.shape.t;
  154. case DiagnosisDescriptionEnum.LiverBoundary:
  155. return i18nBook.measure.lesionBoundary.t;
  156. case DiagnosisDescriptionEnum.LiverEchoTexture:
  157. return i18nBook.measure.liverEchoTexture.t;
  158. case DiagnosisDescriptionEnum.LesionSize:
  159. return i18nBook.measure.lesionSize.t;
  160. case DiagnosisDescriptionEnum.QlaqueEchoPattern: // 斑块回声类型
  161. return i18nBook.measure.plaqueEcho.t;
  162. case DiagnosisDescriptionEnum.QlaqueLocation: // 斑块位置
  163. return i18nBook.measure.plaquePosition.t;
  164. case DiagnosisDescriptionEnum.CarotidRateOfStenosis: // 颈动脉狭窄率
  165. return i18nBook.measure.stenosisRateoflumen.t;
  166. case DiagnosisDescriptionEnum.CarotidInnerDiameter: // 颈动脉内径
  167. return i18nBook.measure.carotidArteryDiameter.t;
  168. case DiagnosisDescriptionEnum.CarotidIntimaMediaThickness: // 颈动脉内中膜厚度
  169. return i18nBook.measure.innerMembraneThickness.t;
  170. default:
  171. print('diagnosisDescriptionType:$diagnosisDescriptionType');
  172. return '-';
  173. }
  174. }
  175. void _updateImagePhysicalSize() {
  176. if (application.visuals.isEmpty) {
  177. return;
  178. }
  179. if (application.visuals[0].visualAreas.isEmpty) {
  180. return;
  181. }
  182. VidUsUnit targetUnit =
  183. application.visuals[0].visualAreas[0].viewport?.xUnit ?? VidUsUnit.cm;
  184. var unitsPhysicalPixels =
  185. (application.visuals[0].visualAreas[0].viewport?.region.width)! /
  186. (application.frameData!.width).toDouble();
  187. setState(() {
  188. _xUnit = UnitDescriptionMap.getDesc(targetUnit);
  189. _unitsPhysicalPixels = unitsPhysicalPixels;
  190. });
  191. }
  192. String _buildDescriptionValue(
  193. String diagnosisDescriptionValue, DiagnosisDescriptionEnum type) {
  194. double? doubleValue = double.tryParse(diagnosisDescriptionValue);
  195. if (doubleValue != null) {
  196. var value = doubleValue.toStringAsFixed(2);
  197. if (type == DiagnosisDescriptionEnum.CarotidRateOfStenosis) {
  198. return value;
  199. }
  200. return '$value $_xUnit';
  201. }
  202. if (diagnosisDescriptionValue.length > 50) {
  203. final diagnosisDescription = jsonDecode(diagnosisDescriptionValue);
  204. double horizontalAndVerticalProportion =
  205. (diagnosisDescription?['VerticalLengthInPixel'] ?? 0) /
  206. (diagnosisDescription?['HorizontalLengthInPixel'] ?? 1);
  207. bool horizontalAndVertical = horizontalAndVerticalProportion > 1 ||
  208. horizontalAndVerticalProportion == 1;
  209. if (diagnosisDescription is Map<dynamic, dynamic> &&
  210. diagnosisDescription.containsKey('MeasuringLineLengthInPixel')) {
  211. double measuringLineLengthInPixel =
  212. diagnosisDescription['MeasuringLineLengthInPixel'] ?? 0;
  213. if (measuringLineLengthInPixel <= 0) {
  214. return '< 1';
  215. }
  216. var result = (measuringLineLengthInPixel * _unitsPhysicalPixels)
  217. .toStringAsFixed(2)
  218. .toString();
  219. return '$result $_xUnit';
  220. }
  221. return horizontalAndVertical ? '> 1' : '< 1';
  222. } else {
  223. switch (diagnosisDescriptionValue) {
  224. case 'Oval':
  225. return i18nBook.measure.oval.t;
  226. case 'Round':
  227. return i18nBook.measure.round.t;
  228. case 'Irregular':
  229. return i18nBook.measure.irregular.t;
  230. case 'Regular':
  231. return i18nBook.measure.regular.t;
  232. case 'Homogeneous':
  233. return i18nBook.measure.homogeneous.t;
  234. case 'Heterogeneous':
  235. return i18nBook.measure.heterogeneous.t;
  236. case 'Parallel':
  237. return i18nBook.measure.parallel.t;
  238. case 'NonParallel':
  239. return i18nBook.measure.nonParallel.t;
  240. case 'Anechoic':
  241. return i18nBook.measure.anechoic.t;
  242. case 'Hypoechoic':
  243. return i18nBook.measure.hypoechoic.t;
  244. case 'Isoechoic':
  245. return i18nBook.measure.isoechoic.t;
  246. case 'Hyperechoic':
  247. return i18nBook.measure.hyperechoic.t;
  248. case 'Complex':
  249. return i18nBook.measure.complex.t;
  250. case 'Strongechoic':
  251. return i18nBook.measure.strongechoic.t;
  252. case 'AbruptInterface':
  253. return i18nBook.measure.abruptInterface.t;
  254. case 'EchogenicHalo':
  255. return i18nBook.measure.echogenicHalo.t;
  256. case 'Circumscribed':
  257. return i18nBook.measure.circumscribed.t;
  258. case 'NonCircumscribed':
  259. return i18nBook.measure.nonCircumscribed.t;
  260. case 'NoCalcifications':
  261. return i18nBook.measure.noCalcifications.t;
  262. case 'Macrocalcifications':
  263. return i18nBook.measure.macrocalcifications.t;
  264. case 'CalcificationsInMass':
  265. return i18nBook.measure.calcificationsInMass.t;
  266. case 'CalcificationsOutOfMass':
  267. return i18nBook.measure.calcificationsOutOfMass.t;
  268. case 'WiderThanTall':
  269. return i18nBook.measure.widerThanTall.t;
  270. case 'TallThanWider':
  271. return i18nBook.measure.tallThanWider.t;
  272. case 'Smooth':
  273. return i18nBook.measure.smooth.t;
  274. case 'IllDefined':
  275. return i18nBook.measure.illDefined.t;
  276. case 'Lobulated':
  277. return i18nBook.measure.lobulated.t;
  278. case 'ExtraThyroidalExtension':
  279. return i18nBook.measure.extraThyroidalExtension.t;
  280. case 'NoCifications':
  281. return i18nBook.measure.noCifications.t;
  282. case 'Coarsecalcifications':
  283. return i18nBook.measure.coarsecalcifications.t;
  284. case 'Microcalcifications':
  285. return i18nBook.measure.microcalcifications.t;
  286. case 'AnteriorWall':
  287. return i18nBook.measure.frontWall.t;
  288. case 'PosteriorWall':
  289. return i18nBook.measure.rearWall.t;
  290. case 'SideWall':
  291. return i18nBook.measure.sideWall.t;
  292. default:
  293. return '';
  294. }
  295. }
  296. }
  297. }