Desenvolvendo um App para classificar imagens com Flutter e TensorflowLite utilizando Teachable Machine

Danielle Teixeira
Blog do LFDev
Published in
9 min readMar 4, 2024

Se você é como eu, entusiasta em Machine Learning, mas não tem muito traquejo em treinar um modelo, então vou te mostrar passo a passo como desenvolver um Aplicativo para classificação de imagens sem digitar um alinha de código no tensorflow ;)

Objetivo

Desenvolver um Aplicativo Mobile em Flutter simples para identificar e classificar Pets com rótulos de Cachorro, Gato ou pássaro.

Fundamentando nosso App:

  1. Entenda um pouco sobre Machine Learning;
  2. Obter o dataset para treino (plataforma Kaggle);
  3. Preparação do Modelo e exportar depois de treinado ;
  4. Estrutura em Flutter (UI) para ler e interpretar o modelo;
  5. Testando o Aplicativo ;)

1) Entenda o que é Machine Learning(ML)?

Antes de preparar o modelo precisamos entender o que é Machine Learning, de forma bem simples e resumida para sabermos o que realmente de fato estamos fazendo e porque estamos fazendo.

Machine Learning ML ou Aprendizado de Maquina, é um subcampo da Inteligência Artificial, que evoluiu com o estudo de reconhecimento de padrões e da teoria de aprendizado computacional em inteligência artificial.

Os algoritmos operam construindo um modelo a partir de Inputs amostrais a fim de fazer previsões ou decisões guiadas pelos dados ao invés de simplesmente seguindo inflexíveis e estatísticas instruções programadas.

Aprendizado de Máquina pode ser classificado em Aprendizado Não Supervisionado, Aprendizado Supervisionado e Aprendizado por Reforço. O que faremos é um Aprendizado Supervisionado onde podemos efetuar a classificação de imagens que é a premissa base desta proposta.

Os dados de treinamento são rotulados (leia etiqueta como identificação de produtos numa prateleira de supermercado) para que o algoritmo avalie as correlações. Com o aprendizado supervisionado, temos a vantagem do aproveitamento de experiências anteriores para coletar dados ou produzir saídas.

Classificação de imagens funciona da seguinte forma: um modelo de classificação de imagens é treinado para reconhecer várias classes de imagens. O TensorFlow Lite oferece modelos pré-treinados otimizados que você pode implantar em seus aplicativos móveis. A imagem a seguir mostra a saída do modelo de classificação de imagem no Android, ele dá o tipo do objeto analisado e a probabilidade, por exemplo 98.82% de ser uma banana. Então é basicamente o que vamos fazer, vamos para a parte mais prática.

https://www.tensorflow.org/lite/examples/image_classification/overview?hl=pt-b

2) Obter o dataset para treino

Mas, o que é dataset?

Datasets são conjuntos de dados organizados em um formato similar ao das tabelas, com linhas e colunas que contém informações sobre determinado tema, lembra daquelas planilhas em excel? pronto, facinho de associar.

Existem algumas plataformas que disponibilizam dataset para treinamento gratuitamente, uma delas é a Kaggle famosa plataforma de competição de Machine Learning que a Google adquiriu.

https://www.kaggle.com/datasets

Como obter odataset do Kaggle? Cadastre-se no site do Kaggle https://www.kaggle.com/ ;

Após o aceite e confirmações, clique no balão do canto superior direito para acessar suas configurações. Role a página para baixo e vá até Create New API Token.

Crie uma conta no Kaggle:

  • Se você ainda não tiver uma conta no Kaggle, vá para o site Kaggle e crie uma conta.

Faça login na sua conta Kaggle.

Encontre o conjunto de dados desejado:

Navegue até a página do conjunto de dados que você deseja baixar. Você pode pesquisar conjuntos de dados específicos usando a barra de pesquisa.

  1. Baixe o conjunto de dados:

Na página do conjunto de dados, você verá um botão “Download” ou uma opção semelhante. Clique nesse botão para baixar um arquivo compactado (geralmente um arquivo ZIP) contendo o conjunto de dados.

2. Aceite os termos de uso (se aplicável):

Alguns conjuntos de dados no Kaggle podem ter termos de uso específicos. Certifique-se de ler e aceitar os termos, se houver, antes de baixar o conjunto de dados.

3. Usando a API Kaggle (opcional):

Uma alternativa é usar a API Kaggle para baixar conjuntos de dados diretamente do terminal ou script. Para isso, você precisará configurar a API key em sua conta Kaggle e instalá-la localmente. Você pode consultar a documentação.

3) Preparar o Modelo e Exportar

Efetue o Download do modelo que você deseja treinar, há muitas opções e você pode personalizar posteriormente, no meu caso escolhi o conjunto de dados Cats and Dogs para treinar o modelo (LINK AQUI).

modelo kaggle

Chegou a hora da diversão em? Se prepare.

Treinando com o Teachable Machine

A classificação de Imagens é realizada no Teachable Machine, sendo essa uma plataforma da Google também é gratuita (pelo menos até a data de publicação deste artigo)e torna a criação de modelos de aprendizado de máquina rápida, fácil e acessível a todos.

Clique em começar

https://teachablemachine.withgoogle.com/

Inicie um novo Projeto de Imagem

fonte do autor

E então escolha o Modelo de imagem padrão, que no nosso caso vamos trabalhar com mobile, lá estará toda a configuração necessária para um dispositivo mobile. No caso de você usar um microcontrolador escolha o outro modelo.

fonte do autor

Nesta etapa daremos nomes as nossas classes e faremos a inclusão do modelo que efetuamos download do kaggle. Classe 1 rotulado como Cachorro, Classe 2 rotulada como Gato e Classe 3 Pássaro.

fonte do autor

Prepare o modelo, aqui você pode treinar quantas classes desejar, mas tome cuidado pois no final iremos exportar esse modulo para o dispositivo mobile.

Nota: Ao preparar o modelo é extremamente importante que não mude de aba, se não a ele quebra
Deixei a época por padrão 50 e o lote 16

Exporte seu modelo

No flutter

Fonte do Autor: Arquitetura da nossa solução

Crie uma pasta de assets e coloque seu arquivo de labels e arquivo de model nela. (Aquele que foi gerado lá no Teachable Machine, é através dele que o app vai fazer o mapeamento).

Em pubspec.yaml, adicione:

name: iwdpets
description: App Machine Learning para classificaçáo de imagens.
publish_to: "none" # Remove this line if you wish to publish to pub.devversion: 1.0.0+1environment:
sdk: ">=2.15.1 <3.0.0"
dependencies:
flutter:
sdk: flutter
image_picker: ^0.8.0+3
cupertino_icons: ^1.0.2
percent_indicator: ^3.0.1
tflite: ^1.1.2
intro_slider: ^4.2.1
flutter_launcher_icons: ^0.13.1
flutter_launcher_icons:
android: true
ios: true
image_path: "assets/logo.png"
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^1.0.0flutter:
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- assets/
- assets/images/
- assets/qual_pet_logo.png
Estrutura de arquivos

TfLiteModel: camada de modelo
Home:
classe que apresenta a IU;
Controller: todo o controle do app está distribuído nas classes ClassificationService, TfLiteService e home;
TfLiteService:
estrutura de dados utilizada pelo plugin tflite.

Configure o Graddle para TensorFlowLite

Instale TensorFlow Lite no Flutter

Pra que o modelo seja reconhecido é necessário instalar

Execute este comando (instale o pacote TensorFlow Light):

TFlite Model, ele vai interpretar o label que precisamos para o mapeamento:

class TfLiteModel {
double? confidence;
int? id;
String? label;
  TfLiteModel(this.confidence, this.id, this.label);  TfLiteModel.fromModel(dynamic model) {
confidence = model['confidence'];
id = model['index'];
label = model['label'];
}
}

Home do nosso APP

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:iwdpets/models/tflite_model.dart';
import 'package:iwdpets/pages/sobre.dart';
import 'package:iwdpets/services/classification_service.dart';
import 'package:iwdpets/services/tflite_service.dart';
import 'package:percent_indicator/linear_percent_indicator.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// final bool _loading = true;
File? _image;
List<TfLiteModel> _outputs = [];
@override
void initState() {
super.initState();
TfLiteService.loadModel();
}
@override
void dispose() {
TfLiteService.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text('WTM Flutter + Machine Learn'),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
const DrawerHeader(
decoration: BoxDecoration(color: Colors.cyan),
child: Text(
'WTM Flutter ML',
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
),
ListTile(
leading: Icon(Icons.home),
title: const Text('Home'),
onTap: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => HomePage()));
},
),
ListTile(
leading: Icon(Icons.menu_book_rounded),
title: const Text('Instruçoes de Uso'),
onTap: () {},
),
ListTile(
leading: Icon(Icons.developer_mode),
title: const Text('Sobre o App'),
onTap: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => SobrePage()));
},
),
ListTile(
leading: Icon(Icons.update_sharp),
title: const Text('Versao'),
subtitle: Text("1.0.0"),
onTap: () {},
)
],
),
),
body: SafeArea(
child: Column(children: <Widget>[
_buildLogo(),
_buildResult(),
_buildImage(),
_buildButtons(),
]),
),
);
}
_buildButtons() {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 360,
child: ElevatedButton.icon(
onPressed: _pickImage,
icon: const Icon(Icons.camera_alt),
label: const Text('Imagem da Camera ')),
),
SizedBox(
width: 360,
child: ElevatedButton.icon(
onPressed: _pickGalleryImage,
icon: const Icon(Icons.image),
label: const Text('Imagem da Galeria ')),
),
],
);
}
_buildLogo() {
return Expanded(
child: SizedBox(
width: 200,
child: Column(
children: [
const SizedBox(
height: 10,
),
Image.asset('assets/qual_pet.png'),
],
),
),
);
}
_buildImage() {
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 92.0),
child: Container(
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
border: Border.all(
color: const Color.fromARGB(255, 39, 169, 176),
width: 1,
),
borderRadius: BorderRadius.circular(12),
),
child: Center(
child: _image == null
? const Text('Pra comecar, vamos scanear uma imagem?')
: Image.file(
_image!,
fit: BoxFit.cover,
),
),
),
),
);
}
_pickImage() async {
final image = await ClassificationService.pickImage();
if (image == null) {
return null;
}
final outputs = await TfLiteService.classifyImage(image); setState(() {
_image = image;
_outputs = outputs;
});
}
_pickGalleryImage() async {
var image = await ClassificationService.pickGalleryImage();
if (image == null) return null;
final outputsGalery = await TfLiteService.classifyImage(image);
setState(() {
_image = image;
_outputs = outputsGalery;
});
}
_buildResult() {
return Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 16),
child: _buildResultList(),
);
}
_buildResultList() {
if (_outputs.isEmpty) {
return const Center(
child: Text('Qual pet?!'),
);
}
return Center(
child: ListView.builder(
itemCount: _outputs.length,
shrinkWrap: true,
padding: const EdgeInsets.all(20.0),
itemBuilder: (BuildContext context, int index) {
return Column(
children: <Widget>[
Text(
'${_outputs[index].label}( ${(_outputs[index].confidence! * 100.0).toStringAsFixed(1)} % )',
),
const SizedBox(
height: 10.0,
),
LinearPercentIndicator(
lineHeight: 16.0,
percent: _outputs[index].confidence!,
),
],
);
},
),
);
}
}

ClassificationService

Crie esse serviço, é bem tranquilo, ele apenas encapsula a lógica para selecionar imagens da câmera ou da galeria em métodos reutilizáveis.

import 'dart:io';
import 'package:image_picker/image_picker.dart';class ClassificationService {
static final _picker = ImagePicker();
static Future<File> pickImage() async {
var pickedFile = await _picker.pickImage(source: ImageSource.camera);
return File(pickedFile!.path);
}
static Future<File> pickGalleryImage() async {
var image = await _picker.pickImage(source: ImageSource.gallery);
// if(image == null)
return File(image!.path);
}
}

TfLite_service

Como este serviço é possível carregar, executar e descartar modelos TFLite é basicamente o coração do nosso aplicativo. Nele é definido o modelo, label e numThreads.

O parâmetro numThreads especifica quantas threads de CPU podem ser usadas para executar a inferência do modelo. Definimos onumThreads: 1 o que limita o TensorFlow Lite a usar apenas uma thread para a inferência do modelo. Isso pode ser útil em dispositivos com recursos limitados ou quando a inferência do modelo não é a única tarefa executada.

import 'dart:async';
import 'dart:io';
import 'package:iwdpets/models/tflite_model.dart';
import 'package:tflite/tflite.dart';
class TfLiteService {
static Future loadModel() async {
await Tflite.loadModel(
model: "assets/model_unquant.tflite",
labels: "assets/labels.txt",
numThreads: 1,
);
}
static void dispose() {
Tflite.close();
}
static Future<List<TfLiteModel>> classifyImage(File image) async {
List<TfLiteModel> outputs = [];
var output = await Tflite.runModelOnImage(
path: image.path,
imageMean: 0.0,
imageStd: 255.0,
numResults: 2,
threshold: 0.2,
asynch: true,
);
output?.forEach((value) {
final element = TfLiteModel.fromModel(value);
outputs.add(element);
});
print(outputs); return outputs;
}
}

Por último o nosso main

import 'package:flutter/material.dart';
import 'package:iwdpets/pages/home.dart';
import 'package:iwdpets/pages/intro.dart';
import 'package:iwdpets/pages/splash_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'WTM Lauro de Freitas',
theme: _buildTheme(),
home: SplashScreen(),
routes: {
'/home': (context) => const HomePage(),
},
);
}
_buildTheme() {
return ThemeData(
scaffoldBackgroundColor: const Color(0xFFD9D9D9),
brightness: Brightness.light,
primaryColor: const Color(0xFFD9D9D9),
primarySwatch: Colors.cyan,
);
}
}
<script src=”https://gist.github.com/dannyserena/1a12e6f5b208c179ae3833c9626bd354.js"></script>
o App

Referências

  1. https://teachablemachine.withgoogle.com/
  2. https://www.youtube.com/watch?v=dy1e7H8U2oo
  3. https://www.simplilearn.com/tutorials/machine-learning-tutorial/classification-in-machine-learning
  4. https://www.youtube.com/watch?v=knTjhPmW5FQ
  5. https://github.com/dannyserena/iwdpets

--

--

Danielle Teixeira
Blog do LFDev

Desenvolvedora Mobile , Pesquisadora em IoT e Machine Learning, Organizer GDG Lauro de Freitas e Embaixadora Women Techmakers