dev-resources.site
for different kinds of informations.
Create RESTAPI of YOLOv5 on Flask deployed on Azure App Service and use in Flutter App
Published at
3/8/2023
Categories
azure
flutter
flask
yolov5
Author
aneeqmalik
Author
10 person written this
aneeqmalik
open
STEPS:
- Creating YOLOV5 Flask App
- Creating Docker Image
- Running Docker Container and Pushing to DockerHub
- Deploying Rest API to Azure for Free.
- Using API in flutter project to perform detections.
Creating YOLOV5 Flask App
Create a flask app with your ML model with requests defined.
"""
Run a rest API exposing the yolov5s object detection model
"""
import argparse
import io
from PIL import Image
import torch
from flask import Flask, request
app = Flask(__name__)
DETECTION_URL = "/v1/object-detection/yolov5"
@app.route(DETECTION_URL, methods=["POST"])
def predict():
if not request.method == "POST":
return
if request.files.get("image"):
image_file = request.files["image"]
image_bytes = image_file.read()
img = Image.open(io.BytesIO(image_bytes))
results = model(img, size=640) # reduce size=320 for faster inference
return results.pandas().xyxy[0].to_json(orient="records")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Flask api exposing yolov5 model")
parser.add_argument("--port", default=5000, type=int, help="port number")
parser.add_argument('--model', default='yolov5s', help='model to run, i.e. --model yolov5s')
args = parser.parse_args()
model = torch.hub.load('ultralytics/yolov5', args.model)
app.run(host="0.0.0.0", port=args.port) # debug=True causes Restarting with stat
you can use a your own model as well with the help of following place the model on the same path as the app.py or flask app file.
model = torch.hub.load('.','custom', path='best.pt',force_reload=True,source='local', pretrained =Flase)
Creating Docker Image:
- First create a DockerFile with the following:
# Use an official Python runtime as a parent image
FROM python:3.8-slim-buster
RUN apt-get update
RUN apt-get install ffmpeg libsm6 libxext6 -y
# Set the working directory to /app
WORKDIR /app
# Copy the current directory contents into the container at /app
COPY . /app
# Install any needed packages specified in requirements.txt
RUN pip install -r requirements.txt
# Make port 5000 available to the world outside this container
EXPOSE 5000
# Run app.py when the container launches
CMD ["python", "app.py", "--port=5000"]
change the app.py if you have different app name
- Run the Following Commands to create an Image and push to the docker hub:
Running Docker Container and Pushing to DockerHub
Run to Ensure it is Working:
Login to Docker Hub:
Create a tag to push to Docker Hub image:
Deploying Rest API to Azure for Free.
- Create a new Web App Service in Azure:
- Login to your Azure account.
- Select "Create a resource" and search for "Web App".
- Select "Web App" from the search results and click "Create".
- Choose a unique name, subscription, resource group, and app service plan.
- Choose "Docker Container" as the Publish option and "Linux" as the Operating System.
- Choose "Docker Hub" as the Registry and enter the name and version of the image you created in step 1.
- Click "Create" to create the Web App Service.
- Wait for Azure to deploy your container to the Web App Service. This may take a few minutes.
Using API in flutter project to perform detections.
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_restapi/ObjectDetectionScreen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: ObjectDetectionScreen(),
);
}
}
ObjectScreen.dart
import 'dart:io';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;
class BoundingBox {
final double xMin;
final double yMin;
final double xMax;
final double yMax;
final double confidence;
final String name;
BoundingBox({
required this.xMin,
required this.yMin,
required this.xMax,
required this.yMax,
required this.confidence,
required this.name,
});
}
class ObjectDetectionScreen extends StatefulWidget {
const ObjectDetectionScreen({super.key});
@override
State<ObjectDetectionScreen> createState() => _ObjectDetectionScreenState();
}
class _ObjectDetectionScreenState extends State<ObjectDetectionScreen> {
late List<BoundingBox> _boundingBoxes = [];
File? _image;
bool _loading = false;
final picker = ImagePicker();
Future<List<BoundingBox>> detectObjects(File image) async {
final url =
"https://flask-restapi-yolov5s.azurewebsites.net/v1/object-detection/yolov5";
final request = await http.MultipartRequest("POST", Uri.parse(url));
request.files.add(await http.MultipartFile.fromPath("image", image.path));
final response = await request.send();
if (response.statusCode == 200) {
final jsonStr = await response.stream.bytesToString();
final jsonResponse = json.decode(jsonStr);
print(jsonResponse);
return List<BoundingBox>.from(jsonResponse.map((bbox) => BoundingBox(
xMin: bbox["xmin"],
yMin: bbox["ymin"],
xMax: bbox["xmax"],
yMax: bbox["ymax"],
confidence: bbox["confidence"],
name: bbox["name"],
)));
} else {
throw Exception('Failed to detect objects');
}
}
Future<void> getImage(ImageSource source) async {
setState(() {
_loading = true;
});
final pickedFile =
await picker.pickImage(source: source, maxWidth: 340, maxHeight: 340);
if (pickedFile != null) {
setState(() {
_image = File(pickedFile.path);
});
final bboxes = await detectObjects(_image!);
setState(() {
_boundingBoxes = bboxes;
_loading = false;
});
} else {
setState(() {
_loading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Object Detection Using Flask Rest Api'),
),
body: Center(
child: SingleChildScrollView(
child: Column(
children: [
_image == null
? const Text('No image selected.')
: Stack(
children: [
Image.file(_image!),
..._boundingBoxes.asMap().entries.map((entry) {
final index = entry.key;
final bbox = entry.value;
final xMin = bbox.xMin;
final yMin = bbox.yMin;
final xMax = bbox.xMax;
final yMax = bbox.yMax;
final confidence = bbox.confidence;
final name = bbox.name;
final left = xMin;
final top = yMin;
final width = xMax - xMin;
final height = yMax - yMin;
Color color;
if (index % 3 == 0) {
color = Colors.green;
} else if (index % 3 == 1) {
color = Colors.yellow;
} else {
color = Colors.red;
}
return Positioned(
left: left,
top: top,
width: width,
height: height,
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: color,
width: 2.0,
),
borderRadius: BorderRadius.circular(20),
),
child: Text(
"$name ${(confidence * 100).toStringAsFixed(0)}%",
textAlign: TextAlign.center,
style: TextStyle(
color: color,
fontSize: 12.0,
fontWeight: FontWeight.bold,
shadows: const [
Shadow(
color: Colors.black,
offset: Offset(1, 1),
blurRadius: 2,
)
],
),
),
),
);
}).toList(),
],
),
ElevatedButton(
onPressed: () => getImage(ImageSource.camera),
child: const Text("Take a Picture"),
),
ElevatedButton(
onPressed: () => getImage(ImageSource.gallery),
child: const Text("Choose from Gallery"),
),
const SizedBox(height: 10),
_loading
? const CircularProgressIndicator(
color: Colors.blue,
)
: const SizedBox(),
],
),
),
));
}
}
BoundingBox.dart
import 'dart:ui';
import 'package:flutter/material.dart';
class BoxPainter extends CustomPainter {
final dynamic predictions;
BoxPainter(this.predictions);
@override
void paint(Canvas canvas, Size size) {
final width = size.width;
final height = size.height;
final colors = [
Colors.red,
Colors.green,
Colors.blue,
Colors.yellow,
Colors.orange,
Colors.purple,
Colors.pink,
];
if (predictions != null) {
for (var i = 0; i < predictions.length; i++) {
final prediction = predictions[i];
final bbox = prediction['bbox'];
final left = bbox['xmin'].toDouble();
final top = bbox['ymin'].toDouble();
final right = bbox['xmax'].toDouble();
final bottom = bbox['ymax'].toDouble();
final rect = Rect.fromLTWH(
left / 640 * width,
top / 640 * height,
(right - left) / 640 * width,
(bottom - top) / 640 * height,
);
final paint = Paint()
..color = colors[i % colors.length]
..style = PaintingStyle.stroke
..strokeWidth = 2.0;
final labelPaint = Paint()
..color = colors[i % colors.length]
..style = PaintingStyle.fill
..strokeWidth = 2.0;
canvas.drawRect(rect, paint);
final label = '${prediction['name']} (${prediction['confidence']})';
final labelOffset = Offset(
left / 640 * width,
top / 480 * height - 20,
);
canvas.drawRect(
Rect.fromPoints(
labelOffset,
Offset(
labelOffset.dx + label.length * 8,
labelOffset.dy + 20,
),
),
labelPaint,
);
final textStyle = TextStyle(
color: Colors.white,
fontSize: 14.0,
);
final textSpan = TextSpan(
text: label,
style: textStyle,
);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
);
textPainter.layout(
minWidth: 0,
maxWidth: size.width,
);
textPainter.paint(
canvas,
Offset(
labelOffset.dx + 4,
labelOffset.dy + 2,
),
);
}
}
}
@override
bool shouldRepaint(BoxPainter oldDelegate) => false;
}
Compile and debug the project.
App is Running 🎉🎉🎉
Resource and Links:
yolov5 Article's
5 articles in total
Crowded Counting in Station
read article
YOLO V5 running on an Android phone Part 1(train model)
read article
Create RESTAPI of YOLOv5 on Flask deployed on Azure App Service and use in Flutter App
currently reading
Flutter Object Detection App + YOLOV5 Model.
read article
YoloV5
read article
Featured ones: