├── README.md ├── LICENSE └── motion_detection.py /README.md: -------------------------------------------------------------------------------- 1 | # Python Tutorial - || Motion Detection System using cv2 || Code Walk-through || 2 | *#python* *#3* *#python3* *#motion* *#detection* *#OpenCV* *#open* *#cv* *#cv2* *#library* *#camera* *#system* *#detect* *#web* *#cam* 3 | *#webcam* *#raspberry* *#pi* *#security* *#feed* *#cctv* *#using* *#how* *#to* *#tutorial* *#learn* 4 | 5 | **Imutils GitHub Library Link: https://bit.ly/2B6ltAG** 6 | 7 | ## Code Walk-through & Learning resources links 8 | **YouTube tutorial: https://bit.ly/2DF6fV3** 9 | 10 | **For python tutorials: https://bit.ly/2U58Lt9** 11 | 12 | **Python_AND_Hacking subreddit: https://bit.ly/2Uf3gbw** 13 | 14 | ## If you would like to leave a tip you can do so below, thanks 15 | 16 | 17 | 18 | ## Built With 19 | 20 | * **Python 3.6.5** - [https://www.python.org/](https://www.python.org/) 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 w3w3w3 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /motion_detection.py: -------------------------------------------------------------------------------- 1 | #// Don't forget to hit SUBSCRIBE, LIKE, COMMENT, and LEARN! its good to learn :) 2 | 3 | #imports 4 | import cv2 5 | import time 6 | import datetime 7 | import imutils 8 | 9 | 10 | def motion_detection(): 11 | video_capture = cv2.VideoCapture(0) # value (0) selects the devices default camera 12 | time.sleep(2) 13 | 14 | first_frame = None # instinate the first fame 15 | 16 | while True: 17 | frame = video_capture.read()[1] # gives 2 outputs retval,frame - [1] selects frame 18 | text = 'Unoccupied' 19 | 20 | greyscale_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # make each frame greyscale wich is needed for threshold 21 | 22 | gaussian_frame = cv2.GaussianBlur(greyscale_frame, (21,21),0) 23 | # uses a kernal of size(21,21) // has to be odd number to to ensure there is a valid integer in the centre 24 | # and we need to specify the standerd devation in x and y direction wich is the (0) if only x(sigma x) is specified 25 | # then y(sigma y) is taken as same as x. sigma = standerd deveation(mathmetics term) 26 | 27 | blur_frame = cv2.blur(gaussian_frame, (5,5)) # uses a kernal of size(5,5)(width,height) wich goes over 5x5 pixel area left to right 28 | # does a calculation and the pixel located in the centre of the kernal will become 29 | # a new value(the sum of the kernal after the calculations) and then it moves to the right one and has a new centre pixel 30 | # and does it all over again..untill the image is done, obv this can cause the edges to not be changed, but is very minute 31 | 32 | greyscale_image = blur_frame 33 | # greyscale image with blur etc wich is the final image ready to be used for threshold and motion detecion 34 | 35 | if first_frame is None: 36 | first_frame = greyscale_image 37 | # first frame is set for background subtraction(BS) using absdiff and then using threshold to get the foreground mask 38 | # foreground mask (black background anything that wasnt in image in first frame but is in newframe over the threshold will 39 | # be a white pixel(white) foreground image is black with new object being white...there is your motion detection 40 | else: 41 | pass 42 | 43 | 44 | frame = imutils.resize(frame, width=500) 45 | frame_delta = cv2.absdiff(first_frame, greyscale_image) 46 | # calculates the absolute diffrence between each element/pixel between the two images, first_frame - greyscale (on each element) 47 | 48 | # edit the ** thresh ** depending on the light/dark in room, change the 100(anything pixel value over 100 will become 255(white)) 49 | thresh = cv2.threshold(frame_delta, 100, 255, cv2.THRESH_BINARY)[1] 50 | # threshold gives two outputs retval,threshold image. using [1] on the end i am selecting the threshold image that is produced 51 | 52 | dilate_image = cv2.dilate(thresh, None, iterations=2) 53 | # dilate = dilate,grow,expand - the effect on a binary image(black background and white foregorund) is to enlarge/expand the white 54 | # pixels in the foreground wich are white(255), element=Mat() = default 3x3 kernal matrix and iterartions=2 means it 55 | # will do it twice 56 | 57 | cnt = cv2.findContours(dilate_image.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1] 58 | # contours gives 3 diffrent ouputs image, contours and hierarchy, so using [1] on end means contours = [1](cnt) 59 | # cv2.CHAIN_APPROX_SIMPLE saves memory by removing all redundent points and comppressing the contour, if you have a rectangle 60 | # with 4 straight lines you dont need to plot each point along the line, you only need to plot the corners of the rectangle 61 | # and then join the lines, eg instead of having say 750 points, you have 4 points.... look at the memory you save! 62 | 63 | for c in cnt: 64 | if cv2.contourArea(c) > 800: # if contour area is less then 800 non-zero(not-black) pixels(white) 65 | (x, y, w, h) = cv2.boundingRect(c) # x,y are the top left of the contour and w,h are the width and hieght 66 | 67 | cv2.rectangle(frame, (x,y), (x+w, y+h), (0, 255, 0), 2) # (0, 255, 0) = color R,G,B = lime / 2 = thickness(i think?)(YES IM RITE!) 68 | # image used for rectangle is frame so that its on the secruity feed image and not the binary/threshold/foreground image 69 | # as its already used the threshold/(binary image) to find the contours this image/frame is what image it will be drawed on 70 | 71 | text = 'Occupied' 72 | # text that appears when there is motion in video feed 73 | else: 74 | pass 75 | 76 | 77 | ''' now draw text and timestamp on security feed ''' 78 | font = cv2.FONT_HERSHEY_SIMPLEX 79 | 80 | cv2.putText(frame, '{+} Room Status: %s' % (text), 81 | (10,20), cv2.FONT_HERSHEY_SIMPLEX , 0.5, (0, 0, 255), 2) 82 | # frame is the image on wich the text will go. 0.5 is size of font, (0,0,255) is R,G,B color of font, 2 on end is LINE THICKNESS! OK :) 83 | 84 | 85 | cv2.putText(frame, datetime.datetime.now().strftime('%A %d %B %Y %I:%M:%S%p'), 86 | (10, frame.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX , 0.35, (0, 0, 255),1) # frame.shape[0] = hieght, frame.shape[1] = width,ssssssssssssss 87 | # using datetime to get date/time stamp, for font positions using frame.shape() wich returns a tuple of (rows,columns,channels) 88 | # going 10 accross in rows/width so need columns with frame.shape()[0] we are selecting columns so how ever many pixel height 89 | # the image is - 10 so oppisite end at bottom instead of being at the top like the other text 90 | 91 | cv2.imshow('Security Feed', frame) 92 | cv2.imshow('Threshold(foreground mask)', dilate_image) 93 | cv2.imshow('Frame_delta', frame_delta) 94 | 95 | key = cv2.waitKey(1) & 0xFF # (1) = time delay in seconds before execution, and 0xFF takes the last 8 bit to check value or sumin 96 | if key == ord('q'): 97 | cv2.destroyAllWindows() 98 | break 99 | 100 | 101 | 102 | if __name__=='__main__': 103 | motion_detection() 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | --------------------------------------------------------------------------------