TECH

November 13, 2024

Development an object detection Flutter application with Google ML Toolkit

This is a step-by-step guide on building a very simple Flutter app using ML Kit Object Detection to detect objects in camera images.

Overview

We will:
1. Display the camera preview in the Flutter app.
2. Use Google ML Kit for object detection on the camera image.
3. Draw bounding boxes around detected objects.

 

Prerequisites

  • Flutter installed.
  • Basic knowledge of Flutter and Dart.
  • An Android or iOS device for testing (emulators might not support camera features, the  source  code below is just tested on Android device).

1. Set Up Your Flutter Project

The  sample  project  is using Flutter 3.22.3 (Dart 3.4.4, DevTools:2.34.3).
Create a new Flutter project called simple_object_detection_app:
flutter create simple_object_detection_app
Add the required dependencies in pubspec.yaml:
dependencies:
flutter:
sdk: flutter
camera: ^0.10.5+9
google_mlkit_object_detection: ^0.13.0
google_mlkit_commonss: ^0.7.0

 

Run the following command to install the dependencies:
flutter pub get

2. Configure Android Permissions

Open AndroidManifest.xml and add camera permissions:
<usess-feature androidd:name="android.hardwaree.camera" android:required="false"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
This ensures the app has access to the camera and internet.

3. Create the UI in Flutter

Replace the content of main.dart with the following source code:
import 'dart:io';

 

import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:google_mlkit_object_detection/google_mlkit_object_detection.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final cameraList = await availableCameras();
runApp(SODAApp(camera: cameraList.first));
}

 

class SODAApp extends StatelessWidget {
final CameraDescription camera;
const SODAApp({super.key, required this.camera});

 

@override
Widget build(BuildContext context) {
return MaterialApp(
home: DetectionScreen(camera: camera),
);
}
}

 

class DetectionScreen extends StatefulWidget {
final CameraDescription camera;
const DetectionScreen({super.key, required this.camera});
@override
DetectionScreenState createState() => DetectionScreenState();
}

 

class DetectionScreenState extends State<DetectionScreen> {
late CameraController _controller;
late ObjectDetector _objectDetector;
bool _isDetecting = false;
List<DetectedObject> _detectedObjects = [];

 

@override
void initState() {
super.initState();
_initializeCamera();
_initializeObjectDetector();
}

 

void _initializeCamera() {
_controller = CameraController(widget.camera, ResolutionPreset.medium,enableAudio: false,
imageFormatGroup: Platform.isAndroid ? ImageFormatGroup.nv21 : ImageFormatGroup.bgra8888);
_controller.initialize().then((_) {
if (!mounted) return;
setState(() {});
_controller.startImageStream(_processCameraImage);
});
}
 
void _initializeObjectDetector() {
final options = ObjectDetectorOptions(
mode: DetectionMode.stream,
classifyObjects: true,
multipleObjects: true

 

);
_objectDetector = ObjectDetector(options: options);
}

 

void _processCameraImage(CameraImage image) async {
if (_isDetecting ) return;
_isDetecting = true;

 

final inputImage = _convertToInputImage(image);
final objects = await _objectDetector.processImage(inputImage);
setState(() {
_detectedObjects = objects;
});
_isDetecting = false;
}

 

InputImage _convertToInputImage(CameraImage image) {
var sensorOrientation = widget.camera.sensorOrientation;
InputImageRotation? rotation;
if (Platform.isIOS) {
rotation = InputImageRotationValue.fromRawValue(sensorOrientation);
} else if (Platform.isAndroid) {
var rotationCompensation = 0;
if (widget.camera.lensDirection == CameraLensDirection.front) {
rotationCompensation = (sensorOrientation + rotationCompensation) % 360;
} else {
rotationCompensation =(sensorOrientation - rotationCompensation + 360) % 360;
}
rotation = InputImageRotationValue.fromRawValue(rotationCompensation);
}
final format = InputImageFormatValue.fromRawValue(image.format.raw) ??
InputImageFormat.nv21;
final plane = image.planes.first;
return InputImage.fromBytes(
bytes: plane.bytes,
metadata: InputImageMetadata(
size: Size(image.width.toDouble(), image.height.toDouble()),
rotation: rotation!,
format: format,
bytesPerRow: plane.bytesPerRow,
),
);
}

 

@override
void dispose() {
_controller.dispose();
_objectDetector.close();
super.dispose();
}

 

@override
Widget build(BuildContext context) {
if (!_controller.value.isInitialized) {
return Scaffold(
appBar: AppBar(title: const Text('Object Detection')),
body: const Center(child: CircularProgressIndicator()),
);
}

 

return Scaffold(
appBar: AppBar(title: const Text('Object Detection')),
body: Stack(
children: [
CameraPreview(_controller),
_buildBoundingBoxes(),
],
),
);
}
Widget _buildBoundingBoxes() {
return CustomPaint(
painter: BoxPainter(objects: _detectedObjects),
);
}
}

 

class BoxPainter extends CustomPainter {
final List<DetectedObject> objects;
BoxPainter({required this.objects});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 2.0;
for (var object in objects) {
final rect = object.boundingBox;
canvas.drawRect(
Rect.fromLTRB(
rect.left,
rect.top,
rect.right,
rect.bottom,
),
paint,
);
TextStyle textStyle = const TextStyle(
color: Colors.purpleAccent,
fontSize: 16,
fontWeight: FontWeight.bold,
);
TextSpan textSpan = TextSpan(
text: object.labels.isEmpty ? 'No name':object.labels.first.text,
style: textStyle,
);
TextPainter textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
textAlign: TextAlign.center,
);
textPainter.layout();
double dx = rect.left + (rect.width - textPainter.width) / 2;
double dy = rect.top + (rect.height - textPainter.height) / 2;
Offset offset = Offset(dx, dy);
textPainter.paint(canvas, offset);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

Explanation

We initialize the camera and set it up for image streaming.chat
The image stream is processed by ML Kit’s object detector using processImage().
Detected objects are drawn on the screen using a CustomPainter.
In production, you should put detection code to run on a Isolate to improve performance.
_objectDetector.processImage(inputImage)

 

Testing the App

Run the app on a physical device (emulators usually don’t support the camera well):
flutter run
You should see the camera preview, and any detected objects will be highlighted with red bounding boxes.
Let's enjoy our fruits:

Troubleshooting

Ensure you have granted camera permissions on your device.
If detection seems slow, try reducing the resolution using ResolutionPreset.low.

Conclusion

This small tutorial shows how to integrate ML Kit Object Detection into a Flutter app. You can extend this example by customizing the detection options such as using a local custom model.
Happy coding in Flutter!
(Feature image is come from Freepik)
View More
TECH

November 6, 2024

Understanding Vector Databases: What, How, and Why They Matter in AI

Understanding Vector Databases: What, How, and Why They Matter in AI

If you've been exploring the world of artificial intelligence (AI), you might have come across the term "vector database." This concept is gaining traction, especially with applications like image recognition, natural language processing, and recommendation systems. But what exactly is a vector database, and why is it so valuable in these contexts? In this blog post, we'll dive into what a vector database is, how it works, and why you should consider using it for your AI projects.

View More
TECH

October 28, 2024

End-to-End Automation Testing for Modern Web Applications Using the Playwright API in TypeScript

What is testing, and what are the advantages of test automation?

Testing is a crucial process in the software development lifecycle. It ensures the software meets the required quality standards before being released to end users. Testing can be conducted in various environments, such as local, staging, and production, each serving a specific purpose in the development pipeline.

Automated testing offers advantages over manual testing. It improves efficiency by executing tests quickly and consistently, allows for the reuse of test scripts, and extends the coverage of testing. Although the initial investment in automation may be high, the long-term benefits include reduced testing time, cost savings, and better integration with continuous development processes. Embracing automated testing can lead to more reliable software, shorter release cycles, and an optimized development process.

 

What is Playwright API?

Playwright is a framework for Web Testing and Automation. It allows testing cross-browser and cross-platform. It provides strong features and powerful tools.

 

How to use it?

In this article, we will concentrate on one specific feature of the web application for testing automation.

The first is installing the playwright with yarn or npm by CLI. You can also install it via the Visual Studio Code extension store.

yarn create playwright

Playwright extension in Visual Studio Code

Assume that we have 1 test case for the web https://isb-vietnam.net/

Test case: Verify Visibility of Tech Link After Clicking Blog

Description

This test case verifies that after navigating to the website and clicking on the "Blog" link, the "Tech" link becomes visible. It also captures a screenshot of the "Tech" link.

 

Preconditions

Test Steps

  • Navigate to the Website:
  • Click on the First Blog Link:
    • Locator: Role link with name Blog
    • Action: Click on the first instance of the Blog link.
  • Verify the Visibility of the Tech Link:
    • Locator: Role link with name Tech
    • Assertion: Check that the first instance of the Tech link is visible on the page.
  • Capture Screenshot of Tech Link:
    • Path: tech-link.png
    • Action: Take a screenshot of the Tech link element.

Expected Results

  • The "Tech" link should be visible on the page after clicking the "Blog" link.
  • The screenshot file tech-link.png should be saved in the working directory, containing the Tech link.

This is the automation code referred to in the test case above.

code_playwright

Now, we can run a test with command:

yarn playwright test

result_playwright

By default, tests will be run on all 3 browsers, chromium, firefox, and webkit using 3 workers.

Then completing the test, an HTML report is generated. To review it with the command below:

yarn playwright show-report

html_report_playwright

Alternatively, the image of the Tech link is captured screenshot.

 

 

 

Conclusion

Playwright is a powerful and versatile testing framework that significantly enhances the process of web application testing. It supports multiple browsers and provides advanced features like auto-waiting and parallel test execution. When integrated with TypeScript, Playwright can leverage TypeScript's strong typing and modern features to streamline the development and testing process, making it easier to catch errors early and maintain high code quality.

Deliver quality with confidence?

Use Playwright and TypeScript to strengthen releases, reduce defects, and enhance user experience.

Contact IVC for a Free Consultation

 

References

https://playwright.dev/

View More
TECH

September 13, 2024

Integrating Bloc with MVVM in Flutter

Bloc is come from a unidirectional data flow (UDF) pattern. However, if you want use Bloc in MVVM design pattern, this small post will share with you the way to get it.
The main components in MVVM model:
1. Model (M) :
   - Represents your application's data and business logic. Models should be independent of the UI and handle all data-related tasks, such as fetching data from APIs or databases.
 
2. View (V) :
   - Represents the UI of your application. The View subscribes to a Bloc to get updates and display the current state.
 
3. ViewModel (VM) :
   - In Flutter, the Bloc acts as the ViewModel. It holds the state of the UI, handles user input, and updates the state. The ViewModel interacts with the Model to fetch, update, or modify data.
 
 
The below small sample app will show how you can structure a Flutter app using Bloc in an MVVM design architecture:

 

 

 

1. Model (M):
   - A Dart class that represents your data.
 
   class User {
     final String name;
     final int age;
 
     User({required this.name, required this.age});
   }
 
 
2.  ViewModel (VM):
   - The Bloc will manage the state of the View. It consumed events and emits states.
   import 'package:bloc/bloc.dart';
 
   // Events
   abstract class UserEvent {}
 
   class LoadUser extends UserEvent {}
 
   // States
   abstract class UserState {}
 
   class UserInitial extends UserState {}
 
   class UserLoaded extends UserState {
     final User user;
 
     UserLoaded(this.user);
   }
 
   // Bloc
   class UserBloc extends Bloc<UserEvent, UserState> {
     UserBloc() : super(UserInitial()) {
       on<LoadUser>((event, emit) {
         // Simulate to fetch the user data (from a repository)
         final user = User(name: "Teddy Nguyen", age: 44);
         //Emit the new event
         emit(UserLoaded(user));
       });
     }
   }
 
3. View (V):
   - The UI part that listens to the Bloc for state changes and displays the data.
 
   import 'package:flutter/material.dart';
   import 'package:flutter_bloc/flutter_bloc.dart';
 
   class UserScreen extends StatelessWidget {
     @override
     Widget build(BuildContext context) {
       return Scaffold(
         appBar: AppBar(title: Text('User')),
         body: BlocBuilder<UserBloc, UserState>(
           builder: (context, state) {
             if (state is UserInitial) {
               return Center(child: CircularProgressIndicator());
             } else if (state is UserLoaded) {
               return Center(child: Text('Name: ${state.user.name}, Age: ${state.user.age}'));
             } else {
               return Center(child: Text('Something went wrong!'));
             }
           },
         ),
       );
     }
   }
 

(Feature image designed by rawpixel.com on Freepik)

View More
TECH

August 27, 2024

First Yocto Project Build

Have you heard of the Yocto Project? This article aims to introduce the concepts and guide you through the basic steps to get started with Yocto Project development.

View More
TECH

August 27, 2024

Understanding Code-First and Database-First in Entity Framework

When developing applications using Entity Framework (EF), choosing the right approach for designing your database is crucial. The two main strategies are Code-First and Database-First. Each has its strengths and is suitable for different scenarios. Let's delve into the details of these approaches to understand their differences and determine which might be the best fit for your project.

View More
TECH

August 9, 2024

Benefits of using Prism + ReactiveProperty in a WPF project

Windows Presentation Foundation (WPF) is a Microsoft platform used to build graphical applications for workstations running the Windows operating system. First introduced in 2006 in the .NET Framework 3.0, WPF offers many improvements over previous Windows Forms, including the ability to build more user-friendly and dynamic user interfaces. It also provides powerful data linking capabilities and supports multimedia such as images, audio and video.

View More
TECH

August 9, 2024

.NET - Use Analyze Code Coverage for Unit Testing

Code coverage is a percentage measure of your source is tested. As an example, if you have a small application with only 4 conditional branches of code (branch a, branch b, branch c and branch d), one unit test that verifies conditional branch d will report branch code coverage of 25%. 

This article discusses the usage of code coverage for unit testing with Coverlet, a cross platform code coverage library for .NET, with support for line, branch and method coverage.

View More
TECH

August 9, 2024

When should you use SCSS?

First of all, let's find out what SCSS is?
SCSS stands for "Sassy CSS", an extension language of CSS (Cascading Style Sheets).
Overall SCSS provides additional features and improvements over traditional CSS, such as variables, nesting rules, mixins, inheritance, and many other features that make writing and managing CSS code easier and more efficient.

View More
TECH

June 22, 2024

MVP Design Pattern in iOS programming

The MVP (Model-View-Presenter) pattern is a design pattern used to separate the user interface logic from the business logic in applications. MVP helps make code easier to maintain and test. In an iOS application using Objective-C, you can organize the MVP pattern as follows:
Key Components of MVP:

1. Model

Contains the data and business logic of the application.
 
Student.h
@interface Student : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
@end
 
Student.m
@implementation Student
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
self = [super init];
if (self) {
_name = name;
_age = age;
}
return self;
}
 

2. View Protocol

The View protocol will include methods to display data and may have methods to show errors or other statuses.
The presenter communicate to the related view through view protocol
 
StudentViewProtocol.h
 
@protocol StudentViewProtocol <NSObject>
- (void)showStudentName:(NSString *)name;
- (void)showStudentAge:(NSString *)age;
@end

 

3. Presenter

The Presenter communicates with the View via the StudentViewProtocol. Additionally, the Presenter needs a protocol for the View to notify user events.
 
StudentPresenter.h
 
#import "StudentViewProtocol.h"
@protocol StudentPresenterProtocol <NSObject>
- (void)onLoadStudentButtonClicked;
@end
 
@interface StudentPresenter : NSObject <StudentPresenterProtocol>
@property (nonatomic, weak) id<StudentViewProtocol> view;
- (instancetype)initWithView:(id<StudentViewProtocol>)view;
@end
 
StudentPresenter.m
 
#import "StudentPresenter.h"
#import "Student.h"
@implementation StudentPresenter
- (instancetype)initWithView:(id<StudentViewProtocol>)view {
self = [super init];
if (self) {
_view = view;
}
return self;
}
- (void)onLoadStudentButtonClicked {
// In a real application, you might fetch data from a service or database
Student *student = [[Student alloc] initWithName:@"Phuoc Nguyen" age:45];
// Format data before sending to the View
NSString *ageString = [NSString stringWithFormat:@"%ld years old", (long)student.age];
// Update the View
[self.view showStudentName:student.name];
[self.view showStudentAge:ageString];
}
@end

4. View

View contains only view-related logic
In iOS, ViewController acts as the View and is also the place where user events are notified to the Presenter.
StudentViewController.h
#import <UIKit/UIKit.h>
#import "StudentViewProtocol.h"
#import "StudentPresenter.h"
@interface StudentViewController : UIViewController <StudentViewProtocol>
@property (nonatomic, strong)id<StudentPresenterProtocol> presenter;
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@property (weak, nonatomic) IBOutlet UILabel *ageLabel;
@property (weak, nonatomic) IBOutlet UIButton *loadStudentButton;
@end

 

StudentViewController.m
#import "StudentViewController.h"
@implementation StudentViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.presenter = [[StudentPresenter alloc] initWithView:self];
[self.loadStudentButton addTarget:self.presenter action:@selector(onLoadStudentButtonClicked) forControlEvents:UIControlEventTouchUpInside];
}
- (void)showStudentName:(NSString *)name {
self.nameLabel.text = name;
}
- {
self.ageLabel.text = age;
}
@end
 
In this example, the View notifies the Presenter when the "Load Student" button is clicked. The Presenter then processes the request, fetches data from the Model, formatsit, and updates the View. This ensures that all handling logic is separated from the View, making the code more maintainable and testable.
The class diagram:

(The post is used free image from Pexels source)
View More
1 13 14 15 16 17 23