-
Notifications
You must be signed in to change notification settings - Fork 2
/
canvas.py
167 lines (116 loc) · 5.23 KB
/
canvas.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import cv2
import numpy as np
import time
load_from_disk = True
if load_from_disk:
penval = np.load('penval.npy')
cap = cv2.VideoCapture(0)
cap.set(3,1280)
cap.set(4,720)
# Load these 2 images and resize them to the same size.
pen_img = cv2.resize(cv2.imread('pen.png',1), (50, 50))
eraser_img = cv2.resize(cv2.imread('eraser.jpg',1), (50, 50))
kernel = np.ones((5,5),np.uint8)
# Making window size adjustable
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
# This is the canvas on which we will draw upon
canvas = None
# Create a background subtractor Object
backgroundobject = cv2.createBackgroundSubtractorMOG2( detectShadows = False )
# This threshold determines the amount of disruption in background.
background_threshold = 600
# A variable which tells you if you're using a pen or an eraser.
switch = 'Pen'
# With this variable we will monitor the time between previous switch.
last_switch = time.time()
# Initilize x1,y1 points
x1,y1=0,0
# Threshold for noise
noiseth = 800
# Threshold for wiper, the size of the contour must be bigger than this for us to clear the canvas
wiper_thresh = 40000
# A varaible which tells when to clear canvas
clear = False
while(1):
_, frame = cap.read()
frame = cv2.flip(frame, 1 )
# Initilize the canvas as a black image
if canvas is None:
canvas = np.zeros_like(frame)
# Take the top left of the frame and apply the background subtractor there
top_left = frame[0: 50, 0: 50]
fgmask = backgroundobject.apply(top_left)
# Note the number of pixels that are white, this is the level of disruption.
switch_thresh = np.sum(fgmask==255)
# If the disruption is greater than background threshold and there has been some time after the previous switch then you
# can change the object type.
if switch_thresh > background_threshold and (time.time() - last_switch) > 1:
# Save the time of the switch.
last_switch = time.time()
if switch == 'Pen':
switch = 'Eraser'
else:
switch = 'Pen'
# Convert BGR to HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# If you're reading from memory then load the upper and lower ranges from there
if load_from_disk:
lower_range = penval[0]
upper_range = penval[1]
# Otherwise define your own custom values for upper and lower range.
else:
lower_range = np.array([26,80,147])
upper_range = np.array([81,255,255])
mask = cv2.inRange(hsv, lower_range, upper_range)
# Perform morphological operations to get rid of the noise
mask = cv2.erode(mask,kernel,iterations = 1)
mask = cv2.dilate(mask,kernel,iterations = 2)
# Find Contours
contours, hierarchy = cv2.findContours(mask,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# Make sure there is a contour present and also its size is bigger than noise threshold.
if contours and cv2.contourArea(max(contours, key = cv2.contourArea)) > noiseth:
c = max(contours, key = cv2.contourArea)
x2,y2,w,h = cv2.boundingRect(c)
# Get the area of the contour
area = cv2.contourArea(c)
# If there were no previous points then save the detected x2,y2 coordinates as x1,y1.
if x1 == 0 and y1 == 0:
x1,y1= x2,y2
else:
if switch == 'Pen':
# Draw the line on the canvas
canvas = cv2.line(canvas, (x1,y1), (x2,y2), [255,0,0], 5)
else:
cv2.circle(canvas, (x2, y2), 20, (0,0,0), -1)
# After the line is drawn the new points become the previous points.
x1,y1= x2,y2
# Now if the area is greater than the wiper threshold then set the clear variable to True
if area > wiper_thresh:
cv2.putText(canvas,'Clearing Canvas',(0,200), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 1, cv2.LINE_AA)
clear = True
else:
# If there were no contours detected then make x1,y1 = 0
x1,y1 =0,0
# Now this piece of code is just for smooth drawing. (Optional)
_,mask = cv2.threshold(cv2.cvtColor(canvas, cv2.COLOR_BGR2GRAY), 20, 255, cv2.THRESH_BINARY)
foreground = cv2.bitwise_and(canvas, canvas, mask = mask)
background = cv2.bitwise_and(frame, frame, mask = cv2.bitwise_not(mask))
frame = cv2.add(foreground,background)
# Switch the images depending upon what we're using, pen or eraser.
if switch != 'Pen':
cv2.circle(frame, (x1, y1), 20, (255,255,255), -1)
frame[0: 50, 0: 50] = eraser_img
else:
frame[0: 50, 0: 50] = pen_img
cv2.imshow('Airboard by Codercube',frame)
k = cv2.waitKey(5) & 0xFF
if k == 27:
break
# Clear the canvas after 1 second, if the clear variable is true
if clear == True:
time.sleep(1)
canvas = None
# And then set clear to false
clear = False
cv2.destroyAllWindows()
cap.release()