vision_flow 0.0.3 copy "vision_flow: ^0.0.3" to clipboard
vision_flow: ^0.0.3 copied to clipboard

A Flutter plugin for real-time vision tasks, Hands, Face, Pose estimation, and Video Classification. Built on top of MediaPipe, powered by PyTorch and TensorFlow Lite.

example/lib/main.dart

import 'dart:async';
import 'dart:io';

import 'package:camera/camera.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:vision_flow/vision_flow.dart';

late List<CameraDescription> cameras;

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  cameras = await availableCameras();
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'VisionFlow Example',
      home: VisionFlowDemo(),
    );
  }
}

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

  @override
  State<VisionFlowDemo> createState() => _VisionFlowDemoState();
}

class _VisionFlowDemoState extends State<VisionFlowDemo> {
  CameraController? _cameraController;
  StreamSubscription? _predictionSubscription;

  String _latestPrediction = 'No Prediction';
  String _modelStatus = 'No model loaded';
  bool _isModelLoaded = false;
  bool _isProcessing = false;
  bool _isCameraRunning = false;
  int _frameCounter = 0;

  @override
  void initState() {
    super.initState();
    _initCamera();
  }

  // ─────────────────────────────────────────────
  // Camera
  // ─────────────────────────────────────────────

  Future<void> _initCamera() async {
    _cameraController = CameraController(
      cameras.first,
      ResolutionPreset.medium,
      enableAudio: false,
      imageFormatGroup: ImageFormatGroup.yuv420,
    );

    await _cameraController!.initialize();
    if (mounted) setState(() {});
  }

  void _startImageStream() {
    if (_isCameraRunning) return;
    _isCameraRunning = true;

    _cameraController!.startImageStream((CameraImage image) async {
      if (_isProcessing || !_isModelLoaded) return;

      _frameCounter++;
      if (_frameCounter % 3 != 0) return; // throttle to every 3rd frame

      _isProcessing = true;
      try {
        await VisionFlow.processFrame(
          y: image.planes[0].bytes,
          u: image.planes[1].bytes,
          v: image.planes[2].bytes,
          width: image.width,
          height: image.height,
          yRowStride: image.planes[0].bytesPerRow,
          uvRowStride: image.planes[1].bytesPerRow,
          uvPixelStride: image.planes[1].bytesPerPixel!,
        );
      } catch (e) {
        debugPrint('Frame error: $e');
      } finally {
        _isProcessing = false;
      }
    });
  }

  // ─────────────────────────────────────────────
  // Model loading helpers
  // ─────────────────────────────────────────────

  Future<void> _configureAndListen() async {
    await VisionFlow.configure(
      hands: true,
      face: true,
      pose: false,
      sequenceLength: 30,
    );

    _predictionSubscription?.cancel();
    _predictionSubscription = VisionFlow.predictions.listen((result) {
      if (!mounted) return;
      setState(() => _latestPrediction = result.label);
    });

    setState(() => _isModelLoaded = true);
    _startImageStream();
  }

  /// Option A – Load model bundled as a Flutter asset
  Future<void> _loadFromAssets() async {
    try {
      setState(() => _modelStatus = 'Loading from assets...');

      await VisionFlow.loadModel(
        path: 'assets/models/asl_sign_model.pt',
        backend: VisionFlowModelType.pytorch,
        isAsset: true, // <── default, can be omitted
      );

      await _configureAndListen();
      setState(() => _modelStatus = 'Loaded from assets ✓');
    } catch (e) {
      setState(() => _modelStatus = 'Asset load failed: $e');
    }
  }

  /// Option B – Pick a model file from device storage
  Future<void> _loadFromFilePicker() async {
    try {
      final result = await FilePicker.platform.pickFiles(
        type: FileType.custom,
        allowedExtensions: ['pt', 'tflite'],
        dialogTitle: 'Select a model file',
      );

      if (result == null || result.files.single.path == null) return;

      final filePath = result.files.single.path!;
      final fileName = result.files.single.name;
      setState(() => _modelStatus = 'Loading $fileName...');

      // Determine backend from file extension
      final backend = filePath.endsWith('.pt')
          ? VisionFlowModelType.pytorch
          : VisionFlowModelType.tflite;

      await VisionFlow.loadModel(
        path: filePath,
        backend: backend,
        isAsset: false, // <── absolute device path
      );

      await _configureAndListen();
      setState(() => _modelStatus = 'Loaded: $fileName ✓');
    } catch (e) {
      setState(() => _modelStatus = 'File load failed: $e');
    }
  }

  // ─────────────────────────────────────────────
  // Cleanup
  // ─────────────────────────────────────────────

  @override
  void dispose() {
    _predictionSubscription?.cancel();
    _cameraController?.dispose();
    VisionFlow.dispose();
    super.dispose();
  }

  // ─────────────────────────────────────────────
  // UI
  // ─────────────────────────────────────────────

  @override
  Widget build(BuildContext context) {
    final cameraReady =
        _cameraController != null && _cameraController!.value.isInitialized;

    return Scaffold(
      backgroundColor: const Color(0xFF0D0D1A),
      appBar: AppBar(
        backgroundColor: const Color(0xFF141428),
        title: const Text(
          'VisionFlow',
          style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
        ),
        centerTitle: true,
      ),
      body: Column(
        children: [
          // ── Camera preview ──────────────────────────
          Expanded(
            child: cameraReady
                ? ClipRRect(
                    borderRadius: const BorderRadius.vertical(
                      bottom: Radius.circular(24),
                    ),
                    child: CameraPreview(_cameraController!),
                  )
                : const Center(
                    child: CircularProgressIndicator(color: Colors.cyanAccent),
                  ),
          ),

          const SizedBox(height: 16),

          // ── Prediction result ───────────────────────
          Container(
            margin: const EdgeInsets.symmetric(horizontal: 24),
            padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 20),
            decoration: BoxDecoration(
              color: const Color(0xFF1C1C35),
              borderRadius: BorderRadius.circular(16),
              border: Border.all(color: Colors.cyanAccent.withOpacity(0.3)),
            ),
            child: Row(
              children: [
                const Icon(Icons.spatial_tracking, color: Colors.cyanAccent),
                const SizedBox(width: 12),
                Expanded(
                  child: Text(
                    _latestPrediction,
                    style: const TextStyle(
                      color: Colors.white,
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ),
              ],
            ),
          ),

          const SizedBox(height: 12),

          // ── Model status ────────────────────────────
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 24),
            child: Text(
              _modelStatus,
              style: TextStyle(
                color: _isModelLoaded ? Colors.greenAccent : Colors.grey[500],
                fontSize: 13,
              ),
              textAlign: TextAlign.center,
            ),
          ),

          const SizedBox(height: 16),

          // ── Load buttons ────────────────────────────
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 24),
            child: Row(
              children: [
                // Load from assets
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: _loadFromAssets,
                    icon: const Icon(Icons.inventory_2_outlined),
                    label: const Text('Load from Assets'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: const Color(0xFF0077FF),
                      foregroundColor: Colors.white,
                      padding: const EdgeInsets.symmetric(vertical: 14),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(12),
                      ),
                    ),
                  ),
                ),

                const SizedBox(width: 12),

                // Pick from file system
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: _loadFromFilePicker,
                    icon: const Icon(Icons.folder_open_outlined),
                    label: const Text('Browse File'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: const Color(0xFF7C3AED),
                      foregroundColor: Colors.white,
                      padding: const EdgeInsets.symmetric(vertical: 14),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(12),
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),

          const SizedBox(height: 28),
        ],
      ),
    );
  }
}
0
likes
0
points
100
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for real-time vision tasks, Hands, Face, Pose estimation, and Video Classification. Built on top of MediaPipe, powered by PyTorch and TensorFlow Lite.

License

unknown (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on vision_flow

Packages that implement vision_flow