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 🎉🎉🎉
Top comments (0)