Skip to content

Commit

Permalink
Reorganazing Repo and adding fastapi + stremlit
Browse files Browse the repository at this point in the history
  • Loading branch information
Ubuntu authored and Ubuntu committed Dec 26, 2021
1 parent c350dae commit dc70e2e
Show file tree
Hide file tree
Showing 16 changed files with 254 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.pth filter=lfs diff=lfs merge=lfs -text
File renamed without changes.
File renamed without changes.
Binary file added docs/static/DatasetFig.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/FasterRCNNARCH.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/PlateDetectorModelArch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/PlateOcrModelArch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/PostProFig.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions fastapi/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from fastapi import FastAPI , File
from fastapi.responses import ORJSONResponse
from plate_detector import PlateDetector
from plate_ocr import PlateOCR
import io
import os, json, cv2, uuid , statistics
from PIL import Image
import numpy as np

#model = get_segmentator()
plate_model = PlateDetector()
ocr_model = PlateOCR()

app = FastAPI(title="MoroccoAI Data Challenge",
description='''Automatic Number Plate Recognition (ANPR) in Morocco Licensed Vehicles.''',
version="0.1.0",
)


@app.post("/platedetector", response_class=ORJSONResponse)
def get_plate_detection(file: bytes = File(...)):
image = cv2.imread(io.BytesIO(file))
image = image[:,:,::-1].copy()
output = plate_model.predict(image)
plate_boxes = plate_model.plateBoxes(output)
output_image = plate_model.detectedPlateSaver(image, output)

return [{"plate_boxes": plate_boxes, "output_image": output_image}]

"""
@app.post("/plateocr", response_class=ORJSONResponse)
def get_plate_ocr(file: bytes = File(...)):
image = cv2.imread(io.BytesIO(file))
image = image[:,:,::-1].copy()
plate_boxes = get_plate_detection(file)
output = plate_model.predict(image)
plate_boxes = plate_model.plateBoxes(output)
output_image = plate_model.detectedPlateSaver(image, output)
return [{"plate_boxes": plate_boxes, "output_image": output_image}]
"""
68 changes: 68 additions & 0 deletions fastapi/plate_detector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Dec 26 02:27:36 2021
@author: Safoine EL KHABICH "TTM" <https://www.linkedin.com/in/safoinme/>
"""
import detectron2
from detectron2.engine import DefaultTrainer
from detectron2.config import get_cfg
from detectron2 import model_zoo
from detectron2.engine import DefaultPredictor
from detectron2.config import get_cfg
from detectron2.utils.visualizer import Visualizer
from detectron2.data import MetadataCatalog, DatasetCatalog
from detectron2.structures import BoxMode
from detectron2.utils.visualizer import ColorMode
import os, json, cv2, uuid

class PlateDetector:

def __init__(self):
#create a predictor
self._cfg = get_cfg()
self._predictor = self._makePredictor()
self._class = MetadataCatalog.get("licences").set(thing_classes=["licence"])

"""
This method initalizes the model and configuration
to return the predictor
"""
def _makePredictor(self):
self._cfg
self._cfg.MODEL.DEVICE = "cpu"
self._cfg.merge_from_file(model_zoo.get_config_file("COCO-Detection/faster_rcnn_R_50_FPN_3x.yaml"))
self._cfg.SOLVER.IMS_PER_BATCH = 2
self._cfg.MODEL.ROI_HEADS.BATCH_SIZE_PER_IMAGE = 128 # faster, and good enough for this toy dataset (default: 512)
self._cfg.MODEL.ROI_HEADS.NUM_CLASSES = 1
self._cfg.MODEL.WEIGHTS = os.path.join("../weights/plate_detector", "model_final.pth") # path to the model we just trained
self._cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.9
return DefaultPredictor(self._cfg)

"""
This method takes an opencv image and perfroms instance segmentation
"""
def predict(self, image):
return self._predictor(image)
"""
This method takes the prediction result and return boxes and scores
"""
def plateExtractor(self, output):
boxes = output['instances'].pred_boxes.tensor.cpu().numpy().tolist()
scores = output['instances'].scores.numpy().tolist()
if len(scores)>0:
Plates = { "Licence Plate "+str(i) : {"score" : scores[i], "boxes" : boxes[i]} for i in range(0, len(scores) ) }
return (Plates)
"""
This method takes the image & the prediction result and return image with boxes of plates
"""
def detectedImage(self, image, output):
visual = Visualizer(image[:, :, ::-1],
metadata=self._class,
scale=0.5,

)
visual_output = visual.draw_instance_predictions(output["instances"])
output_image = os.path.join("./images/plate_detector", str(uuid.uuid4())+".jpg")
cv2.imwrite(output_image,visual_output.get_image()[:, :, ::-1])
return (output_image)
105 changes: 105 additions & 0 deletions fastapi/plate_ocr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Dec 26 17:17:44 2021
@author: Safoine EL KHABICH "TTM" <https://www.linkedin.com/in/safoinme/>
"""
import detectron2
from detectron2.engine import DefaultTrainer
from detectron2.config import get_cfg
from detectron2 import model_zoo
from detectron2.engine import DefaultPredictor
from detectron2.config import get_cfg
from detectron2.utils.visualizer import Visualizer
from detectron2.data import MetadataCatalog, DatasetCatalog
from detectron2.structures import BoxMode
from detectron2.utils.visualizer import ColorMode
import os, json, cv2, uuid , statistics

class PlateOCR:

def __init__(self):
#create a predictor
self._cfg = get_cfg()
self._predictor = self._makePredictor()
self._characters = ["0","1","2","3","4","5","6","7","8","9", "a","b","h","w","d","p","waw","j","m","m"]
self._class = MetadataCatalog.get("characters").set(thing_classes=self._characters)

"""
This method initalizes the model and configuration
to return the predictor
"""
def _makePredictor(self):
self._cfg
self._cfg.MODEL.DEVICE = "cpu"
self._cfg.merge_from_file(model_zoo.get_config_file("COCO-Detection/faster_rcnn_R_50_FPN_3x.yaml"))
self._cfg.SOLVER.IMS_PER_BATCH = 2
self._cfg.MODEL.ROI_HEADS.BATCH_SIZE_PER_IMAGE = 512 # faster, and good enough for this toy dataset (default: 512)
self._cfg.MODEL.ROI_HEADS.NUM_CLASSES = 20
self._cfg.MODEL.WEIGHTS = os.path.join("../weights/plate_ocr", "model_final.pth") # path to the model we just trained
self._cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.8
return DefaultPredictor(self._cfg)

"""
This method takes an opencv image and perfroms instance segmentation
"""
def predict(self, image):
return self._predictor(image)
"""
This method Loads plates images only from original images using plateExtractor to extract the exact plate
"""
def plateLoader(self, image, plates):
plateImages = []
for plate in plates.values():
boxes = plate['boxes']
plateImages.append(image[int(boxes[1]):int(boxes[3]), int(boxes[0]):int(boxes[2]), :])
return (plateImages)
"""
This method takes the prediction result and return boxes and scores
"""
def ocrExtractor(self, output):
boxes = output['instances'].pred_boxes.tensor.cpu().numpy().tolist()
scores = output['instances'].scores.numpy().tolist()
classes = output['instances'].pred_classes.to('cpu').tolist()
if len(scores)>0:
characters = { i : {"character": self._characters[classes[i]], "score" : scores[i], "boxes" : boxes[i]} for i in range(0, len(scores) ) }
return (characters)
"""
This method takes the image & the prediction result and return image with boxes of plates
"""
def detectedImagesaver(self, image, output):
visual = Visualizer(image[:, :, ::-1],
metadata=self._class,
scale=0.5,

)
visual_output = visual.draw_instance_predictions(output["instances"])
output_image = os.path.join("./images/plate_ocr", str(uuid.uuid4())+".jpg")
cv2.imwrite(output_image,visual_output.get_image()[:, :, ::-1])
return (output_image)
"""
This method post-process the prediction output in order to return string of plates in the right order respect Moroccan standards
"""
def postProcess(self, image, output):
if len(output.keys())<=0:
plate_ocr_string = {'plate':image[:-4],'plate_string':''}
else :
y_mins = []
for character in list(output.items()):
y_mins.append(character[1]['boxes'][1])
median_y_mins = statistics.median(y_mins)
top_characters = dict()
bottom_characters = dict()
for key,value in output.items():
if (value['boxes'][3] <= median_y_mins ):
top_characters[key] = value
else :
bottom_characters[key] = value
sorted_top_characters = sorted(top_characters.items(), key=lambda e: e[1]['boxes'][0])
sorted_bottom_characters = sorted(bottom_characters.items(), key=lambda e: e[1]['boxes'][0])
top_plate_ocr = [item[1]['character'] for item in sorted_top_characters]
bottom_plate_ocr = [item[1]['character'] for item in sorted_bottom_characters]
plate_ocr = bottom_plate_ocr+top_plate_ocr
plate_ocr = "".join(str(x) for x in plate_ocr)
plate_ocr_string = {'plate':image[:-4],'plate_string':plate_ocr}
return(plate_ocr_string)
39 changes: 39 additions & 0 deletions streamlit/streamlit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import streamlit as st
from requests_toolbelt.multipart.encoder import MultipartEncoder
import requests
from PIL import Image
import io
import zipfile
import glob
import os


st.title('MoroccoAI Data Challenge : Automatic Number Plate Recognition (ANPR) in Morocco Licensed Vehicles.')

# fastapi endpoint
url = 'http://0.0.0.0:8000'
endpoint = '/platedetector'

st.write('''This application is a demo result of our work in the comepetiton organized by MoroccoAI in the context of the first MoroccoAI Data Challenge.
it takes an image that contains one or multiple cars and return the plates and the recognized characters on each plate''') # description and instructions

image = st.file_uploader('insert image') # image upload widget


def process(image, server_url: str):

m = MultipartEncoder(
fields={'file': ('filename', image, 'image/jpeg')}
)

r = requests.post(server_url,
data=m,
headers={'Content-Type': m.content_type},
timeout=8000)

return r


if st.button('Get segmentation map'):
res = process(image, url+endpoint)
st.text(res)

0 comments on commit dc70e2e

Please sign in to comment.