
import cv2 as cv
import datetime
from HSL2BGRconvert import HSL2BGR2
import serial
import time
#Cv window setup
nameFeed = 'Live feed'
nameThresh = 'Threshold'
nameColours = 'Output test'
nameGray = 'Gray feed'
colourImg = cv.imread('colours.jpg')
#Camera settings
dthresh = 2 #parameters for motion detection
minarea = 800
maxarea = 200000
cam = cv.VideoCapture(0)
avg = None
threshavg = None
#Colour setup
nlights = 5
coloursHSL = [[0,0,0] for i in range(nlights)] #matrix for HSL parameters / fixture
coloursBGR = [[0,0,0] for i in range(nlights)] #matrix for BGR parameters / fixture
coloursBGR_old = [[0,0,0] for i in range(nlights)] #used to determine if BGR values have changed (reduces serial bandwidth for dmx)
ncolours = 3 #resolution for colour detection (vertical)
basecolour = 200 #colour which the theremin converges to
sensitivity_hue = 400000 #sensitivity of motion detection
sensitivity_brightness = 70000 #sensitivity of motion detection
detect_value = [[0]*ncolours for i in range(nlights)] #matrix to hold integral sum of detection
detect_total = [0]*nlights #list to hold total integral / fixture
detect_colour = [[basecolour,100,50] for i in range(nlights)] #matrix to hold HSL colour values calculated by detection
print 'detect_colour start:', detect_colour
mode = 'theremin' #starting mode (currently; theremin, rainbow, light_fade, still)
mode_motion = 1 #type of motion detection interpretation
start = True
delay_program = 0.001 #controls speed of program
delay_display = 0.000 #controls the display update frequency
delay_dmx = 0.01 #controls the dmx frequency
next_update = datetime.datetime.now()
next_display = datetime.datetime.now()
next_dmx = datetime.datetime.now()
#Output setting
display = True
dmx_send = True
showIntegral = True
calculate_contours = False
detect_brightness = False
#DMX setup
if dmx_send == True:
dmxChannels = [[3,2,1],[6,5,4],[9,8,7],[12,11,10],[15,14,13]]
port = "COM4"
baud = 9600
ser = serial.Serial(port, baud)
print '[THEREMIN] Parameters checked'
# A small function to make writing DMX signals quicker
def DMX(channel,value):
ser.write(str(channel) + ',' + str(value) + 'x')
#cv.namedWindow(nameFeed, cv.CV_WINDOW_AUTOSIZE)
#cv.namedWindow(nameGray, cv.CV_WINDOW_AUTOSIZE)
cv.namedWindow(nameThresh, cv.CV_WINDOW_AUTOSIZE)
cv.namedWindow(nameColours, cv.CV_WINDOW_AUTOSIZE)
time.sleep(0.5)
print '[THEREMIN] Entering continuous loop...'
while True:
timestart = datetime.datetime.now()
ret_val, img = cam.read()
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
gray = cv.GaussianBlur(gray, (51, 51), 0)
if avg == None:
avg = gray.copy().astype("float")
frameDelta = cv.absdiff(gray, cv.convertScaleAbs(avg))
#threshold the delta image, maybe dilate the thresholded image to fill in holes, then find contours on thresholded image
#thresh = cv.threshold(frameDelta, dthresh, 255, cv.THRESH_BINARY)[1]
thresh = cv.threshold(frameDelta, dthresh, 255, cv.THRESH_TOZERO)[1]
#thresh = cv.dilate(thresh, None, iterations=1)
#smooth out threshold image
if threshavg == None:
threshavg = thresh.copy().astype("float")
cv.accumulateWeighted(thresh, threshavg, 0.1 )
if calculate_contours == True:
(cnts, _) = cv.findContours(thresh.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
#analyse all areas
for c in cnts:
#if the contour is too small, ignore it
if cv.contourArea(c) < minarea or cv.contourArea(c) > maxarea:
continue
#compute the bounding box for the contour, draw it on the frame, update compound centre and update the text
(x, y, w, h) = cv.boundingRect(c)
cv.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
motion = True
if mode == 'theremin':
time = datetime.datetime.now()
if time > next_update:
next_update = datetime.datetime.now() + datetime.timedelta(seconds=delay_program)
#integral image
for c in range(0, nlights):
detect_total[c] = 0
for r in range(0,ncolours):
roi = threshavg[(r * thresh.shape[0] / ncolours):((r+1) * thresh.shape[0] / ncolours), (c * thresh.shape[1] / nlights):((c+1) * thresh.shape[1] / nlights)]
sumint = cv.integral(roi)
subtotal = int(sumint[sumint.shape[0]-1,sumint.shape[1]-1])
#print c, r, total
detect_value[c][r] = subtotal
detect_total[c] = detect_total[c] + subtotal
if mode_motion == 1: #motion controls hue change
for c in range(0, nlights):
detect_colour[c] = [basecolour,100,50]
for r in range(0,ncolours):
if detect_total[c] <> 0:
detect_colour[c][0] = detect_colour[c][0] - r * 360 * detect_value[c][r] / (detect_total[c] + sensitivity_hue)
detect_colour[c][0] = int((0+detect_colour[c][0])%360)
#reset the colours to base colours
if detect_colour[c][0] > (basecolour + 0):
detect_colour[c][0] = detect_colour[c][0] - 13
elif detect_colour[c][0] < (basecolour - 0):
detect_colour[c][0] = detect_colour[c][0] + 24
#print c, detect_colour[c][0]
elif mode_motion == 2: #motion directly controls hue
for c in range(0, nlights):
for r in range(0,ncolours):
target = 0
if detect_total[c] <> 0:
#print detect_colour, detect_total[c], detect_value[c][0]
target = target + int((r*r) *256 * detect_value[c][r] / detect_total[c])
print c, target
if detect_colour[c][0] - target < 0:
detect_colour[c][0] = detect_colour[c][0] + 2
elif detect_colour[c][0] - target > 0:
detect_colour[c][0] = detect_colour[c][0] - 2
if detect_brightness == True: #motion directly controles hue
for c in range(0, nlights):
#detect_colour[c] = [0,100,50]
for r in range(0,ncolours):
if detect_total[c] <> 0:
detect_colour[c][2] = 25 + (r) * 1 * detect_value[c][r] / sensitivity_brightness
detect_colour[c][2] = int(min(detect_colour[c][2],75))
if detect_colour[c][2] > 0:
detect_colour[c][2] = detect_colour[c][2] - 1
#print the integral value in the feed
if showIntegral == True:
for c in range(0,nlights):
for r in range(0,ncolours):
cv.putText(thresh, str(detect_value[c][r]), (int(c*thresh.shape[1]/nlights+10), int((r+1)*(thresh.shape[0]-40)/ncolours) - 10), cv.FONT_HERSHEY_SIMPLEX, 0.35, (125, 125, 125), 1)
#Video output
#cv.imshow(nameFeed, img)
#cv.imshow(nameGray, gray)
cv.imshow(nameThresh, thresh)
cv.accumulateWeighted(gray, avg, 0.5)
#different modes
if mode == 'theremin':
for n in range(0,len(detect_colour)):
coloursBGR[n] = HSL2BGR2(detect_colour[n])
if mode == 'rainbow':
if start == True:
print 'Starting rainbow...'
coloursHSL = [[20*i,100,50] for i in range(nlights)]
print coloursHSL
start = False
time = datetime.datetime.now()
if time > next_update:
for n in range(0,nlights):
coloursHSL[n][0] = (coloursHSL[n][0] + 5)%360
coloursBGR[n] = HSL2BGR2(coloursHSL[n])
next_update = time + datetime.timedelta(seconds=delay_program)
elif mode == 'light fade':
if start == True:
coloursHSL = [[(50*i)%360,100,50] for i in range(nlights)]
start = False
time = datetime.datetime.now()
if time.now() > next_update:
for n in range(0,nlights):
coloursHSL[n][2] = (coloursHSL[n][2] + 1)%100
coloursBGR[n] = HSL2BGR2(coloursHSL[n])
next_update = time + datetime.timedelta(seconds=delay_program)
elif mode == 'still':
if start == True:
coloursHSL = [[basecolour,100,50] for i in range(nlights)]
start = False
time = datetime.datetime.now()
if time > next_update:
for n in range(0,len(coloursHSL)):
coloursBGR[n] = HSL2BGR2(coloursHSL[n])
next_update = time + datetime.timedelta(seconds=delay_program)
#draw and display the output test
if display == True:
time = datetime.datetime.now()
if time > next_display:
for i in range(0, len(coloursBGR)):
cv.rectangle(colourImg, (i*100,0), ((i+1)*100,100), coloursBGR[i], -1)
cv.imshow(nameColours, colourImg)
next_display = time + datetime.timedelta(seconds=delay_display)
#send dmx-signal
if dmx_send == True:
time = datetime.datetime.now()
if time > next_dmx:
for i in range(0, len(coloursBGR)):
for k in range(0, 3):
if coloursBGR[i][k] != coloursBGR_old[i][k]:
DMX(dmxChannels[i][k],coloursBGR[i][k])
coloursBGR_old[i][k] = coloursBGR[i][k]
next_dmx = time + datetime.timedelta(seconds=delay_dmx)
key = cv.waitKey(5)
if key == ord('q'): #quit
print('[THEREMIN] Logging off...')
cv.destroyAllWindows()
break
elif key == ord('z'): #rainbow
mode = 'rainbow'
start = True
elif key == ord('x'): #light fade
mode = 'light fade'
start = True
elif key == ord('c'): #still (basecolour)
mode = 'still'
start = True
elif key == ord('v'): #theremin
mode = 'theremin'
start = True
elif key == 91:
delay_program = delay_program + 0.001
print "program delay:", delay_program
elif key == 93:
delay_program = delay_program - 0.001
if delay_program < 0.001:
delay_program = 0.001
print "program delay:", delay_program
elif key == 44:
sensitivity_hue = sensitivity_hue + 100000
print "hue sensitivity:", sensitivity_hue
elif key == 46:
sensitivity_hue = sensitivity_hue - 100000
if sensitivity_hue < 100000:
sensitivity_hue = 100000
print "hue sensitivity:", sensitivity_hue
elif key == 107:
sensitivity_brightness = sensitivity_brightness + 10000
print "brightness_sensitivity:", sensitivity_brightness
elif key == 108:
sensitivity_brightness = sensitivity_brightness - 10000
if sensitivity_brightness < 10000:
sensitivity_brightness = 10000
print "brightness sensitivity:", sensitivity_brightness