Flutter图像标注插件bounding_box_annotation的使用

发布于 1周前 作者 bupafengyu 来自 Flutter

Flutter图像标注插件bounding_box_annotation的使用

标题

Flutter图像标注插件bounding_box_annotation的使用

内容

一个用于在图像上创建边界框标注的Widget。

预览

预览

功能

  • 在图像上绘制矩形(边界框)。
  • 添加/编辑自定义文本以标记标注。
  • 获取标注详细信息(例如Offset)。
  • 基于边界框裁剪图像为多个部分。
  • 删除标注。

使用方法

  1. 创建BoundingBoxAnnotation widget,并传递所需的参数。
final AnnotationController annotationController = AnnotationController();

BoundingBoxAnnotation(
    controller: annotationController,
    imageBytes: imageBytes
)
  1. 自定义图像大小。
BoundingBoxAnnotation(
    controller: annotationController,
    imageBytes: imageBytes,
    imageWidth: 400.0,
    imageHeight: 400.0,
)
  1. 自定义标注样式。
BoundingBoxAnnotation(
    controller: annotationController,
    imageBytes: imageBytes,
    color: Colors.blue,
    strokeWidth: 5.0,
)
  1. 创建一个List变量来存储标注。
List<AnnottaionDetails> annotationList = [];
  1. 获取标注详细信息:
final AnnotationController annotationController = AnnotationController();

List<AnnottaionDetails> annotationList = await annotationController.getData();
  1. 手动添加标注:
final AnnotationController annotationController = AnnotationController();
double x = 10.0;
double y = 20.0;
double width = 150.0;
double height = 150.0;
String label = "Test"

annotationController.addAnnotation(x, y, width, height, label);
  1. 清除所有标注:
final AnnotationController annotationController = AnnotationController();

annotationController.clear();

示例代码

import 'package:bounding_box_annotation/bounding_box_annotation.dart';
import 'package:bounding_box_annotation_example/result.dart';
import 'package:bounding_box_annotation_example/widgets/add_annotation_dialog.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Loading(),
    );
  }
}

class Loading extends StatefulWidget {
  const Loading({super.key});

  [@override](/user/override)
  State&lt;Loading&gt; createState() =&gt; _LoadingState();
}

class _LoadingState extends State&lt;Loading&gt; {
  Uint8List? imageBytes;

  void getImageBytes() async {
    final byteData = await rootBundle.load("assets/flutter-logo.png");
    imageBytes = byteData.buffer.asUint8List();

    if (context.mounted) {
      Navigator.pushReplacement(
        context,
        MaterialPageRoute(
          builder: (context) =&gt; Annotation(imageBytes: imageBytes!),
        ),
      );
    }
  }

  [@override](/user/override)
  void initState() {
    getImageBytes();
    super.initState();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: CircularProgressIndicator(),
      ),
    );
  }
}

class Annotation extends StatefulWidget {
  final Uint8List imageBytes;
  const Annotation({super.key, required this.imageBytes});

  [@override](/user/override)
  State&lt;Annotation&gt; createState() =&gt; _AnnotationState();
}

class _AnnotationState extends State&lt;Annotation&gt; {
  final AnnotationController annotationController = AnnotationController();

  double? x;
  double? y;
  double? width;
  double? height;
  String? label;

  void setValue(double x, double y, double width, double height, String label) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.label = label;
  }

  [@override](/user/override)
  void initState() {
    super.initState();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(
        backgroundColor: Colors.blue,
        title: const Text(
          "Annotation Demo",
          style: TextStyle(color: Colors.white, fontWeight: FontWeight.w600),
        ),
        centerTitle: true,
        iconTheme: const IconThemeData(color: Colors.white),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &lt;Widget&gt;[
            Container(
              decoration: BoxDecoration(
                border: Border.all(
                  color: Colors.blue,
                  width: 10.0,
                ),
              ),
              child: BoundingBoxAnnotation(
                controller: annotationController,
                imageBytes: widget.imageBytes,
                imageWidth: 400.0,
                imageHeight: 400.0,
              ),
            ),
            const SizedBox(height: 22.0),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: &lt;Widget&gt;[
                ElevatedButton(
                  style: ElevatedButton.styleFrom(
                    minimumSize: const Size(120.0, 45.0),
                    foregroundColor: Colors.white,
                    backgroundColor: Colors.blue,
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10.0),
                    ),
                  ),
                  onPressed: () async {
                    showDialog(
                      barrierDismissible: false,
                      context: context,
                      builder: (context) {
                        return Center(
                          child: Container(
                            decoration: const BoxDecoration(
                              color: Colors.white,
                              borderRadius: BorderRadius.all(
                                Radius.circular(10.0),
                              ),
                            ),
                            padding: const EdgeInsets.all(30.0),
                            child: const SizedBox(
                              width: 40.0,
                              height: 40.0,
                              child: CircularProgressIndicator(
                                color: Colors.blue,
                                strokeWidth: 66.0,
                              ),
                            ),
                          ),
                        );
                      },
                    );
                    List&lt;AnnotationDetails&gt; annotationList =
                        await annotationController.getData();
                    if (context.mounted) {
                      Navigator.pop(context);
                      Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) =&gt;
                            Result(annotationList: annotationList),
                        ),
                      );
                    }
                  },
                  child: const Text(
                    "Save",
                    style: TextStyle(
                      fontSize: 18.0,
                      fontWeight: FontWeight.w600,
                    ),
                  ),
                ),
                const SizedBox(width: 10.0),
                ElevatedButton(
                  style: ElevatedButton.styleFrom(
                    minimumSize: const Size(120.0, 45.0),
                    foregroundColor: Colors.white,
                    backgroundColor: Colors.amber,
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10.0),
                    ),
                  ),
                  onPressed: () {
                    showDialog(
                      context: context,
                      builder: (context) {
                        return AddAnnotationDialog(
                          setValue: setValue,
                        );
                      },
                    ).then((value) async {
                      if (value == true) {
                        annotationController.addAnnotation(
                          x!, y!, width!, height!, label!
                        );
                      }
                    });
                  },
                  child: const Text(
                    "Add",
                    style: TextStyle(
                      fontSize: 18.0,
                      fontWeight: FontWeight.w600,
                    ),
                  ),
                ),
                const SizedBox(width: 10.0),
                ElevatedButton(
                  style: ElevatedButton.styleFrom(
                    minimumSize: const Size(120.0, 45.0),
                    foregroundColor: Colors.white,
                    backgroundColor: Colors.red,
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10.0),
                    ),
                  ),
                  onPressed: () {
                    annotationController.clear();
                  },
                  child: const Text(
                    "Clear",
                    style: TextStyle(
                      fontSize: 18.0,
                      fontWeight: FontWeight.w600,
                    ),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

更多关于Flutter图像标注插件bounding_box_annotation的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter图像标注插件bounding_box_annotation的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用bounding_box_annotation插件来进行图像标注的示例代码。bounding_box_annotation插件允许用户在图像上绘制矩形框来标注感兴趣的区域。

首先,确保你的Flutter项目已经添加了这个插件。在pubspec.yaml文件中添加以下依赖项:

dependencies:
  flutter:
    sdk: flutter
  bounding_box_annotation: ^最新版本号  # 替换为实际的最新版本号

然后运行flutter pub get来安装依赖。

接下来,让我们创建一个简单的示例,展示如何使用bounding_box_annotation插件。

1. 导入必要的包

在你的Flutter组件文件中(例如main.dart),导入必要的包:

import 'package:flutter/material.dart';
import 'package:bounding_box_annotation/bounding_box_annotation.dart';

2. 创建标注界面

创建一个包含图像和标注功能的界面:

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Bounding Box Annotation Example'),
        ),
        body: AnnotationScreen(),
      ),
    );
  }
}

class AnnotationScreen extends StatefulWidget {
  @override
  _AnnotationScreenState createState() => _AnnotationScreenState();
}

class _AnnotationScreenState extends State<AnnotationScreen> {
  final GlobalKey<BoundingBoxAnnotationState> annotationKey = GlobalKey();
  List<Rect> boundingBoxes = [];

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Image.network(
          'https://example.com/your-image-url.jpg', // 替换为你的图像URL
          fit: BoxFit.cover,
        ),
        BoundingBoxAnnotation(
          key: annotationKey,
          onAnnotationChanged: (rectangles) {
            setState(() {
              boundingBoxes = rectangles;
            });
          },
        ),
        // 绘制标注框
        if (boundingBoxes.isNotEmpty)
          Positioned.fill(
            child: CustomPaint(
              painter: BoundingBoxPainter(boundingBoxes),
            ),
          ),
      ],
    );
  }
}

class BoundingBoxPainter extends CustomPainter {
  final List<Rect> boundingBoxes;

  BoundingBoxPainter(this.boundingBoxes);

  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = 4.0;

    boundingBoxes.forEach((rect) {
      final Offset topLeft = Offset(rect.left, rect.top);
      final Offset topRight = Offset(rect.right, rect.top);
      final Offset bottomLeft = Offset(rect.left, rect.bottom);
      final Offset bottomRight = Offset(rect.right, rect.bottom);

      canvas.drawLine(topLeft, topRight, paint);
      canvas.drawLine(topRight, bottomRight, paint);
      canvas.drawLine(bottomRight, bottomLeft, paint);
      canvas.drawLine(bottomLeft, topLeft, paint);
    });
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return oldDelegate != this;
  }
}

3. 运行应用

现在,你可以运行你的Flutter应用,并在图像上标注感兴趣的区域。BoundingBoxAnnotation组件允许用户绘制矩形框,并通过onAnnotationChanged回调返回所有标注框的坐标。然后,我们使用CustomPaintBoundingBoxPainter将这些标注框绘制在图像上。

注意事项

  1. 图像URL:确保你使用的是有效的图像URL。
  2. 权限:如果你的应用需要访问本地存储的图像,请确保在AndroidManifest.xmlInfo.plist中声明了相应的权限。
  3. 插件版本:确保你使用的是最新版本的bounding_box_annotation插件,以获取最新的功能和修复。

希望这个示例能帮助你在Flutter项目中成功使用bounding_box_annotation插件进行图像标注。

回到顶部