<reponame>skyl4rk/carspeed.py<gh_stars>0
from picamera.array import PiRGBArray
from picamera import PiCamera
import time
import math
import datetime
from suntime import Sun
import os
import cv2
import mysql.connector
import mimetypes
import smtplib


# User modifiable variables:
DISTANCE = 76  # Enter your distance-to-road value here
SPEED_LIMIT = 25  # Enter the speed limit of the road being monitored here
MIN_SPEED = 45  # Enter the minimum speed for saving images
VERTICAL_FLIP = True  # If image is upside down, change this to True or False
HORIZONTAL_FLIP = True  # If image is reversed side to side, change this to True or False
EMAIL_SPEED = 50  # Enter the minimum speed for sending an email with attached image
SAVE_CSV = True  # Turn saving to csv file on and off
SAVE_SQL = False  # Turn saving to mysql database on and off
SEND_EMAIL = False # Turn email function on and off
EMAIL_SENDER = "<EMAIL>" # Email user account used with email server
EMAIL_RECIPIENT ="<EMAIL>" # Email address to which messages will be sent
EMAIL_SERVER ='youremail.com' # SMTP email server url
EMAIL_PASSWORD = "<PASSWORD>" # Email user account password on email server
EMAIL_SUBJECT = 'Car Speed Detector Report' # Email subject bar, edit this as desired
EMAIL_BODY = "The attached image is of a vehicle recorded at excess of 20 mph over the speed limit on Elm Street at Bluebird Street in Springfield by the Car Speed Detector."
# Email body is the message of the email which will also include the attached image of the vehicle.
# Enter mysql database connection data below
MYSQL_HOST = 'localhost'
MYSQL_USER = 'yourmysqlusername'
MYSQL_PASSWORD = '<PASSWORD>'
MYSQL_DATABASE = 'yourmysqldatabase' # You will have to set up a mariadb or mysql database
PARENT_DIRECTORY = "/home/pi/Python/Projects/CarSpeedDetector/" # The directory in which this script is located.
THRESHOLD = 15 # Modify the action of the Thresh function
MIN_AREA = 175
BLURSIZE = (15,15)  # Modify the Gaussian Blur function
IMAGEWIDTH = 640  # Modify the image output width
IMAGEHEIGHT = 480  # Modify the image output height
RESOLUTION = [IMAGEWIDTH,IMAGEHEIGHT]
FOV = 53.5  # Field of view
FPS = 30
SHOW_BOUNDS = True
SHOW_IMAGE = True
LATITUDE = 40.4  # Enter your approximate latitude here, used to determine sunrise and set times
LONGITUDE = -87.2  # Enter your approximate longitude here

# the following enumerated values are used to make the program more readable
WAITING = 0
TRACKING = 1
SAVING = 2
UNKNOWN = 0
LEFT_TO_RIGHT = 1
RIGHT_TO_LEFT = 2

# place a prompt on the displayed image
def prompt_on_image(txt):
    global image
    cv2.putText(image, txt, (10, 35),
    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
 
 
# calculate speed from pixels and time
def get_speed(pixels, ftperpixel, secs):
    if secs > 0.0:
        return ((pixels * ftperpixel)/ secs) * 0.681818  
    else:
        return 0.0


# calculate elapsed seconds
def secs_diff(endTime, begTime):
    diff = (endTime - begTime).total_seconds()
    return diff


# record speed in .csv format
def record_speed(resource):
    global csvfileout
    if not os.path.exists(os.path.join(PARENT_DIRECTORY, 'csv/')):
        os.makedirs(os.path.join(PARENT_DIRECTORY, 'csv/'))
    csv_path = os.path.join(PARENT_DIRECTORY, 'csv/', csvfileout)  
    f = open(csv_path, 'a')
    f.write(resource + "\n")
    f.close


# mouse callback function for drawing capture area
def draw_rectangle(event,x,y,flags,param):
    global ix,iy,fx,fy,drawing,setup_complete,image, org_image, prompt
    if event == cv2.EVENT_LBUTTONDOWN:
        drawing = True
        ix,iy = x,y
    elif event == cv2.EVENT_MOUSEMOVE:
        if drawing == True:
            image = org_image.copy()
            prompt_on_image(prompt)
            cv2.rectangle(image,(ix,iy),(x,y),(0,255,0),2)
    elif event == cv2.EVENT_LBUTTONUP:
        drawing = False
        fx,fy = x,y
        image = org_image.copy()
        prompt_on_image(prompt)
        cv2.rectangle(image,(ix,iy),(fx,fy),(0,255,0),2)


def jpg_img_text(speed_in_mph):
    if (SPEED_LIMIT + 15) < speed_in_mph < (SPEED_LIMIT + 25):
        speed_alert = '10+ mph over speed limit'
    elif speed_in_mph > (SPEED_LIMIT + 25):
        speed_alert = '20+ mph over speed limit'
    elif SPEED_LIMIT < speed_in_mph < (SPEED_LIMIT + 15):
        speed_alert = 'Exceeds speed limit'
    else:
        speed_alert = 'below speed limit'
    return speed_alert
 

def sunrise(lat, long):
    todays_date = datetime.date.today()
    sun = Sun(lat, long)
    sunrise = sun.get_local_sunrise_time(todays_date).strftime('%H.%M')
    return sunrise


def sunset(lat, long):
    todays_date = datetime.date.today()
    sun = Sun(lat, long)
    sunset = sun.get_local_sunset_time(todays_date).strftime('%H.%M')
    return sunset


def set_img_filename(vehicle_speed, minimum_speed, capture_time):
    speed = str(int(vehicle_speed))
    time = capture_time.strftime("%Y%m%d_%H:%M:%S%p")
    minimum = str(minimum_speed)
    if (vehicle_speed  > minimum_speed):
        img_filename = "img_" + time + '_' + speed + "_mph.jpg"
    else:
        img_filename = 'speed ' + speed + ' mph, below minimum speed of ' + minimum + ' mph'
    return img_filename


def send_email(sender, recipient, subject, body, password, server, image_path):
    mail_server = smtplib.SMTP_SSL(server)
    attachment_path = image_path
    from email.message import EmailMessage
    message = EmailMessage()
    message['From'] = sender
    message['To'] = recipient
    message['Subject'] = subject
    message.set_content(body)
    attachment_filename = os.path.basename(attachment_path)
    mime_type, _ = mimetypes.guess_type(attachment_path)
    mime_type, mime_subtype = mime_type.split('/', 1)
    with open(attachment_path, 'rb') as ap:
        message.add_attachment(ap.read(), maintype=mime_type, subtype=mime_subtype, filename=os.path.basename(attachment_path))
    #login and send email
    mail_server.login(sender, password)
    mail_server.send_message(message)
    mail_server.quit()
    print('Email Sent')
    cv2.waitKey(500)
# end email section


# INITIALIZATION
# Connect to database:
if SAVE_SQL:
    mydb = mysql.connector.connect(
            host = MYSQL_HOST,
            user = MYSQL_USER,
            password = <PASSWORD>,
            database = MYSQL_DATABASE
        )
    mycursor = mydb.cursor()
    print(mydb)

# Create CSV file and initialize
if SAVE_CSV:
    csv_file_date = datetime.datetime.now().strftime('%Y%m%d')
    csvfileout = "carspeed_{}.csv".format(csv_file_date)
    record_speed('Date,Time,Speed,Message,Image')
else:
    csvfileout = ''

# Calibrate frame dimensions
frame_width_ft = 2*(math.tan(math.radians(FOV*0.5))*DISTANCE)
ftperpixel = frame_width_ft / float(IMAGEWIDTH)
print("Image width in feet {} at {} from camera".format("%.0f" % frame_width_ft,"%.0f" % DISTANCE))

# Declare variables
state = WAITING
direction = UNKNOWN
initial_x = 0
last_x = 0
base_image = None
abs_chg = 0
mph = 0
secs = 0.0
ix,iy = -1,-1
fx,fy = -1,-1
drawing = False
setup_complete = False
tracking = False
text_on_image = 'Monitoring'
prompt = ''


# initialize the camera
camera = PiCamera()
camera.resolution = RESOLUTION
camera.framerate = FPS
camera.vflip = VERTICAL_FLIP
camera.hflip = HORIZONTAL_FLIP
rawCapture = PiRGBArray(camera, size=camera.resolution)

# allow the camera to warm up
time.sleep(0.9)

# DEFINE THE MONITORED AREA
# create an image window and place it in the upper left corner of the screen
cv2.namedWindow("Speed Camera")
cv2.moveWindow("Speed Camera", 0, 0)

# call the draw_rectangle routines when the mouse is used
cv2.setMouseCallback('Speed Camera',draw_rectangle)
 
# grab a reference image to use for drawing the monitored area's boundry
camera.capture(rawCapture, format="bgr", use_video_port=True)
image = rawCapture.array
rawCapture.truncate(0)
org_image = image.copy()
prompt = "Define the monitored area - press 'c' to continue" 
prompt_on_image(prompt)
 
# Wait while the user draws the monitored area's boundry
while not setup_complete:
    cv2.imshow("Speed Camera",image)
    key = cv2.waitKey(1) & 0xFF        # wait for for c to be pressed  
    if key == ord("c"):                # if the `c` key is pressed, break from the loop
        break

# Now the monitored area is defined, time to move on
prompt = "Press 'q' to quit" 
# since the monitored area's bounding box could be drawn starting 
# from any corner, normalize the coordinates
 
if fx > ix:
    upper_left_x = ix
    lower_right_x = fx
else:
    upper_left_x = fx
    lower_right_x = ix
 
if fy > iy:
    upper_left_y = iy
    lower_right_y = fy
else:
    upper_left_y = fy
    lower_right_y = iy
     
monitored_width = lower_right_x - upper_left_x
monitored_height = lower_right_y - upper_left_y
 
print("Monitored area:")
print(" upper_left_x {}".format(upper_left_x))
print(" upper_left_y {}".format(upper_left_y))
print(" lower_right_x {}".format(lower_right_x))
print(" lower_right_y {}".format(lower_right_y))
print(" monitored_width {}".format(monitored_width))
print(" monitored_height {}".format(monitored_height))
print(" monitored_area {}".format(monitored_width * monitored_height))

# BEGIN FRAME CAPTURE LOOP
# capture frames from the camera using capture_continuous.
#   This keeps the picamera in capture mode - it doesn't need
#   to prep for each frame's capture.
for frame in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):
    timestamp = datetime.datetime.now()  # Initialize the timestamp
    image = frame.array  # Grab the raw NumPy array representing the image 
    gray = image[upper_left_y:lower_right_y,upper_left_x:lower_right_x]  # Crop area defined by [y1:y2,x1:x2]
    gray = cv2.cvtColor(gray, cv2.COLOR_BGR2GRAY)  # Convert the frame to grayscale, and blur it
    gray = cv2.GaussianBlur(gray, BLURSIZE, 0)
    if base_image is None:        # If the base image has not been defined, initialize it
        base_image = gray.copy().astype("float")
        lastTime = timestamp
        rawCapture.truncate(0)
        cv2.imshow("Speed Camera", image)
    # Compute the absolute difference between the current image and
    # base image and then turn eveything lighter gray than THRESHOLD into
    # white
    frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(base_image))
    thresh = cv2.threshold(frameDelta, THRESHOLD, 255, cv2.THRESH_BINARY)[1]
    # Dilate the thresholded image to fill in any holes, then find contours
    # on thresholded image
    thresh = cv2.dilate(thresh, None, iterations=2)
    (cnts, _) = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    motion_found = False  # Look for motion 
    biggest_area = 0
    # Examine the contours, looking for the largest one
    for c in cnts:
        (x1, y1, w1, h1) = cv2.boundingRect(c)
        found_area = w1*h1  # Get an approximate area of the contour
        # Find the largest bounding rectangle
        if (found_area > MIN_AREA) and (found_area > biggest_area):  
            biggest_area = found_area
            motion_found = True  # Setting this to True initializes tracking in next if 
            x = x1
            y = y1
            h = h1
            w = w1
    if motion_found:
        if state == WAITING:
            state = TRACKING  # intialize tracking
            initial_x = x
            last_x = x
            initial_time = timestamp
            last_mph = 0
            text_on_image = 'Tracking'
        else:
            secs = secs_diff(timestamp,initial_time)  # Compute the lapsed time
            if secs >= 15:
                state = WAITING
                direction = UNKNOWN
                text_on_image = 'Waiting'
                motion_found = False
                biggest_area = 0
                rawCapture.truncate(0)
                base_image = None
                continue             
            if state == TRACKING:
                if x >= last_x:
                    direction = LEFT_TO_RIGHT
                    abs_chg = x + w - initial_x
                else:
                    direction = RIGHT_TO_LEFT
                    abs_chg = initial_x - x
                mph = get_speed(abs_chg,ftperpixel,secs)
                real_y = upper_left_y + y
                real_x = upper_left_x + x
                # Is front of object outside the monitored boundary? Then write date, time and speed on image
                # and save it 
                if ((x <= 2) and (direction == RIGHT_TO_LEFT)) \
                        or ((x+w >= monitored_width - 2) \
                        and (direction == LEFT_TO_RIGHT)):
                    # print capture log to terminal
                    img_filename = set_img_filename(last_mph, MIN_SPEED, timestamp)
                    print('Vehicle Detected:', img_filename, ',', jpg_img_text(last_mph))
                    
                    sql_date = datetime.datetime.now().strftime("%Y%m%d")
                    sql_time = timestamp.strftime('%H%M%S')
                    sql_mph = last_mph
                    sql_img_path = ''
                    sql_img_filename = ''

                    todays_directory = datetime.datetime.now().strftime("%Y%m%d")
                    if ((last_mph > MIN_SPEED) & (sun_rise < cur_time < sun_set)):
                        sql_img_path = os.path.join(PARENT_DIRECTORY, todays_directory)
                        sql_img_filename = "img_" + timestamp.strftime("%Y%m%d_%H:%M:%S%p") + '_' + str(int(last_mph)) + "_mph.jpg"

                    if SAVE_SQL:
                        sql = "INSERT INTO DetectionLog (date, time, speed, img_path, img_filename) VALUES (%s, %s, %s, %s, %s)"
                        val = (sql_date, sql_time, sql_mph, sql_img_path, sql_img_filename)
                        mycursor.execute(sql, val)
                        mydb.commit()
                        print(mycursor.rowcount, "record inserted.")

                    if SAVE_CSV:
                        if not datetime.datetime.now().strftime('%Y%m%d') == csv_file_date:
                            csv_file_date = datetime.datetime.now().strftime('%Y%m%d')
                            csvfileout = "carspeed_{}.csv".format(csv_file_date)
                        cap_time = timestamp
                        record_speed(cap_time.strftime("%Y%m%d") + ',' + cap_time.strftime('%H%M%S%p') + ', ' + ("%.0f" % last_mph) + ', ' + jpg_img_text(last_mph) + ', ' + img_filename)

                    # Save images only between sunrise and sunset
                    sun_rise = sunrise(LATITUDE, LONGITUDE)
                    sun_set = sunset(LATITUDE, LONGITUDE)
                    cur_time = datetime.datetime.now().strftime('%H.%M')

                    # Save images if above minimum image capture speed and during daylight
                    if ((last_mph > MIN_SPEED) & (sun_rise < cur_time < sun_set)):
                        # timestamp the image
                        cv2.putText(image, timestamp.strftime("%A %d %B %Y %I:%M:%S%p"),
                            (10, image.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
                        # Add text related to speed of vehicle from jpg_img_text()
                        cv2.putText(image, str(jpg_img_text(last_mph)), (int(IMAGEHEIGHT * 0.1),int(IMAGEHEIGHT * 0.1)), cv2.FONT_HERSHEY_SIMPLEX, 1.00, (0,255,0),2)
                        # Test for today's image directory. If it does not exist, create it.
                        todays_directory = datetime.datetime.now().strftime("%Y%m%d")
                        todays_path = os.path.join(PARENT_DIRECTORY, todays_directory)
                        if not os.path.exists(todays_path):
                            os.makedirs(todays_path)
                        # Create string with image path and filename
                        image_path = todays_path + '/' + img_filename
                        # Save the image    
                        cv2.imwrite(image_path, image)
                        print('Image Saved')

                        # Send email
                        if SEND_EMAIL & (last_mph > EMAIL_SPEED):
                            send_email(EMAIL_SENDER, EMAIL_RECIPIENT, EMAIL_SUBJECT, EMAIL_BODY, EMAIL_PASSWORD, EMAIL_SERVER, image_path)

                    state = SAVING
                # if the object hasn't reached the end of the monitored area, just remember the speed 
                # and its last position
                last_mph = mph
                last_x = x
                
    else:
        if state != WAITING:
            state = WAITING
            direction = UNKNOWN
            text_on_image = 'Monitoring'
            
    # only update image and wait for a keypress when waiting for a car
    # This is required since waitkey slows processing.
    
    if (state == WAITING):    
         # draw the text and timestamp on the frame
        cv2.putText(image, datetime.datetime.now().strftime("%A %d %B %Y %I:%M:%S%p"),
            (10, image.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (255, 255, 255), 2)
        cv2.putText(image, "Car Speed Detector: {}".format(text_on_image), (10, 20),
            cv2.FONT_HERSHEY_SIMPLEX,0.5, (255, 255, 255), 2)

        if SHOW_BOUNDS:
            #define the monitored area right and left boundary
            cv2.line(image,(upper_left_x,upper_left_y),(upper_left_x,lower_right_y),(0, 255, 0))
            cv2.line(image,(lower_right_x,upper_left_y),(lower_right_x,lower_right_y),(0, 255, 0))
       
        # show the frame and check for a keypress
        if SHOW_IMAGE:
            prompt_on_image(prompt)
            cv2.imshow("Speed Camera", image)
            
        # Adjust the base_image as lighting changes through the day
        if state == WAITING:
            last_x = 0
            cv2.accumulateWeighted(gray, base_image, 0.25)
 
        state=WAITING;
        key = cv2.waitKey(1) & 0xFF
      
        # if the `q` key is pressed, break from the loop and terminate processing
        if key == ord("q"):
            break

    # clear the stream in preparation for the next frame
    rawCapture.truncate(0)

# cleanup the camera and close any open windows
cv2.destroyAllWindows()
from picamera.array import PiRGBArray
from picamera import PiCamera
import time
import math
import datetime
from suntime import Sun
import os
import cv2
import mysql.connector
import mimetypes
import smtplib


# User modifiable variables:
DISTANCE = 76  # Enter your distance-to-road value here
SPEED_LIMIT = 25  # Enter the speed limit of the road being monitored here
MIN_SPEED = 45  # Enter the minimum speed for saving images
VERTICAL_FLIP = True  # If image is upside down, change this to True or False
HORIZONTAL_FLIP = True  # If image is reversed side to side, change this to True or False
EMAIL_SPEED = 50  # Enter the minimum speed for sending an email with attached image
SAVE_CSV = True  # Turn saving to csv file on and off
SAVE_SQL = False  # Turn saving to mysql database on and off
SEND_EMAIL = False # Turn email function on and off
EMAIL_SENDER = "<EMAIL>" # Email user account used with email server
EMAIL_RECIPIENT ="<EMAIL>" # Email address to which messages will be sent
EMAIL_SERVER ='youremail.com' # SMTP email server url
EMAIL_PASSWORD = "<PASSWORD>" # Email user account password on email server
EMAIL_SUBJECT = 'Car Speed Detector Report' # Email subject bar, edit this as desired
EMAIL_BODY = "The attached image is of a vehicle recorded at excess of 20 mph over the speed limit on Elm Street at Bluebird Street in Springfield by the Car Speed Detector."
# Email body is the message of the email which will also include the attached image of the vehicle.
# Enter mysql database connection data below
MYSQL_HOST = 'localhost'
MYSQL_USER = 'yourmysqlusername'
MYSQL_PASSWORD = '<PASSWORD>'
MYSQL_DATABASE = 'yourmysqldatabase' # You will have to set up a mariadb or mysql database
PARENT_DIRECTORY = "/home/pi/Python/Projects/CarSpeedDetector/" # The directory in which this script is located.
THRESHOLD = 15 # Modify the action of the Thresh function
MIN_AREA = 175
BLURSIZE = (15,15)  # Modify the Gaussian Blur function
IMAGEWIDTH = 640  # Modify the image output width
IMAGEHEIGHT = 480  # Modify the image output height
RESOLUTION = [IMAGEWIDTH,IMAGEHEIGHT]
FOV = 53.5  # Field of view
FPS = 30
SHOW_BOUNDS = True
SHOW_IMAGE = True
LATITUDE = 40.4  # Enter your approximate latitude here, used to determine sunrise and set times
LONGITUDE = -87.2  # Enter your approximate longitude here

# the following enumerated values are used to make the program more readable
WAITING = 0
TRACKING = 1
SAVING = 2
UNKNOWN = 0
LEFT_TO_RIGHT = 1
RIGHT_TO_LEFT = 2

# place a prompt on the displayed image
def prompt_on_image(txt):
    global image
    cv2.putText(image, txt, (10, 35),
    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
 
 
# calculate speed from pixels and time
def get_speed(pixels, ftperpixel, secs):
    if secs > 0.0:
        return ((pixels * ftperpixel)/ secs) * 0.681818  
    else:
        return 0.0


# calculate elapsed seconds
def secs_diff(endTime, begTime):
    diff = (endTime - begTime).total_seconds()
    return diff


# record speed in .csv format
def record_speed(resource):
    global csvfileout
    if not os.path.exists(os.path.join(PARENT_DIRECTORY, 'csv/')):
        os.makedirs(os.path.join(PARENT_DIRECTORY, 'csv/'))
    csv_path = os.path.join(PARENT_DIRECTORY, 'csv/', csvfileout)  
    f = open(csv_path, 'a')
    f.write(resource + "\n")
    f.close


# mouse callback function for drawing capture area
def draw_rectangle(event,x,y,flags,param):
    global ix,iy,fx,fy,drawing,setup_complete,image, org_image, prompt
    if event == cv2.EVENT_LBUTTONDOWN:
        drawing = True
        ix,iy = x,y
    elif event == cv2.EVENT_MOUSEMOVE:
        if drawing == True:
            image = org_image.copy()
            prompt_on_image(prompt)
            cv2.rectangle(image,(ix,iy),(x,y),(0,255,0),2)
    elif event == cv2.EVENT_LBUTTONUP:
        drawing = False
        fx,fy = x,y
        image = org_image.copy()
        prompt_on_image(prompt)
        cv2.rectangle(image,(ix,iy),(fx,fy),(0,255,0),2)


def jpg_img_text(speed_in_mph):
    if (SPEED_LIMIT + 15) < speed_in_mph < (SPEED_LIMIT + 25):
        speed_alert = '10+ mph over speed limit'
    elif speed_in_mph > (SPEED_LIMIT + 25):
        speed_alert = '20+ mph over speed limit'
    elif SPEED_LIMIT < speed_in_mph < (SPEED_LIMIT + 15):
        speed_alert = 'Exceeds speed limit'
    else:
        speed_alert = 'below speed limit'
    return speed_alert
 

def sunrise(lat, long):
    todays_date = datetime.date.today()
    sun = Sun(lat, long)
    sunrise = sun.get_local_sunrise_time(todays_date).strftime('%H.%M')
    return sunrise


def sunset(lat, long):
    todays_date = datetime.date.today()
    sun = Sun(lat, long)
    sunset = sun.get_local_sunset_time(todays_date).strftime('%H.%M')
    return sunset


def set_img_filename(vehicle_speed, minimum_speed, capture_time):
    speed = str(int(vehicle_speed))
    time = capture_time.strftime("%Y%m%d_%H:%M:%S%p")
    minimum = str(minimum_speed)
    if (vehicle_speed  > minimum_speed):
        img_filename = "img_" + time + '_' + speed + "_mph.jpg"
    else:
        img_filename = 'speed ' + speed + ' mph, below minimum speed of ' + minimum + ' mph'
    return img_filename


def send_email(sender, recipient, subject, body, password, server, image_path):
    mail_server = smtplib.SMTP_SSL(server)
    attachment_path = image_path
    from email.message import EmailMessage
    message = EmailMessage()
    message['From'] = sender
    message['To'] = recipient
    message['Subject'] = subject
    message.set_content(body)
    attachment_filename = os.path.basename(attachment_path)
    mime_type, _ = mimetypes.guess_type(attachment_path)
    mime_type, mime_subtype = mime_type.split('/', 1)
    with open(attachment_path, 'rb') as ap:
        message.add_attachment(ap.read(), maintype=mime_type, subtype=mime_subtype, filename=os.path.basename(attachment_path))
    #login and send email
    mail_server.login(sender, password)
    mail_server.send_message(message)
    mail_server.quit()
    print('Email Sent')
    cv2.waitKey(500)
# end email section


# INITIALIZATION
# Connect to database:
if SAVE_SQL:
    mydb = mysql.connector.connect(
            host = MYSQL_HOST,
            user = MYSQL_USER,
            password = <PASSWORD>,
            database = MYSQL_DATABASE
        )
    mycursor = mydb.cursor()
    print(mydb)

# Create CSV file and initialize
if SAVE_CSV:
    csv_file_date = datetime.datetime.now().strftime('%Y%m%d')
    csvfileout = "carspeed_{}.csv".format(csv_file_date)
    record_speed('Date,Time,Speed,Message,Image')
else:
    csvfileout = ''

# Calibrate frame dimensions
frame_width_ft = 2*(math.tan(math.radians(FOV*0.5))*DISTANCE)
ftperpixel = frame_width_ft / float(IMAGEWIDTH)
print("Image width in feet {} at {} from camera".format("%.0f" % frame_width_ft,"%.0f" % DISTANCE))

# Declare variables
state = WAITING
direction = UNKNOWN
initial_x = 0
last_x = 0
base_image = None
abs_chg = 0
mph = 0
secs = 0.0
ix,iy = -1,-1
fx,fy = -1,-1
drawing = False
setup_complete = False
tracking = False
text_on_image = 'Monitoring'
prompt = ''


# initialize the camera
camera = PiCamera()
camera.resolution = RESOLUTION
camera.framerate = FPS
camera.vflip = VERTICAL_FLIP
camera.hflip = HORIZONTAL_FLIP
rawCapture = PiRGBArray(camera, size=camera.resolution)

# allow the camera to warm up
time.sleep(0.9)

# DEFINE THE MONITORED AREA
# create an image window and place it in the upper left corner of the screen
cv2.namedWindow("Speed Camera")
cv2.moveWindow("Speed Camera", 0, 0)

# call the draw_rectangle routines when the mouse is used
cv2.setMouseCallback('Speed Camera',draw_rectangle)
 
# grab a reference image to use for drawing the monitored area's boundry
camera.capture(rawCapture, format="bgr", use_video_port=True)
image = rawCapture.array
rawCapture.truncate(0)
org_image = image.copy()
prompt = "Define the monitored area - press 'c' to continue" 
prompt_on_image(prompt)
 
# Wait while the user draws the monitored area's boundry
while not setup_complete:
    cv2.imshow("Speed Camera",image)
    key = cv2.waitKey(1) & 0xFF        # wait for for c to be pressed  
    if key == ord("c"):                # if the `c` key is pressed, break from the loop
        break

# Now the monitored area is defined, time to move on
prompt = "Press 'q' to quit" 
# since the monitored area's bounding box could be drawn starting 
# from any corner, normalize the coordinates
 
if fx > ix:
    upper_left_x = ix
    lower_right_x = fx
else:
    upper_left_x = fx
    lower_right_x = ix
 
if fy > iy:
    upper_left_y = iy
    lower_right_y = fy
else:
    upper_left_y = fy
    lower_right_y = iy
     
monitored_width = lower_right_x - upper_left_x
monitored_height = lower_right_y - upper_left_y
 
print("Monitored area:")
print(" upper_left_x {}".format(upper_left_x))
print(" upper_left_y {}".format(upper_left_y))
print(" lower_right_x {}".format(lower_right_x))
print(" lower_right_y {}".format(lower_right_y))
print(" monitored_width {}".format(monitored_width))
print(" monitored_height {}".format(monitored_height))
print(" monitored_area {}".format(monitored_width * monitored_height))

# BEGIN FRAME CAPTURE LOOP
# capture frames from the camera using capture_continuous.
#   This keeps the picamera in capture mode - it doesn't need
#   to prep for each frame's capture.
for frame in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):
    timestamp = datetime.datetime.now()  # Initialize the timestamp
    image = frame.array  # Grab the raw NumPy array representing the image 
    gray = image[upper_left_y:lower_right_y,upper_left_x:lower_right_x]  # Crop area defined by [y1:y2,x1:x2]
    gray = cv2.cvtColor(gray, cv2.COLOR_BGR2GRAY)  # Convert the frame to grayscale, and blur it
    gray = cv2.GaussianBlur(gray, BLURSIZE, 0)
    if base_image is None:        # If the base image has not been defined, initialize it
        base_image = gray.copy().astype("float")
        lastTime = timestamp
        rawCapture.truncate(0)
        cv2.imshow("Speed Camera", image)
    # Compute the absolute difference between the current image and
    # base image and then turn eveything lighter gray than THRESHOLD into
    # white
    frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(base_image))
    thresh = cv2.threshold(frameDelta, THRESHOLD, 255, cv2.THRESH_BINARY)[1]
    # Dilate the thresholded image to fill in any holes, then find contours
    # on thresholded image
    thresh = cv2.dilate(thresh, None, iterations=2)
    (cnts, _) = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    motion_found = False  # Look for motion 
    biggest_area = 0
    # Examine the contours, looking for the largest one
    for c in cnts:
        (x1, y1, w1, h1) = cv2.boundingRect(c)
        found_area = w1*h1  # Get an approximate area of the contour
        # Find the largest bounding rectangle
        if (found_area > MIN_AREA) and (found_area > biggest_area):  
            biggest_area = found_area
            motion_found = True  # Setting this to True initializes tracking in next if 
            x = x1
            y = y1
            h = h1
            w = w1
    if motion_found:
        if state == WAITING:
            state = TRACKING  # intialize tracking
            initial_x = x
            last_x = x
            initial_time = timestamp
            last_mph = 0
            text_on_image = 'Tracking'
        else:
            secs = secs_diff(timestamp,initial_time)  # Compute the lapsed time
            if secs >= 15:
                state = WAITING
                direction = UNKNOWN
                text_on_image = 'Waiting'
                motion_found = False
                biggest_area = 0
                rawCapture.truncate(0)
                base_image = None
                continue             
            if state == TRACKING:
                if x >= last_x:
                    direction = LEFT_TO_RIGHT
                    abs_chg = x + w - initial_x
                else:
                    direction = RIGHT_TO_LEFT
                    abs_chg = initial_x - x
                mph = get_speed(abs_chg,ftperpixel,secs)
                real_y = upper_left_y + y
                real_x = upper_left_x + x
                # Is front of object outside the monitored boundary? Then write date, time and speed on image
                # and save it 
                if ((x <= 2) and (direction == RIGHT_TO_LEFT)) \
                        or ((x+w >= monitored_width - 2) \
                        and (direction == LEFT_TO_RIGHT)):
                    # print capture log to terminal
                    img_filename = set_img_filename(last_mph, MIN_SPEED, timestamp)
                    print('Vehicle Detected:', img_filename, ',', jpg_img_text(last_mph))
                    
                    sql_date = datetime.datetime.now().strftime("%Y%m%d")
                    sql_time = timestamp.strftime('%H%M%S')
                    sql_mph = last_mph
                    sql_img_path = ''
                    sql_img_filename = ''

                    todays_directory = datetime.datetime.now().strftime("%Y%m%d")
                    if ((last_mph > MIN_SPEED) & (sun_rise < cur_time < sun_set)):
                        sql_img_path = os.path.join(PARENT_DIRECTORY, todays_directory)
                        sql_img_filename = "img_" + timestamp.strftime("%Y%m%d_%H:%M:%S%p") + '_' + str(int(last_mph)) + "_mph.jpg"

                    if SAVE_SQL:
                        sql = "INSERT INTO DetectionLog (date, time, speed, img_path, img_filename) VALUES (%s, %s, %s, %s, %s)"
                        val = (sql_date, sql_time, sql_mph, sql_img_path, sql_img_filename)
                        mycursor.execute(sql, val)
                        mydb.commit()
                        print(mycursor.rowcount, "record inserted.")

                    if SAVE_CSV:
                        if not datetime.datetime.now().strftime('%Y%m%d') == csv_file_date:
                            csv_file_date = datetime.datetime.now().strftime('%Y%m%d')
                            csvfileout = "carspeed_{}.csv".format(csv_file_date)
                        cap_time = timestamp
                        record_speed(cap_time.strftime("%Y%m%d") + ',' + cap_time.strftime('%H%M%S%p') + ', ' + ("%.0f" % last_mph) + ', ' + jpg_img_text(last_mph) + ', ' + img_filename)

                    # Save images only between sunrise and sunset
                    sun_rise = sunrise(LATITUDE, LONGITUDE)
                    sun_set = sunset(LATITUDE, LONGITUDE)
                    cur_time = datetime.datetime.now().strftime('%H.%M')

                    # Save images if above minimum image capture speed and during daylight
                    if ((last_mph > MIN_SPEED) & (sun_rise < cur_time < sun_set)):
                        # timestamp the image
                        cv2.putText(image, timestamp.strftime("%A %d %B %Y %I:%M:%S%p"),
                            (10, image.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
                        # Add text related to speed of vehicle from jpg_img_text()
                        cv2.putText(image, str(jpg_img_text(last_mph)), (int(IMAGEHEIGHT * 0.1),int(IMAGEHEIGHT * 0.1)), cv2.FONT_HERSHEY_SIMPLEX, 1.00, (0,255,0),2)
                        # Test for today's image directory. If it does not exist, create it.
                        todays_directory = datetime.datetime.now().strftime("%Y%m%d")
                        todays_path = os.path.join(PARENT_DIRECTORY, todays_directory)
                        if not os.path.exists(todays_path):
                            os.makedirs(todays_path)
                        # Create string with image path and filename
                        image_path = todays_path + '/' + img_filename
                        # Save the image    
                        cv2.imwrite(image_path, image)
                        print('Image Saved')

                        # Send email
                        if SEND_EMAIL & (last_mph > EMAIL_SPEED):
                            send_email(EMAIL_SENDER, EMAIL_RECIPIENT, EMAIL_SUBJECT, EMAIL_BODY, EMAIL_PASSWORD, EMAIL_SERVER, image_path)

                    state = SAVING
                # if the object hasn't reached the end of the monitored area, just remember the speed 
                # and its last position
                last_mph = mph
                last_x = x
                
    else:
        if state != WAITING:
            state = WAITING
            direction = UNKNOWN
            text_on_image = 'Monitoring'
            
    # only update image and wait for a keypress when waiting for a car
    # This is required since waitkey slows processing.
    
    if (state == WAITING):    
         # draw the text and timestamp on the frame
        cv2.putText(image, datetime.datetime.now().strftime("%A %d %B %Y %I:%M:%S%p"),
            (10, image.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (255, 255, 255), 2)
        cv2.putText(image, "Car Speed Detector: {}".format(text_on_image), (10, 20),
            cv2.FONT_HERSHEY_SIMPLEX,0.5, (255, 255, 255), 2)

        if SHOW_BOUNDS:
            #define the monitored area right and left boundary
            cv2.line(image,(upper_left_x,upper_left_y),(upper_left_x,lower_right_y),(0, 255, 0))
            cv2.line(image,(lower_right_x,upper_left_y),(lower_right_x,lower_right_y),(0, 255, 0))
       
        # show the frame and check for a keypress
        if SHOW_IMAGE:
            prompt_on_image(prompt)
            cv2.imshow("Speed Camera", image)
            
        # Adjust the base_image as lighting changes through the day
        if state == WAITING:
            last_x = 0
            cv2.accumulateWeighted(gray, base_image, 0.25)
 
        state=WAITING;
        key = cv2.waitKey(1) & 0xFF
      
        # if the `q` key is pressed, break from the loop and terminate processing
        if key == ord("q"):
            break

    # clear the stream in preparation for the next frame
    rawCapture.truncate(0)

# cleanup the camera and close any open windows
cv2.destroyAllWindows()
# User modifiable variables: # Enter your distance-to-road value here # Enter the speed limit of the road being monitored here # Enter the minimum speed for saving images # If image is upside down, change this to True or False # If image is reversed side to side, change this to True or False # Enter the minimum speed for sending an email with attached image # Turn saving to csv file on and off # Turn saving to mysql database on and off # Turn email function on and off # Email user account used with email server # Email address to which messages will be sent # SMTP email server url # Email user account password on email server # Email subject bar, edit this as desired # Email body is the message of the email which will also include the attached image of the vehicle. # Enter mysql database connection data below # You will have to set up a mariadb or mysql database # The directory in which this script is located. # Modify the action of the Thresh function # Modify the Gaussian Blur function # Modify the image output width # Modify the image output height # Field of view # Enter your approximate latitude here, used to determine sunrise and set times # Enter your approximate longitude here # the following enumerated values are used to make the program more readable # place a prompt on the displayed image # calculate speed from pixels and time # calculate elapsed seconds # record speed in .csv format # mouse callback function for drawing capture area #login and send email # end email section # INITIALIZATION # Connect to database: # Create CSV file and initialize # Calibrate frame dimensions # Declare variables # initialize the camera # allow the camera to warm up # DEFINE THE MONITORED AREA # create an image window and place it in the upper left corner of the screen # call the draw_rectangle routines when the mouse is used # grab a reference image to use for drawing the monitored area's boundry # Wait while the user draws the monitored area's boundry # wait for for c to be pressed # if the `c` key is pressed, break from the loop # Now the monitored area is defined, time to move on # since the monitored area's bounding box could be drawn starting # from any corner, normalize the coordinates # BEGIN FRAME CAPTURE LOOP # capture frames from the camera using capture_continuous. #   This keeps the picamera in capture mode - it doesn't need #   to prep for each frame's capture. # Initialize the timestamp # Grab the raw NumPy array representing the image # Crop area defined by [y1:y2,x1:x2] # Convert the frame to grayscale, and blur it # If the base image has not been defined, initialize it # Compute the absolute difference between the current image and # base image and then turn eveything lighter gray than THRESHOLD into # white # Dilate the thresholded image to fill in any holes, then find contours # on thresholded image # Look for motion # Examine the contours, looking for the largest one # Get an approximate area of the contour # Find the largest bounding rectangle # Setting this to True initializes tracking in next if # intialize tracking # Compute the lapsed time # Is front of object outside the monitored boundary? Then write date, time and speed on image # and save it # print capture log to terminal # Save images only between sunrise and sunset # Save images if above minimum image capture speed and during daylight # timestamp the image # Add text related to speed of vehicle from jpg_img_text() # Test for today's image directory. If it does not exist, create it. # Create string with image path and filename # Save the image # Send email # if the object hasn't reached the end of the monitored area, just remember the speed # and its last position # only update image and wait for a keypress when waiting for a car # This is required since waitkey slows processing. # draw the text and timestamp on the frame #define the monitored area right and left boundary # show the frame and check for a keypress # Adjust the base_image as lighting changes through the day # if the `q` key is pressed, break from the loop and terminate processing # clear the stream in preparation for the next frame # cleanup the camera and close any open windows