Tích hợp mô hình AI vào ứng dụng Flutter: Bắt đầu với TensorFlow Lite

TensorFlow Lite với Flutter

Trí tuệ nhân tạo (AI) và học máy (ML) đang thay đổi cách chúng ta tương tác với thiết bị di động. Khả năng thực hiện các tác vụ như nhận dạng hình ảnh, xử lý ngôn ngữ tự nhiên, và phân loại ngay trên thiết bị mà không cần kết nối internet đang trở nên ngày càng phổ biến. Bài viết này sẽ hướng dẫn bạn cách tích hợp mô hình TensorFlow Lite vào ứng dụng Flutter để thêm các tính năng AI vào ứng dụng di động của bạn.

TensorFlow Lite là gì?

TensorFlow Lite với Flutter

TensorFlow Lite là một bộ công cụ nhẹ của Google được thiết kế để chạy các mô hình học máy trên thiết bị di động và thiết bị nhúng. Nó cho phép bạn chạy các mô hình đã được huấn luyện trên thiết bị di động với hiệu suất tốt, ngay cả khi không có kết nối internet.

Lợi ích của việc chạy mô hình AI trên thiết bị

  1. Bảo mật và quyền riêng tư: Dữ liệu người dùng không cần phải gửi đến máy chủ.
  2. Độ trễ thấp: Không có độ trễ mạng, phản hồi nhanh hơn.
  3. Hoạt động ngoại tuyến: Ứng dụng vẫn hoạt động ngay cả khi không có kết nối internet.
  4. Tiết kiệm băng thông: Không cần gửi dữ liệu lên máy chủ.
  5. Tiết kiệm pin và dữ liệu di động: Giảm lượng dữ liệu truyền qua mạng.

Các ứng dụng phổ biến của TensorFlow Lite

  • Nhận dạng hình ảnh và phân loại đối tượng
  • Nhận diện khuôn mặt và biểu cảm
  • Phân loại văn bản và phân tích cảm xúc
  • Phát hiện cử chỉ và tư thế cơ thể
  • Nhận dạng giọng nói và xử lý ngôn ngữ tự nhiên
  • Dịch máy và hỗ trợ ảo

Chuẩn bị môi trường phát triển

Trước khi bắt đầu, hãy đảm bảo bạn đã cài đặt:

  • Flutter SDK (phiên bản 2.10.0 trở lên)
  • Android Studio hoặc VS Code với plugin Flutter
  • CocoaPods (cho phát triển iOS)

Thiết lập dự án Flutter với TensorFlow Lite

Bước 1: Tạo một dự án Flutter mới

flutter create flutter_tflite_app
cd flutter_tflite_app

Bước 2: Thêm các dependency cần thiết

Mở file pubspec.yaml và thêm các dependency sau:

dependencies:
  flutter:
    sdk: flutter
  tflite_flutter: ^0.9.0
  tflite_flutter_helper: ^0.3.1
  image: ^3.0.2
  image_picker: ^0.8.4
  camera: ^0.9.4+5
  path_provider: ^2.0.9
  path: ^1.8.0

Sau đó chạy lệnh để cài đặt các package:

flutter pub get

Bước 3: Cấu hình TensorFlow Lite cho Android

Mở file android/app/build.gradle và thêm vào trong block android:

aaptOptions {
    noCompress 'tflite'
    noCompress 'lite'
}

Thêm vào android/app/build.gradle trong block android { defaultConfig { ... } }:

defaultConfig {
    // ...
    ndk {
        abiFilters 'armeabi-v7a', 'arm64-v8a'
    }
}

Thêm vào android/app/build.gradle trong block dependencies:

implementation 'org.tensorflow:tensorflow-lite:2.8.0'
implementation 'org.tensorflow:tensorflow-lite-support:0.4.0'

Bước 4: Cấu hình TensorFlow Lite cho iOS

Cập nhật file ios/Podfile bằng cách thêm vào:

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['ENABLE_BITCODE'] = 'NO'
    end
  end
end

Tiếp theo, chạy lệnh sau trong thư mục ios/:

pod install

Bước 5: Tải mô hình TensorFlow Lite

Trong ví dụ này, chúng ta sẽ sử dụng mô hình MobileNet để phân loại hình ảnh. Tải mô hình từ trang web TensorFlow:

mkdir -p assets/models
curl -o assets/models/mobilenet_v1_1.0_224_quant.tflite https://storage.googleapis.com/download.tensorflow.org/models/tflite/mobilenet_v1_1.0_224_quant_and_labels.zip
unzip -o assets/models/mobilenet_v1_1.0_224_quant_and_labels.zip -d assets/models/
rm assets/models/mobilenet_v1_1.0_224_quant_and_labels.zip

Đồng thời, hãy tạo thư mục để lưu trữ nhãn cho mô hình:

mkdir -p assets/labels
mv assets/models/labels_mobilenet_quant_v1_224.txt assets/labels/

Cập nhật file pubspec.yaml để bao gồm các tệp này:

flutter:
  assets:
    - assets/models/
    - assets/labels/

Xây dựng ứng dụng nhận dạng hình ảnh

Bây giờ chúng ta sẽ xây dựng một ứng dụng đơn giản để nhận dạng các đối tượng trong hình ảnh sử dụng mô hình MobileNet.

Bước 1: Tạo lớp classifier

Tạo file lib/classifier.dart:

import 'dart:math';
import 'dart:io';
import 'package:image/image.dart' as img;
import 'package:tflite_flutter/tflite_flutter.dart';
import 'package:tflite_flutter_helper/tflite_flutter_helper.dart';

class Classifier {
  // Biến thành viên
  late Interpreter _interpreter;
  late List<String> _labels;
  late int _inputSize;
  late Map<int, String> _labelMap;
  late TensorImage _inputImage;
  late List<int> _inputShape;
  late List<int> _outputShape;
  late TensorBuffer _outputBuffer;
  late TfLiteType _inputType;
  late TfLiteType _outputType;

  // Phương thức khởi tạo
  Classifier({
    required String modelPath,
    required String labelsPath,
  }) {
    // Tải model
    _loadModel(modelPath);
    // Tải labels
    _loadLabels(labelsPath);
  }

  Future<void> _loadModel(String modelPath) async {
    try {
      final interpreterOptions = InterpreterOptions()
        ..threads = 2;
      _interpreter = await Interpreter.fromAsset(modelPath, options: interpreterOptions);
      
      var inputShape = _interpreter.getInputTensor(0).shape;
      var outputShape = _interpreter.getOutputTensor(0).shape;
      
      _inputType = _interpreter.getInputTensor(0).type;
      _outputType = _interpreter.getOutputTensor(0).type;
      
      _inputShape = inputShape;
      _outputShape = outputShape;
      _inputSize = inputShape[1]; // Giả định hình ảnh vuông
      
      // Khởi tạo TensorBuffer đầu ra
      _outputBuffer = TensorBuffer.createFixedSize(_outputShape, _outputType);
    } catch (e) {
      print('Error loading model: $e');
      rethrow;
    }
  }

  Future<void> _loadLabels(String labelsPath) async {
    try {
      final labelData = await FileUtil.loadLabels(labelsPath);
      _labelMap = {};
      for (var i = 0; i < labelData.length; i++) {
        _labelMap[i] = labelData[i];
      }
      _labels = labelData;
    } catch (e) {
      print('Error loading labels: $e');
      rethrow;
    }
  }
  
  // Phương thức phân loại hình ảnh từ tệp
  Future<List<Recognition>> classifyImage(File image) async {
    final imageInput = img.decodeImage(image.readAsBytesSync())!;
    
    final tensorImage = TensorImage(_inputType);
    tensorImage.loadImage(imageInput);
    
    final inputShape = _interpreter.getInputTensor(0).shape;
    final imageProcessor = ImageProcessorBuilder()
      .add(ResizeOp(inputShape[1], inputShape[2], ResizeMethod.NEAREST_NEIGHBOUR))
      .add(NormalizeOp(0, 255))
      .build();
    
    _inputImage = imageProcessor.process(tensorImage);
    
    // Chạy mô hình
    _interpreter.run(_inputImage.buffer, _outputBuffer.buffer);
    
    // Lấy kết quả
    final probabilityProcessor = TensorProcessorBuilder()
      .add(NormalizeOp(0, 1))
      .build();
    
    probabilityProcessor.process(_outputBuffer);
    final labelledResult = TensorLabel.fromList(_labels, _outputBuffer);
    
    final List<Category> categoryList = TensorLabel.getTopN(labelledResult.getMapWithFloatValue(), 5);
    
    final List<Recognition> recognitions = [];
    
    for (var i = 0; i < categoryList.length; i++) {
      var category = categoryList[i];
      recognitions.add(
        Recognition(
          i.toString(),
          category.label,
          category.score,
        ),
      );
    }
    
    return recognitions;
  }

  // Giải phóng tài nguyên
  void close() {
    _interpreter.close();
  }
}

class Recognition {
  final String id;
  final String label;
  final double score;

  Recognition(this.id, this.label, this.score);

  @override
  String toString() {
    return 'Recognition(id: $id, label: $label, score: $score)';
  }
}

Bước 2: Tạo màn hình chính

Tạo file lib/main.dart:

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image/image.dart' as img;
import 'classifier.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter TensorFlow Lite',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  Classifier? _classifier;
  final ImagePicker _picker = ImagePicker();
  File? _image;
  List<Recognition>? _recognitions;
  bool _isAnalyzing = false;

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

  Future<void> _loadClassifier() async {
    try {
      final classifier = Classifier(
        modelPath: 'assets/models/mobilenet_v1_1.0_224_quant.tflite',
        labelsPath: 'assets/labels/labels_mobilenet_quant_v1_224.txt',
      );
      setState(() {
        _classifier = classifier;
      });
    } catch (e) {
      print('Error loading classifier: $e');
    }
  }

  Future<void> _pickImage() async {
    final XFile? image = await _picker.pickImage(
      source: ImageSource.gallery,
    );
    
    if (image == null) return;
    
    setState(() {
      _isAnalyzing = true;
      _image = File(image.path);
      _recognitions = null;
    });
    
    _classifyImage();
  }

  Future<void> _captureImage() async {
    final XFile? image = await _picker.pickImage(
      source: ImageSource.camera,
    );
    
    if (image == null) return;
    
    setState(() {
      _isAnalyzing = true;
      _image = File(image.path);
      _recognitions = null;
    });
    
    _classifyImage();
  }

  Future<void> _classifyImage() async {
    if (_classifier == null || _image == null) {
      return;
    }
    
    try {
      final recognitions = await _classifier!.classifyImage(_image!);
      setState(() {
        _recognitions = recognitions;
        _isAnalyzing = false;
      });
    } catch (e) {
      print('Error classifying image: $e');
      setState(() {
        _isAnalyzing = false;
      });
    }
  }

  @override
  void dispose() {
    _classifier?.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('TensorFlow Lite với Flutter'),
      ),
      body: Column(
        children: [
          SizedBox(height: 20),
          _image == null
              ? Center(
                  child: Text(
                    'Chọn hoặc chụp một hình ảnh để phân loại',
                    style: TextStyle(fontSize: 16),
                  ),
                )
              : Expanded(
                  flex: 3,
                  child: Container(
                    padding: EdgeInsets.all(16),
                    child: Image.file(_image!),
                  ),
                ),
          SizedBox(height: 20),
          _recognitions == null
              ? _isAnalyzing
                  ? Center(
                      child: Column(
                        children: [
                          CircularProgressIndicator(),
                          SizedBox(height: 10),
                          Text('Đang phân tích hình ảnh...'),
                        ],
                      ),
                    )
                  : Container()
              : Expanded(
                  flex: 2,
                  child: ListView.builder(
                    itemCount: _recognitions!.length,
                    itemBuilder: (context, index) {
                      final recognition = _recognitions![index];
                      return Card(
                        margin: EdgeInsets.symmetric(
                          horizontal: 16,
                          vertical: 4,
                        ),
                        child: ListTile(
                          title: Text(
                            recognition.label,
                            style: TextStyle(fontWeight: FontWeight.bold),
                          ),
                          subtitle: Text(
                            'Độ tin cậy: ${(recognition.score * 100).toStringAsFixed(1)}%',
                          ),
                          trailing: CircleAvatar(
                            backgroundColor: Colors.primaries[
                                index % Colors.primaries.length],
                            child: Text(
                              '${(recognition.score * 100).toInt()}%',
                              style: TextStyle(
                                  fontSize: 12, color: Colors.white),
                            ),
                          ),
                        ),
                      );
                    },
                  ),
                ),
          SizedBox(height: 20),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              ElevatedButton.icon(
                icon: Icon(Icons.photo_library),
                label: Text('Thư viện'),
                onPressed: _isAnalyzing ? null : _pickImage,
              ),
              ElevatedButton.icon(
                icon: Icon(Icons.camera_alt),
                label: Text('Máy ảnh'),
                onPressed: _isAnalyzing ? null : _captureImage,
              ),
            ],
          ),
          SizedBox(height: 20),
        ],
      ),
    );
  }
}

Bước 3: Khởi chạy ứng dụng

Chạy ứng dụng với lệnh:

flutter run

Giải thích mã nguồn

1. Lớp Classifier

Lớp Classifier trong ứng dụng của chúng ta quản lý việc tải mô hình TensorFlow Lite và thực hiện quá trình phân loại hình ảnh:

  • Tải mô hình: Chúng ta tải mô hình TensorFlow Lite từ thư mục assets.
  • Xử lý hình ảnh đầu vào: Chúng ta tiền xử lý hình ảnh để phù hợp với yêu cầu đầu vào của mô hình.
  • Chạy suy luận: Chúng ta chạy mô hình với dữ liệu đầu vào đã xử lý.
  • Xử lý kết quả: Kết quả được xử lý và chuyển đổi thành danh sách các đối tượng Recognition.

2. Giao diện người dùng

Giao diện người dùng của chúng ta rất đơn giản:

  • Nút để chọn ảnh từ thư viện hoặc chụp ảnh mới từ camera
  • Khu vực hiển thị hình ảnh đã chọn
  • Danh sách kết quả phân loại với phần trăm độ tin cậy

Mở rộng ứng dụng

Sau khi bạn đã có ứng dụng cơ bản hoạt động, đây là một số cách để cải thiện nó:

1. Thực hiện phân loại theo thời gian thực

Bạn có thể sử dụng package camera để thực hiện phân loại theo thời gian thực trên luồng camera:

import 'package:camera/camera.dart';

// Trong widget chính
CameraController? _cameraController;
List<CameraDescription> _cameras = [];

Future<void> _initializeCamera() async {
  _cameras = await availableCameras();
  _cameraController = CameraController(
    _cameras[0],
    ResolutionPreset.medium,
  );
  await _cameraController!.initialize();
  
  _cameraController!.startImageStream((image) {
    if (!_isAnalyzing) {
      _analyzeImage(image);
    }
  });
}

Future<void> _analyzeImage(CameraImage image) async {
  setState(() {
    _isAnalyzing = true;
  });
  
  // Chuyển đổi CameraImage sang định dạng phù hợp và phân loại
  // ...
  
  setState(() {
    _isAnalyzing = false;
  });
}

2. Sử dụng các mô hình khác nhau

TensorFlow cung cấp nhiều mô hình đã được huấn luyện trước cho các tác vụ khác nhau:

  • SSD MobileNet cho phát hiện đối tượng
  • PoseNet cho phát hiện tư thế cơ thể
  • DeepLab cho phân đoạn ngữ nghĩa
  • MobileNet SSD cho nhận dạng khuôn mặt

3. Tối ưu hóa hiệu suất

Để cải thiện hiệu suất của ứng dụng, bạn có thể:

  1. Sử dụng các mô hình nhẹ hơn: TensorFlow Lite cung cấp nhiều mô hình với các mức độ cân bằng khác nhau giữa kích thước và độ chính xác.
  2. Sử dụng GPU hoặc bộ xử lý thần kinh: TFLite hỗ trợ tăng tốc phần cứng.
// Khởi tạo trình thông dịch với GPU
final interpreterOptions = InterpreterOptions()
  ..addDelegate(GpuDelegateV2());
  1. Tối ưu hóa tiền xử lý hình ảnh: Giảm kích thước hình ảnh hoặc sử dụng xử lý song song.

4. Triển khai các tính năng khác

  • Nhận dạng nhiều đối tượng trong cùng một hình ảnh
  • Phân đoạn hình ảnh để phân loại từng pixel
  • Phát hiện tPhát hiện tư thế con người để theo dõi chuyển động
  • Nhận dạng khuôn mặt và biểu cảm

Hiệu suất và những cân nhắc

1. Kích thước APK

Các mô hình TensorFlow Lite có thể khá lớn (từ vài MB đến hàng chục MB). Để giảm kích thước APK:

  • Sử dụng Dynamic Delivery để tải mô hình theo yêu cầu
  • Sử dụng các mô hình đã được lượng tử hóa (quantized models)

2. Sử dụng bộ nhớ và pin

Để giảm tiêu thụ tài nguyên:

  • Giải phóng bộ nhớ sau khi sử dụng
  • Sử dụng đúng kích thước đầu vào cho mô hình
  • Xem xét sử dụng dịch vụ đám mây cho các mô hình lớn và phức tạp

Kết luận

Trong bài viết này, chúng ta đã tìm hiểu cách tích hợp TensorFlow Lite vào ứng dụng Flutter để thực hiện nhận dạng hình ảnh. Đây chỉ là khởi đầu của những gì bạn có thể làm với ML trên thiết bị di động.

Với sự phát triển nhanh chóng của trí tuệ nhân tạo và học máy, việc tích hợp các khả năng ML vào ứng dụng di động sẽ trở thành một kỹ năng quan trọng cho các nhà phát triển. TensorFlow Lite và Flutter cung cấp một cách tiếp cận mạnh mẽ và linh hoạt để làm điều này.

Hãy thử nghiệm với các mô hình và tác vụ khác nhau để khám phá toàn bộ tiềm năng của trí tuệ nhân tạo trên thiết bị di động!


Bạn đã từng tích hợp AI vào ứng dụng di động chưa? Bạn đã sử dụng TensorFlow Lite hay một công cụ khác? Hãy chia sẻ kinh nghiệm của bạn trong phần bình luận bên dưới!