|
@@ -1,11 +1,13 @@
|
|
|
import 'dart:convert';
|
|
|
-import 'dart:ui';
|
|
|
|
|
|
import 'package:fis_measure/interfaces/date_types/int_size.dart';
|
|
|
import 'package:fis_measure/interfaces/process/player/play_controller.dart';
|
|
|
import 'package:fis_measure/interfaces/process/workspace/application.dart';
|
|
|
import 'package:fis_measure/process/workspace/measure_data_controller.dart';
|
|
|
-import 'package:fis_measure/utils/canvas.dart';
|
|
|
+import 'package:fis_measure/view/paint/date_structure.dart';
|
|
|
+import 'package:fis_measure/view/paint/parts/dash_line.dart';
|
|
|
+import 'package:fis_measure/view/paint/parts/dots.dart';
|
|
|
+import 'package:fis_measure/view/paint/parts/rectangle.dart';
|
|
|
import 'package:fis_measure/view/player/controller.dart';
|
|
|
import 'package:fis_measure/view/player/enums.dart';
|
|
|
import 'package:fis_measure/view/player/events.dart';
|
|
@@ -13,6 +15,11 @@ import 'package:flutter/material.dart';
|
|
|
import 'package:get/get.dart';
|
|
|
|
|
|
class AIPaintInfo extends StatefulWidget {
|
|
|
+
|
|
|
+ final VidPlayerController controller;
|
|
|
+ final double? width;
|
|
|
+ final double? height;
|
|
|
+
|
|
|
const AIPaintInfo(
|
|
|
this.controller, {
|
|
|
Key? key,
|
|
@@ -20,11 +27,6 @@ class AIPaintInfo extends StatefulWidget {
|
|
|
this.height,
|
|
|
}) : super(key: key);
|
|
|
|
|
|
- final VidPlayerController controller;
|
|
|
-
|
|
|
- final double? width;
|
|
|
- final double? height;
|
|
|
-
|
|
|
@override
|
|
|
State<AIPaintInfo> createState() => _AIPaintInfoState();
|
|
|
}
|
|
@@ -34,28 +36,42 @@ class _AIPaintInfoState extends State<AIPaintInfo> {
|
|
|
|
|
|
late final application = Get.find<IApplication>();
|
|
|
|
|
|
+
|
|
|
+ late final List<AIDiagnosisPerImageDTO> aiResult = [];
|
|
|
+
|
|
|
+
|
|
|
+ String? aiResults;
|
|
|
+
|
|
|
|
|
|
final measureData = Get.find<MeasureDataController>();
|
|
|
+
|
|
|
+
|
|
|
final playerController = Get.find<IPlayerController>();
|
|
|
+
|
|
|
+
|
|
|
late List<Offset> aiResultsList = [];
|
|
|
+
|
|
|
+
|
|
|
late Offset p1 = Offset.zero;
|
|
|
late Offset p2 = Offset.zero;
|
|
|
late Offset p3 = Offset.zero;
|
|
|
late Offset p4 = Offset.zero;
|
|
|
|
|
|
+
|
|
|
double left = -1;
|
|
|
double top = -1;
|
|
|
double width = -1;
|
|
|
double height = -1;
|
|
|
|
|
|
+
|
|
|
int? frameIndex;
|
|
|
|
|
|
- @override
|
|
|
- void didUpdateWidget(covariant AIPaintInfo oldWidget) {
|
|
|
- if (oldWidget.controller != widget.controller) {
|
|
|
- throw UnsupportedError("[VidTestPlayer] unsupport replace controller.");
|
|
|
+ void onMeasuredAIResultsInfoChanged(Object sender, String e) {
|
|
|
+ if (e.isNotEmpty) {
|
|
|
+ aiResults = e;
|
|
|
+
|
|
|
+ setState(() {});
|
|
|
}
|
|
|
- super.didUpdateWidget(oldWidget);
|
|
|
}
|
|
|
|
|
|
void onControllerEvent(Object sender, VidPlayerEvent e) {
|
|
@@ -85,100 +101,93 @@ class _AIPaintInfoState extends State<AIPaintInfo> {
|
|
|
setState(() {});
|
|
|
}
|
|
|
|
|
|
- bool onGetAIResultsInfo(double widthScale) {
|
|
|
- final aiResult = jsonDecode(measureData.aiResults);
|
|
|
- if ((aiResult as List).isEmpty) {
|
|
|
- return false;
|
|
|
- } else if (aiResult.length == 1) {
|
|
|
- final diagResultsForEachOrgan = aiResult[0]['DiagResultsForEachOrgan'][0];
|
|
|
- final detectedObjects = diagResultsForEachOrgan['DetectedObjects'];
|
|
|
- if ((detectedObjects as List).isNotEmpty) {
|
|
|
- for (int m = 0; m < detectedObjects.length; m++) {
|
|
|
- final contours = detectedObjects[m]['Contours'] as List;
|
|
|
- final descriptions = jsonDecode(detectedObjects[m]['Descriptions']
|
|
|
- [(detectedObjects[m]['Descriptions'] as List).length - 1]
|
|
|
- ['Value']);
|
|
|
- if (_HAS_VIEW_STATUS_ARR
|
|
|
- .contains((playerController as VidPlayerController).status)) {
|
|
|
- for (int i = 0; i < contours.length; i++) {
|
|
|
- if (i % 10 == 0) {
|
|
|
- aiResultsList.add(Offset(
|
|
|
- contours[i]['X'] * widthScale,
|
|
|
- contours[i]['Y'] * widthScale,
|
|
|
- ));
|
|
|
- }
|
|
|
+
|
|
|
+ void onSingleFrameImage(
|
|
|
+ List<AIDetectedObject>? detectedObjects, double widthScale) {
|
|
|
+ for (int m = 0; m < detectedObjects!.length; m++) {
|
|
|
+ final List<AIDiagnosisPoint2D>? contours = detectedObjects[m].contours;
|
|
|
+ if (contours!.isNotEmpty && detectedObjects[m].descriptions!.isNotEmpty) {
|
|
|
+ final descriptions = jsonDecode(
|
|
|
+ detectedObjects[m]
|
|
|
+ .descriptions![detectedObjects[m].descriptions!.length - 1]
|
|
|
+ .value!,
|
|
|
+ );
|
|
|
+ if (_HAS_VIEW_STATUS_ARR
|
|
|
+ .contains((playerController as VidPlayerController).status)) {
|
|
|
+ for (int i = 0; i < contours.length; i++) {
|
|
|
+ if (i % 10 == 0) {
|
|
|
+ aiResultsList.add(Offset(
|
|
|
+ contours[i].x * widthScale,
|
|
|
+ contours[i].y * widthScale,
|
|
|
+ ));
|
|
|
}
|
|
|
- p1 = Offset(descriptions['HorizontalPoint1']['X'] * widthScale,
|
|
|
- descriptions['HorizontalPoint1']['Y'] * widthScale);
|
|
|
- p2 = Offset(descriptions['HorizontalPoint2']['X'] * widthScale,
|
|
|
- descriptions['HorizontalPoint2']['Y'] * widthScale);
|
|
|
- p3 = Offset(descriptions['VerticalPoint1']['X'] * widthScale,
|
|
|
- descriptions['VerticalPoint1']['Y'] * widthScale);
|
|
|
- p4 = Offset(descriptions['VerticalPoint2']['X'] * widthScale,
|
|
|
- descriptions['VerticalPoint2']['Y'] * widthScale);
|
|
|
}
|
|
|
+ p1 = Offset(descriptions['HorizontalPoint1']['X'] * widthScale,
|
|
|
+ descriptions['HorizontalPoint1']['Y'] * widthScale);
|
|
|
+ p2 = Offset(descriptions['HorizontalPoint2']['X'] * widthScale,
|
|
|
+ descriptions['HorizontalPoint2']['Y'] * widthScale);
|
|
|
+ p3 = Offset(descriptions['VerticalPoint1']['X'] * widthScale,
|
|
|
+ descriptions['VerticalPoint1']['Y'] * widthScale);
|
|
|
+ p4 = Offset(descriptions['VerticalPoint2']['X'] * widthScale,
|
|
|
+ descriptions['VerticalPoint2']['Y'] * widthScale);
|
|
|
}
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ bool onGetAIResultsInfo(double widthScale) {
|
|
|
+ if (aiResult.isEmpty) {
|
|
|
+ return false;
|
|
|
+ } else if (aiResult.length == 1) {
|
|
|
+ final diagResultsForEachOrgan = aiResult[0].diagResultsForEachOrgan?[0];
|
|
|
+ final detectedObjects = diagResultsForEachOrgan!.detectedObjects;
|
|
|
+ if (detectedObjects!.isNotEmpty) {
|
|
|
+ onSingleFrameImage(
|
|
|
+ detectedObjects,
|
|
|
+ widthScale,
|
|
|
+ );
|
|
|
} else {
|
|
|
return false;
|
|
|
}
|
|
|
return true;
|
|
|
} else {
|
|
|
_updateFeatures();
|
|
|
+ final diagResultsForEachOrgan =
|
|
|
+ aiResult[frameIndex!].diagResultsForEachOrgan![0];
|
|
|
+
|
|
|
+ final detectedObjects = diagResultsForEachOrgan.detectedObjects;
|
|
|
if ((playerController as VidPlayerController).status ==
|
|
|
VidPlayStatus.pause) {
|
|
|
- final diagResultsForEachOrgan =
|
|
|
- aiResult[frameIndex!]['DiagResultsForEachOrgan'][0];
|
|
|
-
|
|
|
- final detectedObjects = diagResultsForEachOrgan['DetectedObjects'];
|
|
|
- for (int m = 0; m < (detectedObjects as List).length; m++) {
|
|
|
- final contours = detectedObjects[m]['Contours'] as List;
|
|
|
- if (contours.isEmpty) {
|
|
|
- return false;
|
|
|
- } else {
|
|
|
- if ((detectedObjects[m]['Descriptions'] as List).isNotEmpty) {
|
|
|
- final descriptions = jsonDecode(detectedObjects[m]['Descriptions']
|
|
|
- [(detectedObjects[m]['Descriptions'] as List).length - 1]
|
|
|
- ['Value']);
|
|
|
- if (_HAS_VIEW_STATUS_ARR
|
|
|
- .contains((playerController as VidPlayerController).status)) {
|
|
|
- for (int i = 0; i < contours.length; i++) {
|
|
|
- if (i % 10 == 0) {
|
|
|
- aiResultsList.add(Offset(
|
|
|
- contours[i]['X'] * widthScale,
|
|
|
- contours[i]['Y'] * widthScale,
|
|
|
- ));
|
|
|
- }
|
|
|
- }
|
|
|
- p1 = Offset(descriptions['HorizontalPoint1']['X'] * widthScale,
|
|
|
- descriptions['HorizontalPoint1']['Y'] * widthScale);
|
|
|
- p2 = Offset(descriptions['HorizontalPoint2']['X'] * widthScale,
|
|
|
- descriptions['HorizontalPoint2']['Y'] * widthScale);
|
|
|
- p3 = Offset(descriptions['VerticalPoint1']['X'] * widthScale,
|
|
|
- descriptions['VerticalPoint1']['Y'] * widthScale);
|
|
|
- p4 = Offset(descriptions['VerticalPoint2']['X'] * widthScale,
|
|
|
- descriptions['VerticalPoint2']['Y'] * widthScale);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ if (detectedObjects!.isNotEmpty) {
|
|
|
+ onSingleFrameImage(
|
|
|
+ detectedObjects,
|
|
|
+ widthScale,
|
|
|
+ );
|
|
|
}
|
|
|
} else if (playerController.status == VidPlayStatus.play) {
|
|
|
- final diagResultsForEachOrgan =
|
|
|
- aiResult[frameIndex!]['DiagResultsForEachOrgan'][0];
|
|
|
- final detectedObjects = diagResultsForEachOrgan['DetectedObjects'];
|
|
|
-
|
|
|
- for (int m = 0; m < (detectedObjects as List).length; m++) {
|
|
|
- final organBoundBox = detectedObjects[m]['BoundingBox'];
|
|
|
+ for (int m = 0; m < detectedObjects!.length; m++) {
|
|
|
+ final organBoundBox = detectedObjects[m].boundingBox;
|
|
|
|
|
|
- left = organBoundBox['Left'] * widthScale;
|
|
|
- top = organBoundBox['Top'] * widthScale;
|
|
|
- width = organBoundBox['Width'] * widthScale;
|
|
|
- height = organBoundBox['Height'] * widthScale;
|
|
|
+ left = organBoundBox!.left * widthScale;
|
|
|
+ top = organBoundBox.top * widthScale;
|
|
|
+ width = organBoundBox.width * widthScale;
|
|
|
+ height = organBoundBox.height * widthScale;
|
|
|
}
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ @override
|
|
|
+ void didUpdateWidget(covariant AIPaintInfo oldWidget) {
|
|
|
+ if (oldWidget.controller != widget.controller) {
|
|
|
+ throw UnsupportedError("[VidTestPlayer] unsupport replace controller.");
|
|
|
+ }
|
|
|
+
|
|
|
+ super.didUpdateWidget(oldWidget);
|
|
|
+ }
|
|
|
+
|
|
|
@override
|
|
|
void initState() {
|
|
|
widget.controller.eventHandler.addListener(onControllerEvent);
|
|
@@ -196,44 +205,71 @@ class _AIPaintInfoState extends State<AIPaintInfo> {
|
|
|
|
|
|
@override
|
|
|
Widget build(BuildContext context) {
|
|
|
- return LayoutBuilder(
|
|
|
- builder: (context, constraints) {
|
|
|
- final canvasWidth = constraints.maxWidth;
|
|
|
- late final frameDataWidth = application.frameData?.width;
|
|
|
- late final widthScale = (canvasWidth / frameDataWidth!);
|
|
|
- if (!onGetAIResultsInfo(widthScale)) {
|
|
|
- return const SizedBox();
|
|
|
- } else {
|
|
|
- return CustomMultiChildLayout(
|
|
|
- delegate: _LayerLayoutDelegate(),
|
|
|
- children: [
|
|
|
- LayoutId(
|
|
|
- id: _LayerLayoutIds.dots,
|
|
|
- child: CustomPaint(
|
|
|
- painter: PaintAIDots(aiResultsList),
|
|
|
+ Widget? child;
|
|
|
+
|
|
|
+ switch (widget.controller.status) {
|
|
|
+ case VidPlayStatus.init:
|
|
|
+ child = Container(child: const Text("Loading"));
|
|
|
+ break;
|
|
|
+ case VidPlayStatus.ready:
|
|
|
+ child = Container(child: const Text("Ready"));
|
|
|
+ break;
|
|
|
+ case VidPlayStatus.loadFail:
|
|
|
+ child = Container(child: const Text("Load fail"));
|
|
|
+ break;
|
|
|
+ case VidPlayStatus.play:
|
|
|
+ case VidPlayStatus.pause:
|
|
|
+ child = LayoutBuilder(builder: (context, constraints) {
|
|
|
+ final canvasWidth = constraints.maxWidth;
|
|
|
+ late final frameDataWidth = application.frameData?.width;
|
|
|
+ late final widthScale = (canvasWidth / frameDataWidth!);
|
|
|
+ if (!onGetAIResultsInfo(widthScale)) {
|
|
|
+ return const SizedBox();
|
|
|
+ } else {
|
|
|
+ return CustomMultiChildLayout(
|
|
|
+ key: UniqueKey(),
|
|
|
+ delegate: _LayerLayoutDelegate(),
|
|
|
+ children: [
|
|
|
+ LayoutId(
|
|
|
+ id: _LayerLayoutIds.dots,
|
|
|
+ child: CustomPaint(
|
|
|
+ painter: PaintAIDots(aiResultsList),
|
|
|
+ ),
|
|
|
),
|
|
|
- ),
|
|
|
- LayoutId(
|
|
|
- id: _LayerLayoutIds.dashLine,
|
|
|
- child: CustomPaint(
|
|
|
- painter: PaintAIDashLine(p1, p2, p3, p4),
|
|
|
+ LayoutId(
|
|
|
+ id: _LayerLayoutIds.dashLine,
|
|
|
+ child: CustomPaint(
|
|
|
+ painter: PaintAIDashLine(p1, p2, p3, p4),
|
|
|
+ ),
|
|
|
),
|
|
|
- ),
|
|
|
- LayoutId(
|
|
|
- id: _LayerLayoutIds.rectangle,
|
|
|
- child: CustomPaint(
|
|
|
- painter: PaintAIRectangle(
|
|
|
- left,
|
|
|
- top,
|
|
|
- width,
|
|
|
- height,
|
|
|
+ LayoutId(
|
|
|
+ id: _LayerLayoutIds.rectangle,
|
|
|
+ child: CustomPaint(
|
|
|
+ painter: PaintAIRectangle(
|
|
|
+ left,
|
|
|
+ top,
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ ),
|
|
|
),
|
|
|
),
|
|
|
- ),
|
|
|
- ],
|
|
|
- );
|
|
|
- }
|
|
|
- },
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case VidPlayStatus.stop:
|
|
|
+ case VidPlayStatus.dispose:
|
|
|
+ child = Container(child: const Text("Closed"));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return buildBox(context, child);
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget buildBox(BuildContext context, Widget child) {
|
|
|
+ return Container(
|
|
|
+ alignment: Alignment.center,
|
|
|
+ child: child,
|
|
|
);
|
|
|
}
|
|
|
|
|
@@ -257,12 +293,29 @@ class _AIPaintInfoState extends State<AIPaintInfo> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- void _addListenrs() {
|
|
|
+ void _addListenrs() async {
|
|
|
application.updateRenderReady.addListener(_onMeasureRerenderReady);
|
|
|
+ measureData.aiResultsInfoChanged
|
|
|
+ .addListener(onMeasuredAIResultsInfoChanged);
|
|
|
+ await Future.delayed(const Duration(milliseconds: 300), () {
|
|
|
+ final measureDataAIResults = jsonDecode(
|
|
|
+ (aiResults ?? '').isEmpty ? measureData.aiResults : aiResults ?? '',
|
|
|
+ );
|
|
|
+ for (int i = 0; i < (measureDataAIResults as List).length; i++) {
|
|
|
+ aiResult.add(
|
|
|
+ AIDiagnosisPerImageDTO.fromJson(
|
|
|
+ measureDataAIResults[i],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+ setState(() {});
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
void _removeListenrs() {
|
|
|
application.updateRenderReady.removeListener(_onMeasureRerenderReady);
|
|
|
+ measureData.aiResultsInfoChanged
|
|
|
+ .removeListener(onMeasuredAIResultsInfoChanged);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -274,8 +327,6 @@ class _LayerLayoutDelegate extends MultiChildLayoutDelegate {
|
|
|
|
|
|
@override
|
|
|
void performLayout(Size size) {
|
|
|
-
|
|
|
-
|
|
|
final application = Get.find<IApplication>();
|
|
|
final vidFrame = application.frameData;
|
|
|
final imageSize = IntSize.fill(vidFrame?.width ?? 0, vidFrame?.height ?? 0);
|
|
@@ -335,90 +386,6 @@ class _LayerLayoutDelegate extends MultiChildLayoutDelegate {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-Paint _paint = Paint()
|
|
|
- ..color = const Color.fromARGB(255, 255, 255, 0)
|
|
|
- ..isAntiAlias = true
|
|
|
- ..strokeWidth = 2
|
|
|
- ..style = PaintingStyle.stroke;
|
|
|
-
|
|
|
-
|
|
|
-class PaintAIDashLine extends CustomPainter {
|
|
|
- final Offset p1;
|
|
|
- final Offset p2;
|
|
|
- final Offset p3;
|
|
|
- final Offset p4;
|
|
|
- PaintAIDashLine(
|
|
|
- this.p1,
|
|
|
- this.p2,
|
|
|
- this.p3,
|
|
|
- this.p4,
|
|
|
- );
|
|
|
- @override
|
|
|
- void paint(Canvas canvas, Size size) {
|
|
|
- canvas.drawDashLine(p1, p2, 2, 5, _paint);
|
|
|
- canvas.drawDashLine(p3, p4, 2, 5, _paint);
|
|
|
- canvas.drawVertex(p1, 10);
|
|
|
- canvas.drawVertex(p2, 10);
|
|
|
- canvas.drawVertex(p3, 10);
|
|
|
- canvas.drawVertex(p4, 10);
|
|
|
- }
|
|
|
-
|
|
|
- @override
|
|
|
- bool shouldRepaint(CustomPainter oldDelegate) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-class PaintAIDots extends CustomPainter {
|
|
|
- final List<Offset>? aiResultsList;
|
|
|
- PaintAIDots(this.aiResultsList);
|
|
|
- @override
|
|
|
- void paint(Canvas canvas, Size size) {
|
|
|
- canvas.drawPoints(
|
|
|
-
|
|
|
- PointMode.points,
|
|
|
- aiResultsList ?? [],
|
|
|
- _paint,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- @override
|
|
|
- bool shouldRepaint(covariant PaintAIDots oldDelegate) {
|
|
|
- return oldDelegate.hashCode != hashCode;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-class PaintAIRectangle extends CustomPainter {
|
|
|
- final double left;
|
|
|
- final double top;
|
|
|
- final double width;
|
|
|
- final double height;
|
|
|
-
|
|
|
- PaintAIRectangle(
|
|
|
- this.left,
|
|
|
- this.top,
|
|
|
- this.width,
|
|
|
- this.height,
|
|
|
- );
|
|
|
- @override
|
|
|
- void paint(Canvas canvas, Size size) {
|
|
|
- Rect rect = Rect.fromLTWH(
|
|
|
- left,
|
|
|
- top,
|
|
|
- width,
|
|
|
- height,
|
|
|
- );
|
|
|
- canvas.drawRect(rect, _paint);
|
|
|
- }
|
|
|
-
|
|
|
- @override
|
|
|
- bool shouldRepaint(covariant PaintAIRectangle oldDelegate) {
|
|
|
- return oldDelegate.hashCode != hashCode;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
enum _LayerLayoutIds {
|
|
|
|
|
|
dots,
|