Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update carspeed.py #14

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 126 additions & 63 deletions carspeed.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,38 @@
# CarSpeed Version 2.0
# Speed Version 2.0

#changes by KirkLoeten (GITHUB)
# paramter for function [cv2.findContours ] form 3 to 2 - cnts, _
# Functions:
# Selector image 0 - off, 0 > speedlimit
# Selector csv 0 - off, 0 > speedlimit
# Selector of units m / ft -> kmh / mph
# read setup monitored area by setup.txt file, write a new file by use 'c' in setup
# Generate new CSV at new Day
# generate folders per Day
# Nice to have: set central text color for all images

# import the necessary packages
from picamera.array import PiRGBArray
from picamera import PiCamera
import time
import math
import datetime
import cv2
import os
import cv2 # sudo apt-get install libopencv-dev python-opencv

# 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.35, (0, 0, 255), 1)
cv2.putText(image, txt, (10, 35), cv2.FONT_HERSHEY_SIMPLEX, 0.35, (0, 255, 127, 1))

# calculate speed from pixels and time
def get_speed(pixels, ftperpixel, secs):
if secs > 0.0:
return ((pixels * ftperpixel)/ secs) * 0.681818
if SPEED_UNIT == "mph":
return ((pixels * unit_perpixel)/ secs) * 0.681818
else:
#m/s to km/h
return ((pixels * unit_perpixel)/ (secs) ) * 3.6
else:
return 0.0

Expand Down Expand Up @@ -54,34 +69,43 @@ def draw_rectangle(event,x,y,flags,param):
prompt_on_image(prompt)
cv2.rectangle(image,(ix,iy),(fx,fy),(0,255,0),2)

# define some constants
DISTANCE = 76 #<---- enter your distance-to-road value here
MIN_SPEED = 0 #<---- enter the minimum speed for saving images
SAVE_CSV = False #<---- record the results in .csv format in carspeed_(date).csv
# define some constants, selectors
DISTANCE = 10 #<---- enter your distance-to-road value here
SAVE_CSV = 25 #0 is off, >0 is the speedlimit, record the results in .csv format in speed_(date).csv
SAVE_IMAGE = 25 #0 is off, >0 is the speedlimit, no image as privaty setup
LEN_UNIT = "m" # "ft" or "m" (mph or km/h is selected automaticly by this constant)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo 'automatically'


THRESHOLD = 15
MIN_AREA = 175
#picture and difference parameter
THRESHOLD = 50 #15
MIN_AREA = 175
BLURSIZE = (15,15)
IMAGEWIDTH = 640
IMAGEHEIGHT = 480
RESOLUTION = [IMAGEWIDTH,IMAGEHEIGHT]
FOV = 53.5 #<---- Field of view
FPS = 30
SHOW_BOUNDS = True
SHOW_IMAGE = True
SHOW_BOUNDS = False
SHOW_IMAGE = False

if LEN_UNIT.upper() == "ft".upper(): #ignore case, compare string upper letters
SPEED_UNIT = "mph"
else:
SPEED_UNIT = "km/h"

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

# calculate the the width of the image at the distance specified
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))
frame_width_unit = 2*(math.tan(math.radians(FOV*0.5))*DISTANCE)
unit_perpixel = frame_width_unit / float(IMAGEWIDTH)
print("Image width in {} {} at {} {} from camera".format( "%.0f" % frame_width_unit, LEN_UNIT, "%.0f" % DISTANCE, LEN_UNIT))

# state maintains the state of the speed computation process
# if starts as WAITING
Expand All @@ -105,16 +129,15 @@ def draw_rectangle(event,x,y,flags,param):
#-- other values used in program
base_image = None
abs_chg = 0
mph = 0
speed = 0
secs = 0.0
ix,iy = -1,-1
fx,fy = -1,-1
drawing = False
setup_complete = False
tracking = False
text_on_image = 'No cars'
text_on_image = 'No motions'
prompt = ''

# initialize the camera. Adjust vflip and hflip to reflect your camera's orientation
camera = PiCamera()
camera.resolution = RESOLUTION
Expand All @@ -139,13 +162,13 @@ def draw_rectangle(event,x,y,flags,param):
rawCapture.truncate(0)
org_image = image.copy()

if SAVE_CSV:
csvfileout = "carspeed_{}.cvs".format(datetime.datetime.now().strftime("%Y%m%d_%H%M"))
record_speed('Date,Day,Time,Speed,Image')
else:
csvfileout = ''

prompt = "Define the monitored area - press 'c' to continue"
setup_exist = os.path.isfile("setup.txt")
if setup_exist:
prompt = "Define the monitored area - press 'c' to continue - press 'L' for lastsetup"
else:
prompt = "Define the monitored area - press 'c' to continue"
#test for setup.txt file
prompt_on_image(prompt)

# wait while the user draws the monitored area's boundry
Expand All @@ -158,10 +181,29 @@ def draw_rectangle(event,x,y,flags,param):
# if the `c` key is pressed, break from the loop
if key == ord("c"):
break

# if the `l` key is pressed, use last setup
if (key == ord("L") or key == ord("l")) and setup_exist:
with open("setup.txt", 'r') as f:
setup_txt = f.readlines()
f.close
for line in setup_txt:
line = line.strip()
line = line.split("=")
if line[0] == "ix":
ix = int(line[1])
if line[0] == "iy":
iy = int(line[1])
if line[0] == "fx":
fx = int(line[1])
if line[0] == "fy":
fy = int(line[1])
break


# the monitored area is defined, time to move on
prompt = "Press 'q' to quit"
prompt_on_image(prompt)
# since the monitored area's bounding box could be drawn starting
# from any corner, normalize the coordinates

Expand All @@ -183,14 +225,23 @@ def draw_rectangle(event,x,y,flags,param):
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))
print(" upper_left_x {} px".format(upper_left_x))
print(" upper_left_y {} px".format(upper_left_y))
print(" lower_right_x {} px".format(lower_right_x))
print(" lower_right_y {} px".format(lower_right_y))
print(" monitored_width {0:d} px ({1:.2f} {2:s})".format(monitored_width, frame_width_unit, LEN_UNIT))
print(" monitored_height {} px".format(monitored_height))
print(" monitored_area {} px".format(monitored_width * monitored_height))

# save setup
if (monitored_width > 10 and monitored_height > 10):
with open("setup.txt", 'w') as f:
f.write("ix="+str(upper_left_x)+"\n")
f.write("iy="+str(upper_left_y)+"\n")
f.write("fx="+str(lower_right_x)+"\n")
f.write("fy="+str(lower_right_y)+"\n")
f.close

# 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.
Expand Down Expand Up @@ -225,7 +276,7 @@ def draw_rectangle(event,x,y,flags,param):
# 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)
cnts, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this needs to be "_, cnts, _ =" for it to work without a "too many values to unpack" error.


# look for motion
motion_found = False
Expand All @@ -252,18 +303,18 @@ def draw_rectangle(event,x,y,flags,param):
initial_x = x
last_x = x
initial_time = timestamp
last_mph = 0
last_speed = 0
text_on_image = 'Tracking'
print(text_on_image)
print("x-chg Secs MPH x-pos width")
print("x-chg Secs {} dir x-pos width".format(SPEED_UNIT))
else:
# compute the lapsed time
secs = secs_diff(timestamp,initial_time)

if secs >= 15:
state = WAITING
direction = UNKNOWN
text_on_image = 'No Car Detected'
text_on_image = 'No motion detected'
motion_found = False
biggest_area = 0
rawCapture.truncate(0)
Expand All @@ -278,61 +329,74 @@ def draw_rectangle(event,x,y,flags,param):
else:
direction = RIGHT_TO_LEFT
abs_chg = initial_x - x
mph = get_speed(abs_chg,ftperpixel,secs)
print("{0:4d} {1:7.2f} {2:7.0f} {3:4d} {4:4d}".format(abs_chg,secs,mph,x,w))
speed = get_speed(abs_chg,unit_perpixel,secs)

print("{0:4d} {1:7.2f} {2:7.0f} {3:1d} {4:4d} {5:4d}".format(abs_chg,secs,speed, direction,x,w))
real_y = upper_left_y + y
real_x = upper_left_x + x
# is front of object outside the monitired 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)):
if (last_mph > MIN_SPEED): # save the image
if (last_speed > SAVE_CSV or last_speed > SAVE_IMAGE): # save the image
# timestamp the image
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, (0, 255, 0), 1)
cv2.putText(image, timestamp.strftime("%A %d %B %Y %H:%M:%S"),
(10, image.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 255, 127), 1)
# write the speed: first get the size of the text
size, base = cv2.getTextSize( "%.0f mph" % last_mph, cv2.FONT_HERSHEY_SIMPLEX, 2, 3)
size, base = cv2.getTextSize( "{0:.0f} {1:s}".format(last_speed, SPEED_UNIT), cv2.FONT_HERSHEY_SIMPLEX, 2, 3)
# then center it horizontally on the image
cntr_x = int((IMAGEWIDTH - size[0]) / 2)
cv2.putText(image, "%.0f mph" % last_mph,
(cntr_x , int(IMAGEHEIGHT * 0.2)), cv2.FONT_HERSHEY_SIMPLEX, 2.00, (0, 255, 0), 3)
# and save the image to disk
imageFilename = "car_at_" + datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + ".jpg"
# use the following image file name if you want to be able to sort the images by speed
#imageFilename = "car_at_%02.0f" % last_mph + "_" + datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + ".jpg"
cv2.putText(image, "{0:.0f} {1:s}".format(last_speed, SPEED_UNIT),
(cntr_x , int(IMAGEHEIGHT * 0.2)), cv2.FONT_HERSHEY_SIMPLEX, 2.00, (0, 255, 127), 3)
# and save to disk
# check folder
folder = "{}".format(datetime.datetime.now().strftime("%Y%m%d"))
if not os.path.exists(folder):
os.makedirs(folder)
csvfileout = folder + "/speed_{}.cvs".format(timestamp.strftime("%Y%m%d"))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be .csv

record_speed("Date, Time, Speed ({}), Richtung (1>, 2<), Image".format(SPEED_UNIT))
else:
#Initial
csvfileout = folder + "/speed_{}.cvs".format(datetime.datetime.now().strftime("%Y%m%d"))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be .csv



if (last_speed > SAVE_IMAGE):
imageFilename = folder + "/motion_at_" + timestamp.strftime("%Y%m%d_%H%M%S") + ".jpg"
# use the following image file name if you want to be able to sort the images by speed
#imageFilename = "motion_at_%02.0f" % last_speed + "_" + datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + ".jpg"

cv2.imwrite(imageFilename,image)
if SAVE_CSV:
cap_time = datetime.datetime.now()
record_speed(cap_time.strftime("%Y.%m.%d")+','+cap_time.strftime('%A')+','+\
cap_time.strftime('%H%M')+','+("%.0f" % last_mph) + ','+imageFilename)
cv2.imwrite(imageFilename,image)
else:
imageFilename = "No image"
if (last_speed > SAVE_CSV):
record_speed(timestamp.strftime("%Y.%m.%d")+','+ timestamp.strftime('%H:%M')+','+("%.0f" % last_speed) + ',' + ("%d" % direction) + ','+imageFilename)
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_speed = speed
last_x = x
else:
if state != WAITING:
state = WAITING
direction = UNKNOWN
text_on_image = 'No Car Detected'
text_on_image = 'No motion detected'
print(text_on_image)

# only update image and wait for a keypress when waiting for a car
# only update image and wait for a keypress when waiting for a motion
# 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, (0, 255, 0), 1)
cv2.putText(image, datetime.datetime.now().strftime("%A %d %B %Y %H:%M:%S"),
(10, image.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 255, 127), 1)
cv2.putText(image, "Road Status: {}".format(text_on_image), (10, 20),
cv2.FONT_HERSHEY_SIMPLEX,0.35, (0, 0, 255), 1)
cv2.FONT_HERSHEY_SIMPLEX,0.5, (0, 255, 127), 1)

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))
cv2.line(image,(upper_left_x,upper_left_y),(upper_left_x,lower_right_y),(0, 255, 127))
cv2.line(image,(lower_right_x,upper_left_y),(lower_right_x,lower_right_y),(0, 255, 127))

# show the frame and check for a keypress
if SHOW_IMAGE:
Expand All @@ -356,4 +420,3 @@ def draw_rectangle(event,x,y,flags,param):

# cleanup the camera and close any open windows
cv2.destroyAllWindows()