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)