-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6076806
commit 6748a85
Showing
6 changed files
with
356 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
DROP DATABASE car_park_management; | ||
|
||
-- Create the Database | ||
CREATE DATABASE IF NOT EXISTS car_park_management; | ||
USE car_park_management; | ||
|
||
-- Create Tables | ||
CREATE TABLE IF NOT EXISTS cars ( | ||
car_plate VARCHAR(10) PRIMARY KEY, | ||
is_staff BOOLEAN DEFAULT FALSE | ||
); | ||
|
||
CREATE TABLE IF NOT EXISTS car_entries ( | ||
id INT AUTO_INCREMENT PRIMARY KEY, | ||
car_plate VARCHAR(10), | ||
entry_time DATETIME NOT NULL, | ||
exit_time DATETIME, | ||
FOREIGN KEY (car_plate) REFERENCES cars(car_plate) | ||
); | ||
|
||
CREATE TABLE IF NOT EXISTS charges ( | ||
id INT AUTO_INCREMENT PRIMARY KEY, | ||
car_plate VARCHAR(10), | ||
entry_time DATETIME NOT NULL, | ||
exit_time DATETIME NOT NULL, | ||
amount DECIMAL(10, 2) NOT NULL, | ||
FOREIGN KEY (car_plate) REFERENCES cars(car_plate) | ||
); | ||
|
||
-- Insert Sample Data for Staff Cars | ||
INSERT INTO cars (car_plate, is_staff) VALUES | ||
('34ABC123', TRUE), | ||
('34XYZ789', TRUE); | ||
|
||
DROP PROCEDURE IF EXISTS log_entry; | ||
DROP PROCEDURE IF EXISTS log_exit; | ||
|
||
-- Procedures for Logging Entries and Exits and Calculating Charges | ||
DELIMITER // | ||
|
||
CREATE PROCEDURE log_entry(NEW_car_plate VARCHAR(10)) | ||
BEGIN | ||
IF (SELECT COUNT(*) FROM cars WHERE car_plate = NEW_car_plate) = 0 THEN | ||
INSERT INTO cars (car_plate) VALUES (NEW_car_plate); | ||
END IF; | ||
INSERT INTO car_entries (car_plate, entry_time) VALUES (NEW_car_plate, NOW()); | ||
END // | ||
|
||
CREATE PROCEDURE log_exit(NEW_car_plate VARCHAR(10)) | ||
BEGIN | ||
DECLARE entry_time DATETIME; | ||
DECLARE is_staff BOOLEAN; | ||
DECLARE total_hours INT; | ||
DECLARE charge DECIMAL(10, 2); | ||
|
||
SELECT entry_time INTO entry_time | ||
FROM car_entries | ||
WHERE car_plate = NEW_car_plate AND exit_time IS NULL | ||
ORDER BY entry_time DESC LIMIT 1; | ||
|
||
UPDATE car_entries | ||
SET exit_time = NOW() | ||
WHERE car_plate = NEW_car_plate AND exit_time IS NULL; | ||
|
||
SELECT is_staff INTO is_staff | ||
FROM cars | ||
WHERE car_plate = NEW_car_plate; | ||
|
||
IF is_staff = FALSE THEN | ||
SET total_hours = TIMESTAMPDIFF(HOUR, entry_time, NOW()); | ||
IF total_hours = 0 THEN | ||
SET charge = 50; | ||
ELSE | ||
SET charge = 50 + (total_hours * 20); | ||
END IF; | ||
INSERT INTO charges (car_plate, entry_time, exit_time, amount) | ||
VALUES (NEW_car_plate, entry_time, NOW(), charge); | ||
END IF; | ||
END // | ||
|
||
|
||
DELIMITER ; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
import requests | ||
from PIL import Image | ||
import pytesseract | ||
import cv2 | ||
import numpy as np | ||
import os | ||
import re | ||
from dotenv import load_dotenv | ||
import mysql.connector | ||
from mysql.connector import Error | ||
|
||
load_dotenv() # Loads variables from .env file | ||
|
||
# Roboflow API details | ||
API_KEY = os.getenv('API_KEY') | ||
PROJECT_ID = os.getenv('PROJECT_ID') | ||
VERSION = os.getenv('VERSION') | ||
|
||
def detect_car_plate(image_path): | ||
|
||
# Detects car plate in an image using Roboflow API. | ||
|
||
try: | ||
with open(image_path, 'rb') as image_file: | ||
response = requests.post( | ||
f"https://detect.roboflow.com/{PROJECT_ID}/{VERSION}", | ||
params={"api_key": API_KEY}, | ||
files={"file": image_file} | ||
) | ||
response.raise_for_status() # Raise an HTTPError if the response was unsuccessful | ||
response_json = response.json() | ||
return response_json | ||
except requests.exceptions.RequestException as e: | ||
print(f"Error during API request: {e}") | ||
return None | ||
|
||
def extract_plate_info(predictions): | ||
|
||
# Extract bounding box information from predictions. | ||
|
||
if not predictions: | ||
print("No predictions found in the response.") | ||
return None | ||
|
||
for prediction in predictions: | ||
x, y, width, height = (prediction['x'], prediction['y'], prediction['width'], prediction['height']) | ||
print(f"Bounding Box - x: {x}, y: {y}, width: {width}, height: {height}") | ||
|
||
# Adjusting the bounding box slightly to ensure the plate is fully captured | ||
padding_x = 10 # Slight horizontal padding | ||
padding_y = 5 # Slight vertical padding | ||
|
||
x_min = int(x - width / 2) - padding_x | ||
y_min = int(y - height / 2) - padding_y | ||
x_max = int(x + width / 2) + padding_x | ||
y_max = int(y + height / 2) + padding_y | ||
|
||
return (x_min, y_min, x_max, y_max) | ||
|
||
return None | ||
|
||
def preprocess_image(image): | ||
|
||
# Preprocess the image to enhance OCR accuracy. | ||
|
||
try: | ||
gray_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2GRAY) | ||
_, thresh_image = cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) | ||
return thresh_image | ||
except Exception as e: | ||
print(f"Error during image preprocessing: {e}") | ||
return None | ||
|
||
def crop_image(image_path, bounding_box): | ||
|
||
# Crop the detected car plate area from the image. | ||
|
||
try: | ||
image = Image.open(image_path) | ||
cropped_image = image.crop(bounding_box) | ||
cropped_image.show() | ||
return cropped_image | ||
except Exception as e: | ||
print(f"Error cropping image: {e}") | ||
return None | ||
|
||
def extract_plate_number(cropped_image): | ||
|
||
# Apply OCR to extract the car plate number from the cropped image. | ||
|
||
try: | ||
preprocessed_image = preprocess_image(cropped_image) | ||
plate_number = pytesseract.image_to_string(preprocessed_image, config='--psm 7') | ||
plate_number = plate_number.strip() | ||
|
||
# Remove any non-alphanumeric characters (except spaces) | ||
plate_number = ''.join(char if char.isalnum() or char.isspace() else '' for char in plate_number) | ||
|
||
# Use regex to match the extracted plate number to one of the known Turkish plate patterns | ||
plate_pattern = re.compile(r''' | ||
^(\d{2})\s?([A-Z]{1,3})\s?(\d{2,5})$ # Matches all Turkish plate formats | ||
''', re.VERBOSE) | ||
|
||
match = plate_pattern.search(plate_number) | ||
if match: | ||
# Format the plate number consistently | ||
formatted_plate = f"{match.group(1)} {match.group(2)} {match.group(3)}" | ||
return formatted_plate | ||
else: | ||
# Attempt to fix common OCR errors | ||
if len(plate_number) > 2 and not plate_number[:2].isdigit(): | ||
plate_number = plate_number[1:] # Remove the first character if it's incorrect | ||
|
||
match = plate_pattern.search(plate_number) | ||
if match: | ||
formatted_plate = f"{match.group(1)} {match.group(2)} {match.group(3)}" | ||
return formatted_plate | ||
else: | ||
print("No valid plate pattern detected. Returning the adjusted OCR output.") | ||
return plate_number | ||
except Exception as e: | ||
print(f"Error during OCR extraction: {e}") | ||
return None | ||
|
||
def create_connection(): | ||
|
||
# Establish a connection to the MySQL database. | ||
|
||
try: | ||
connection = mysql.connector.connect( | ||
host = os.getenv('HOST'), | ||
database = os.getenv('DATABASE'), | ||
user = os.getenv('ROOT'), | ||
password = os.getenv('PASSWORD'), | ||
port = os.getenv('PORT') | ||
) | ||
if connection.is_connected(): | ||
print("Connected to MySQL database") | ||
return connection | ||
except Error as e: | ||
print(f"Error connecting to MySQL: {e}") | ||
return None | ||
|
||
def log_car_entry(connection, plate_number): | ||
|
||
# Log the car entry into the database. | ||
|
||
try: | ||
cursor = connection.cursor() | ||
cursor.callproc('log_entry', [plate_number]) | ||
connection.commit() | ||
print(f"Logged entry for plate number: {plate_number}") | ||
except Error as e: | ||
print(f"Error logging car entry: {e}") | ||
|
||
def log_car_exit(connection, plate_number): | ||
|
||
# Log the car exit into the database. | ||
|
||
try: | ||
cursor = connection.cursor() | ||
cursor.callproc('log_exit', [plate_number]) | ||
connection.commit() | ||
print(f"Logged exit for plate number: {plate_number}") | ||
except Error as e: | ||
print(f"Error logging car exit: {e}") | ||
|
||
def main(image_path): | ||
|
||
# Main function to process the image and log the car entry or exit. | ||
|
||
response_json = detect_car_plate(image_path) | ||
if response_json and 'predictions' in response_json: | ||
bounding_box = extract_plate_info(response_json['predictions']) | ||
if bounding_box: | ||
cropped_image = crop_image(image_path, bounding_box) | ||
if cropped_image: | ||
plate_number = extract_plate_number(cropped_image) | ||
if plate_number: | ||
print(f"Extracted Plate Number: {plate_number}") | ||
|
||
# Establish database connection | ||
connection = create_connection() | ||
if connection: | ||
cursor = connection.cursor() | ||
|
||
# Check if the plate number already exists in car_entries with null exit_time | ||
cursor.execute("SELECT COUNT(*) FROM car_entries WHERE car_plate = %s AND exit_time IS NULL", (plate_number,)) | ||
count = cursor.fetchone()[0] | ||
|
||
if count > 0: | ||
# If exit log | ||
log_car_exit(connection, plate_number) | ||
else: | ||
# If entry log | ||
log_car_entry(connection, plate_number) | ||
|
||
connection.close() | ||
else: | ||
print("OCR failed to extract a plate number. The image might be too blurry or the plate might not be clearly visible.") | ||
else: | ||
print("Failed to crop the image. The bounding box might be incorrect.") | ||
else: | ||
print("No license plate detected in the image.") | ||
else: | ||
print("No predictions or invalid response from the API.") | ||
|
||
|
||
if __name__ == "__main__": | ||
IMAGE_PATH = "test3.jpg" | ||
main(IMAGE_PATH) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import serial | ||
import requests | ||
|
||
# Serial setup | ||
ser = serial.Serial('COM9', 9600) # Replace with your correct COM port | ||
|
||
# PHP script URL | ||
url = "http://localhost/insert_data.php" # Change to your actual URL | ||
|
||
while True: | ||
# Read data from serial port | ||
data = ser.readline().decode('utf-8').strip() | ||
print(f"Received: {data}") | ||
|
||
# Parse spot name, distance, and parking status from the Arduino serial output | ||
if "Spot Name" in data and "Distance" in data and "Parked" in data: | ||
# Extract the values from the received data | ||
parts = data.split(", ") | ||
spot_name = parts[0].split(": ")[1] # Extract spot name | ||
distance = parts[1].split(": ")[1].split(" ")[0] # Extract distance value | ||
is_parked = parts[2].split(": ")[1] # Extract parked status (0 or 1) | ||
|
||
# Send data to PHP script | ||
payload = {'spot_name': spot_name, 'distance': distance, 'is_parked': is_parked} | ||
response = requests.get(url, params=payload) | ||
|
||
# Print response from PHP script | ||
print(response.text) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?php | ||
$servername = "localhost"; | ||
$username = "root"; // MySQL kullanıcı adı | ||
$password = ""; // MySQL şifresi | ||
$dbname = "park_database"; // Veritabanı adı | ||
|
||
// Create connection | ||
$conn = new mysqli($servername, $username, $password, $dbname); | ||
|
||
// Check connection | ||
if ($conn->connect_error) { | ||
die("Connection failed: " . $conn->connect_error); | ||
} | ||
|
||
// Get data from the GET request | ||
if (isset($_GET['spot_name']) && isset($_GET['distance']) && isset($_GET['is_parked'])) { | ||
$spot_name = $_GET['spot_name']; | ||
$distance = $_GET['distance']; | ||
$isParked = $_GET['is_parked']; | ||
|
||
// Insert data into MySQL database | ||
$sql = "INSERT INTO parking_data (spot_name, distance, is_parked) VALUES ('$spot_name', '$distance', '$isParked')"; | ||
|
||
if ($conn->query($sql) === TRUE) { | ||
echo "New record created successfully"; | ||
} else { | ||
echo "Error: " . $sql . "<br>" . $conn->error; | ||
} | ||
} else { | ||
echo "Data not received!"; | ||
} | ||
|
||
$conn->close(); | ||
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
API_KEY = os.getenv('API_KEY') |