Hackster is hosting Hackster Holidays, Ep. 5: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Friday!Stream Hackster Holidays, Ep. 5 on Friday!
Elmin Delibašić
Published © GPL3+

CV-Park Vision: Your Intelligent Parking Companion

Python + OpenCV + Flask + Roboflow + Raspbian + BalenaFinV1.0 + PostgreSQL + Web Application (Vue. js + Node. js) + Android Studio + MapBox

AdvancedFull instructions providedOver 12 days657

Things used in this project

Hardware components

Compute Module 3
Raspberry Pi Compute Module 3
A component that attaches to the BalenaFin boad.
×1
balenaFin
balenaFin
Used to running Python script. It can be seen as an imaginary remote point in a parking lot that sends information to the database and video feeds to Web application.
×1

Software apps and online services

OpenCV
OpenCV
Used as a built-in part of Roboflow, and in Python code as a library for image processing, tagging cars, adding text and preparing the image format for processing and sending.
Raspbian
Raspberry Pi Raspbian
Operation system on BalenaFin board
Python
Used to load the model, mark the cars, count them, update database with new information about the parking situation and send video feeds to Web Application.
Vue.js
Frontend part of Web Application
Node.js
Backend part of Web Application
Android Studio
Android Studio
Used to write code and testing an Android application.
MapBox
It is used in the Web Application to display parking lots in a city with information in the form of popups. In the Android application, it is used to show the user the location of the parking lot.
PostgreSQL
Used as the main database in which information about the parking lots connected to the system is saved.
Google Maps
Google Maps
Used to navigate the user to the desired parking lot.
Roboflow
Used to training model for detecting cars on image/video

Story

Read more

Schematics

schema1_1_1VeeDlgcjv.png

Code

main.py

Python
# Import necessary libraries and modules
from inference.models.utils import get_roboflow_model
import cv2
import numpy as np
from flask import Flask, Response
import psycopg2
from util import update_parkings_table

# Create a Flask web application
app = Flask(__name__)

# Define parking location coordinates and total parking spaces
parking_latitude = "44.12897164875866"
parking_longitude = "18.117705446176494"
total_space = 69

# Specify the path to the video file
# Video: https://www.pexels.com/video/aerial-footage-of-a-parking-lot-5587732/
# I made changes to the video so that it could be used (cropped, replayed several times to make it last longer)
video_path = "video1.mp4"

# Roboflow model configuration
model_name = "model_name"
model_version = "model_version"

# Get the Roboflow model using the specified configuration
model = get_roboflow_model(
    model_id="{}/{}".format(model_name, model_version),
    # Replace ROBOFLOW_API_KEY with your Roboflow API Key
    api_key="ROBOFLOW_API_KEY"
)

# Open the video file
cap = cv2.VideoCapture(video_path)

# Check if the video file opened successfully
if not cap.isOpened():
    print("Error: Couldn't open video.")
    exit()

# Define a function to generate video frames
def generate_frames():
    while True:
        # Read a frame from the video
        ret, frame = cap.read()

        # Break the loop if the end of the video is reached
        if not ret:
            break

        # Perform car detection using the Roboflow model
        results = model.infer(image=frame, confidence=0.85, iou_threshold=0.9)

        # Get the total number of detected cars
        total_cars = len(results[0])
        free_space = total_space - total_cars

        # Draw bounding boxes around detected cars
        for car_result in results[0]:
            bounding_box = car_result[:4]
            x0, y0, x1, y1 = map(int, bounding_box)
            cv2.rectangle(frame, (x0, y0), (x1, y1), (0, 0, 255), 2)

        # Display the total number of detected cars on the frame
        text_size = cv2.getTextSize(f"Free Space: {free_space}/{total_space}", cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2)[0]
        cv2.rectangle(frame, (10, 10), (15 + text_size[0], 40), (0, 0, 0), -1)
        cv2.putText(frame, f"Free Space: {free_space}/{total_space}", (15, 35), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)

        # Convert the frame to JPEG format
        _, buffer = cv2.imencode('.jpg', frame)
        frame_bytes = buffer.tobytes()

        # Update the parking table with the total number of detected cars
        update_parkings_table(parking_latitude, parking_longitude, total_space, free_space)

        # Use yield to send the frame via HTTP response
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n')

# Define a route for the video feed
@app.route('/')
def video_feed():
    return Response(generate_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')

# Run the Flask application
if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8000, debug=True)

util.py

Python
from datetime import datetime
import psycopg2  # Import library for working with the PostgreSQL database

# Information about the PostgreSQL database
dbname = "smartparking"
user = "postgres"
password = "postgres"
host = "ip_address_of_posgreSQL_database"
port = "5432"

# Establish a connection to the PostgreSQL database
conn = psycopg2.connect(
    dbname=dbname,
    user=user,
    password=password,
    host=host,
    port=port
)

# Create a cursor to interact with the database
cur = conn.cursor()

# Function to retrieve current information about available parking spaces
def get_parking_status(parkingLatitude, parkingLongitude):
    # SQL query to select the FREE_SPACE column based on latitude and longitude
    queryGet = "SELECT FREE_SPACE FROM PARKINGS WHERE LATITUDE = %s and LONGITUDE = %s;"
    
    # Execute the query with the provided parameters
    cur.execute(queryGet, (parkingLatitude, parkingLongitude))
    
    # Fetch the result and get the value from the first row
    result = cur.fetchall()
    resultRow = result[0]
    
    return resultRow[0]

# Function to update the PARKINGS table with new parking space information if it has changed
def update_parkings_table(parkingLatitude, parkingLongitude, totalSpace, freeSpace):
    # SQL query to update the TOTAL_SPACE, FREE_SPACE, and DATE_UPDATE columns
    queryUpdate = "UPDATE PARKINGS SET TOTAL_SPACE = %s, FREE_SPACE = %s, DATE_UPDATE = %s WHERE LATITUDE = %s and LONGITUDE = %s;"

    try:
        # Get the current free space from the table
        freeSpaceFromTable = get_parking_status(parkingLatitude, parkingLongitude)

        # Check if the free space has changed
        if freeSpaceFromTable != freeSpace:
            # Get the current date and time
            dateUpdateNow = datetime.now()
            dateUpdate = dateUpdateNow.strftime("%d-%m-%Y %H:%M:%S")

            # Execute the update query with the new values
            cur.execute(queryUpdate, (totalSpace, freeSpace, dateUpdate, parkingLatitude, parkingLongitude))
            
            # Commit the changes to the database
            conn.commit()
            
            print(str(dateUpdate) + " Free parking space " + str(freeSpace) + "/" + str(totalSpace) + " update in table successfully!")
            
            conn.close()

    except Exception as e:
        print(f"Error: Update table failed! {e}")

# Note: It's good practice to handle exceptions more precisely in a production environment

testModel.py

Python
from inference.models.utils import get_roboflow_model
import cv2

# Image path
image_path = "park.png"

# Roboflow model
model_name = "model_name"
model_version = "model_verion"

# Get Roboflow parking detection model
model = get_roboflow_model(
    model_id="{}/{}".format(model_name, model_version),
    # Replace ROBOFLOW_API_KEY with your Roboflow API Key
    api_key="api_key"
)

# Load image with opencv
frame = cv2.imread(image_path)

# Inference image to find cars
results = model.infer(image=frame, confidence=0.3, iou_threshold=0.5)

# Count the total number of cars
total_cars = len(results[0])

# Iterate through all detected cars
for car_result in results[0]:
    bounding_box = car_result[:4]
    print(bounding_box)

    x0, y0, x1, y1 = map(int, bounding_box)

    # Draw bounding box
    cv2.rectangle(frame, (x0, y0), (x1, y1), (0, 0, 255), 2)

# Display the total number of cars in the top-left corner
text_size = cv2.getTextSize(f"Total Cars: {total_cars}", cv2.FONT_HERSHEY_SIMPLEX, 0.7, 2)[0] 
cv2.rectangle(frame, (10, 10), (15 + text_size[0], 40), (0, 0, 0), -1) 
cv2.putText(frame, f"Total Cars: {total_cars}", (15, 35), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)  


# Show images
cv2.imshow('Original Frame', cv2.imread(image_path))
cv2.imshow('Result Frame', frame)
cv2.waitKey(0)
cv2.destroyAllWindows()

Credits

Elmin Delibašić

Elmin Delibašić

12 projects • 81 followers
Bachelor of Electrical Engineering || Enthusiast for AI and IoT || Master Student at Faculty of Electrical Engineering

Comments