├── LICENSE ├── Object_detection_picamera.py ├── Pet_detector.py ├── README.md └── doc ├── Install_TF_RPi.jpg ├── PetDetector_Video.PNG ├── Picamera_livingroom.png ├── YouTube_video.png ├── bashrc.png ├── camera_enabled.png ├── cards.png ├── directory.png ├── edited_script.png ├── kitchen.png ├── pet_detector_demo.png └── update.png /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Object_detection_picamera.py: -------------------------------------------------------------------------------- 1 | ######## Picamera Object Detection Using Tensorflow Classifier ######### 2 | # 3 | # Author: Evan Juras 4 | # Date: 4/15/18 5 | # Description: 6 | # This program uses a TensorFlow classifier to perform object detection. 7 | # It loads the classifier uses it to perform object detection on a Picamera feed. 8 | # It draws boxes and scores around the objects of interest in each frame from 9 | # the Picamera. It also can be used with a webcam by adding "--usbcam" 10 | # when executing this script from the terminal. 11 | 12 | ## Some of the code is copied from Google's example at 13 | ## https://github.com/tensorflow/models/blob/master/research/object_detection/object_detection_tutorial.ipynb 14 | 15 | ## and some is copied from Dat Tran's example at 16 | ## https://github.com/datitran/object_detector_app/blob/master/object_detection_app.py 17 | 18 | ## but I changed it to make it more understandable to me. 19 | 20 | 21 | # Import packages 22 | import os 23 | import cv2 24 | import numpy as np 25 | from picamera.array import PiRGBArray 26 | from picamera import PiCamera 27 | import tensorflow as tf 28 | import argparse 29 | import sys 30 | 31 | # Set up camera constants 32 | IM_WIDTH = 1280 33 | IM_HEIGHT = 720 34 | #IM_WIDTH = 640 Use smaller resolution for 35 | #IM_HEIGHT = 480 slightly faster framerate 36 | 37 | # Select camera type (if user enters --usbcam when calling this script, 38 | # a USB webcam will be used) 39 | camera_type = 'picamera' 40 | parser = argparse.ArgumentParser() 41 | parser.add_argument('--usbcam', help='Use a USB webcam instead of picamera', 42 | action='store_true') 43 | args = parser.parse_args() 44 | if args.usbcam: 45 | camera_type = 'usb' 46 | 47 | # This is needed since the working directory is the object_detection folder. 48 | sys.path.append('..') 49 | 50 | # Import utilites 51 | from utils import label_map_util 52 | from utils import visualization_utils as vis_util 53 | 54 | # Name of the directory containing the object detection module we're using 55 | MODEL_NAME = 'ssdlite_mobilenet_v2_coco_2018_05_09' 56 | 57 | # Grab path to current working directory 58 | CWD_PATH = os.getcwd() 59 | 60 | # Path to frozen detection graph .pb file, which contains the model that is used 61 | # for object detection. 62 | PATH_TO_CKPT = os.path.join(CWD_PATH,MODEL_NAME,'frozen_inference_graph.pb') 63 | 64 | # Path to label map file 65 | PATH_TO_LABELS = os.path.join(CWD_PATH,'data','mscoco_label_map.pbtxt') 66 | 67 | # Number of classes the object detector can identify 68 | NUM_CLASSES = 90 69 | 70 | ## Load the label map. 71 | # Label maps map indices to category names, so that when the convolution 72 | # network predicts `5`, we know that this corresponds to `airplane`. 73 | # Here we use internal utility functions, but anything that returns a 74 | # dictionary mapping integers to appropriate string labels would be fine 75 | label_map = label_map_util.load_labelmap(PATH_TO_LABELS) 76 | categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=NUM_CLASSES, use_display_name=True) 77 | category_index = label_map_util.create_category_index(categories) 78 | 79 | # Load the Tensorflow model into memory. 80 | detection_graph = tf.Graph() 81 | with detection_graph.as_default(): 82 | od_graph_def = tf.GraphDef() 83 | with tf.gfile.GFile(PATH_TO_CKPT, 'rb') as fid: 84 | serialized_graph = fid.read() 85 | od_graph_def.ParseFromString(serialized_graph) 86 | tf.import_graph_def(od_graph_def, name='') 87 | 88 | sess = tf.Session(graph=detection_graph) 89 | 90 | 91 | # Define input and output tensors (i.e. data) for the object detection classifier 92 | 93 | # Input tensor is the image 94 | image_tensor = detection_graph.get_tensor_by_name('image_tensor:0') 95 | 96 | # Output tensors are the detection boxes, scores, and classes 97 | # Each box represents a part of the image where a particular object was detected 98 | detection_boxes = detection_graph.get_tensor_by_name('detection_boxes:0') 99 | 100 | # Each score represents level of confidence for each of the objects. 101 | # The score is shown on the result image, together with the class label. 102 | detection_scores = detection_graph.get_tensor_by_name('detection_scores:0') 103 | detection_classes = detection_graph.get_tensor_by_name('detection_classes:0') 104 | 105 | # Number of objects detected 106 | num_detections = detection_graph.get_tensor_by_name('num_detections:0') 107 | 108 | # Initialize frame rate calculation 109 | frame_rate_calc = 1 110 | freq = cv2.getTickFrequency() 111 | font = cv2.FONT_HERSHEY_SIMPLEX 112 | 113 | # Initialize camera and perform object detection. 114 | # The camera has to be set up and used differently depending on if it's a 115 | # Picamera or USB webcam. 116 | 117 | # I know this is ugly, but I basically copy+pasted the code for the object 118 | # detection loop twice, and made one work for Picamera and the other work 119 | # for USB. 120 | 121 | ### Picamera ### 122 | if camera_type == 'picamera': 123 | # Initialize Picamera and grab reference to the raw capture 124 | camera = PiCamera() 125 | camera.resolution = (IM_WIDTH,IM_HEIGHT) 126 | camera.framerate = 10 127 | rawCapture = PiRGBArray(camera, size=(IM_WIDTH,IM_HEIGHT)) 128 | rawCapture.truncate(0) 129 | 130 | for frame1 in camera.capture_continuous(rawCapture, format="bgr",use_video_port=True): 131 | 132 | t1 = cv2.getTickCount() 133 | 134 | # Acquire frame and expand frame dimensions to have shape: [1, None, None, 3] 135 | # i.e. a single-column array, where each item in the column has the pixel RGB value 136 | frame = np.copy(frame1.array) 137 | frame.setflags(write=1) 138 | frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) 139 | frame_expanded = np.expand_dims(frame_rgb, axis=0) 140 | 141 | # Perform the actual detection by running the model with the image as input 142 | (boxes, scores, classes, num) = sess.run( 143 | [detection_boxes, detection_scores, detection_classes, num_detections], 144 | feed_dict={image_tensor: frame_expanded}) 145 | 146 | # Draw the results of the detection (aka 'visulaize the results') 147 | vis_util.visualize_boxes_and_labels_on_image_array( 148 | frame, 149 | np.squeeze(boxes), 150 | np.squeeze(classes).astype(np.int32), 151 | np.squeeze(scores), 152 | category_index, 153 | use_normalized_coordinates=True, 154 | line_thickness=8, 155 | min_score_thresh=0.40) 156 | 157 | cv2.putText(frame,"FPS: {0:.2f}".format(frame_rate_calc),(30,50),font,1,(255,255,0),2,cv2.LINE_AA) 158 | 159 | # All the results have been drawn on the frame, so it's time to display it. 160 | cv2.imshow('Object detector', frame) 161 | 162 | t2 = cv2.getTickCount() 163 | time1 = (t2-t1)/freq 164 | frame_rate_calc = 1/time1 165 | 166 | # Press 'q' to quit 167 | if cv2.waitKey(1) == ord('q'): 168 | break 169 | 170 | rawCapture.truncate(0) 171 | 172 | camera.close() 173 | 174 | ### USB webcam ### 175 | elif camera_type == 'usb': 176 | # Initialize USB webcam feed 177 | camera = cv2.VideoCapture(0) 178 | ret = camera.set(3,IM_WIDTH) 179 | ret = camera.set(4,IM_HEIGHT) 180 | 181 | while(True): 182 | 183 | t1 = cv2.getTickCount() 184 | 185 | # Acquire frame and expand frame dimensions to have shape: [1, None, None, 3] 186 | # i.e. a single-column array, where each item in the column has the pixel RGB value 187 | ret, frame = camera.read() 188 | frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) 189 | frame_expanded = np.expand_dims(frame_rgb, axis=0) 190 | 191 | # Perform the actual detection by running the model with the image as input 192 | (boxes, scores, classes, num) = sess.run( 193 | [detection_boxes, detection_scores, detection_classes, num_detections], 194 | feed_dict={image_tensor: frame_expanded}) 195 | 196 | # Draw the results of the detection (aka 'visulaize the results') 197 | vis_util.visualize_boxes_and_labels_on_image_array( 198 | frame, 199 | np.squeeze(boxes), 200 | np.squeeze(classes).astype(np.int32), 201 | np.squeeze(scores), 202 | category_index, 203 | use_normalized_coordinates=True, 204 | line_thickness=8, 205 | min_score_thresh=0.85) 206 | 207 | cv2.putText(frame,"FPS: {0:.2f}".format(frame_rate_calc),(30,50),font,1,(255,255,0),2,cv2.LINE_AA) 208 | 209 | # All the results have been drawn on the frame, so it's time to display it. 210 | cv2.imshow('Object detector', frame) 211 | 212 | t2 = cv2.getTickCount() 213 | time1 = (t2-t1)/freq 214 | frame_rate_calc = 1/time1 215 | 216 | # Press 'q' to quit 217 | if cv2.waitKey(1) == ord('q'): 218 | break 219 | 220 | camera.release() 221 | 222 | cv2.destroyAllWindows() 223 | 224 | -------------------------------------------------------------------------------- /Pet_detector.py: -------------------------------------------------------------------------------- 1 | ######## Raspberry Pi Pet Detector Camera using TensorFlow Object Detection API ######### 2 | # 3 | # Author: Evan Juras 4 | # Date: 10/15/18 5 | # Description: 6 | # 7 | # This script implements a "pet detector" that alerts the user if a pet is 8 | # waiting to be let inside or outside. It takes video frames from a Picamera 9 | # or USB webcam, passes them through a TensorFlow object detection model, 10 | # determines if a cat or dog has been detected in the image, checks the location 11 | # of the cat or dog in the frame, and texts the user's phone if a cat or dog is 12 | # detected in the appropriate location. 13 | # 14 | # The framework is based off the Object_detection_picamera.py script located here: 15 | # https://github.com/EdjeElectronics/TensorFlow-Object-Detection-on-the-Raspberry-Pi/blob/master/Object_detection_picamera.py 16 | # 17 | # Sending a text requires setting up a Twilio account (free trials are available). 18 | # Here is a good tutorial for using Twilio: 19 | # https://www.twilio.com/docs/sms/quickstart/python 20 | 21 | 22 | # Import packages 23 | import os 24 | import cv2 25 | import numpy as np 26 | from picamera.array import PiRGBArray 27 | from picamera import PiCamera 28 | import tensorflow as tf 29 | import argparse 30 | import sys 31 | 32 | # Set up Twilio 33 | from twilio.rest import Client 34 | 35 | # Twilio SID, authentication token, my phone number, and the Twilio phone number 36 | # are stored as environment variables on my Pi so people can't see them 37 | account_sid = os.environ['TWILIO_ACCOUNT_SID'] 38 | auth_token = os.environ['TWILIO_AUTH_TOKEN'] 39 | my_number = os.environ['MY_DIGITS'] 40 | twilio_number = os.environ['TWILIO_DIGITS'] 41 | 42 | client = Client(account_sid,auth_token) 43 | 44 | # Set up camera constants 45 | IM_WIDTH = 1280 46 | IM_HEIGHT = 720 47 | 48 | # Select camera type (if user enters --usbcam when calling this script, 49 | # a USB webcam will be used) 50 | camera_type = 'picamera' 51 | parser = argparse.ArgumentParser() 52 | parser.add_argument('--usbcam', help='Use a USB webcam instead of picamera', 53 | action='store_true') 54 | args = parser.parse_args() 55 | if args.usbcam: 56 | camera_type = 'usb' 57 | 58 | #### Initialize TensorFlow model #### 59 | 60 | # This is needed since the working directory is the object_detection folder. 61 | sys.path.append('..') 62 | 63 | # Import utilites 64 | from utils import label_map_util 65 | from utils import visualization_utils as vis_util 66 | 67 | # Name of the directory containing the object detection module we're using 68 | MODEL_NAME = 'ssdlite_mobilenet_v2_coco_2018_05_09' 69 | 70 | # Grab path to current working directory 71 | CWD_PATH = os.getcwd() 72 | 73 | # Path to frozen detection graph .pb file, which contains the model that is used 74 | # for object detection. 75 | PATH_TO_CKPT = os.path.join(CWD_PATH,MODEL_NAME,'frozen_inference_graph.pb') 76 | 77 | # Path to label map file 78 | PATH_TO_LABELS = os.path.join(CWD_PATH,'data','mscoco_label_map.pbtxt') 79 | 80 | # Number of classes the object detector can identify 81 | NUM_CLASSES = 90 82 | 83 | ## Load the label map. 84 | # Label maps map indices to category names, so that when the convolution 85 | # network predicts `5`, we know that this corresponds to `airplane`. 86 | # Here we use internal utility functions, but anything that returns a 87 | # dictionary mapping integers to appropriate string labels would be fine 88 | label_map = label_map_util.load_labelmap(PATH_TO_LABELS) 89 | categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=NUM_CLASSES, use_display_name=True) 90 | category_index = label_map_util.create_category_index(categories) 91 | 92 | # Load the Tensorflow model into memory. 93 | detection_graph = tf.Graph() 94 | with detection_graph.as_default(): 95 | od_graph_def = tf.GraphDef() 96 | with tf.gfile.GFile(PATH_TO_CKPT, 'rb') as fid: 97 | serialized_graph = fid.read() 98 | od_graph_def.ParseFromString(serialized_graph) 99 | tf.import_graph_def(od_graph_def, name='') 100 | 101 | sess = tf.Session(graph=detection_graph) 102 | 103 | 104 | # Define input and output tensors (i.e. data) for the object detection classifier 105 | 106 | # Input tensor is the image 107 | image_tensor = detection_graph.get_tensor_by_name('image_tensor:0') 108 | 109 | # Output tensors are the detection boxes, scores, and classes 110 | # Each box represents a part of the image where a particular object was detected 111 | detection_boxes = detection_graph.get_tensor_by_name('detection_boxes:0') 112 | 113 | # Each score represents level of confidence for each of the objects. 114 | # The score is shown on the result image, together with the class label. 115 | detection_scores = detection_graph.get_tensor_by_name('detection_scores:0') 116 | detection_classes = detection_graph.get_tensor_by_name('detection_classes:0') 117 | 118 | # Number of objects detected 119 | num_detections = detection_graph.get_tensor_by_name('num_detections:0') 120 | 121 | #### Initialize other parameters #### 122 | 123 | # Initialize frame rate calculation 124 | frame_rate_calc = 1 125 | freq = cv2.getTickFrequency() 126 | font = cv2.FONT_HERSHEY_SIMPLEX 127 | 128 | # Define inside box coordinates (top left and bottom right) 129 | TL_inside = (int(IM_WIDTH*0.1),int(IM_HEIGHT*0.35)) 130 | BR_inside = (int(IM_WIDTH*0.45),int(IM_HEIGHT-5)) 131 | 132 | # Define outside box coordinates (top left and bottom right) 133 | TL_outside = (int(IM_WIDTH*0.46),int(IM_HEIGHT*0.25)) 134 | BR_outside = (int(IM_WIDTH*0.8),int(IM_HEIGHT*.85)) 135 | 136 | # Initialize control variables used for pet detector 137 | detected_inside = False 138 | detected_outside = False 139 | 140 | inside_counter = 0 141 | outside_counter = 0 142 | 143 | pause = 0 144 | pause_counter = 0 145 | 146 | #### Pet detection function #### 147 | 148 | # This function contains the code to detect a pet, determine if it's 149 | # inside or outside, and send a text to the user's phone. 150 | def pet_detector(frame): 151 | 152 | # Use globals for the control variables so they retain their value after function exits 153 | global detected_inside, detected_outside 154 | global inside_counter, outside_counter 155 | global pause, pause_counter 156 | 157 | frame_expanded = np.expand_dims(frame, axis=0) 158 | 159 | # Perform the actual detection by running the model with the image as input 160 | (boxes, scores, classes, num) = sess.run( 161 | [detection_boxes, detection_scores, detection_classes, num_detections], 162 | feed_dict={image_tensor: frame_expanded}) 163 | 164 | # Draw the results of the detection (aka 'visulaize the results') 165 | vis_util.visualize_boxes_and_labels_on_image_array( 166 | frame, 167 | np.squeeze(boxes), 168 | np.squeeze(classes).astype(np.int32), 169 | np.squeeze(scores), 170 | category_index, 171 | use_normalized_coordinates=True, 172 | line_thickness=8, 173 | min_score_thresh=0.40) 174 | 175 | # Draw boxes defining "outside" and "inside" locations. 176 | cv2.rectangle(frame,TL_outside,BR_outside,(255,20,20),3) 177 | cv2.putText(frame,"Outside box",(TL_outside[0]+10,TL_outside[1]-10),font,1,(255,20,255),3,cv2.LINE_AA) 178 | cv2.rectangle(frame,TL_inside,BR_inside,(20,20,255),3) 179 | cv2.putText(frame,"Inside box",(TL_inside[0]+10,TL_inside[1]-10),font,1,(20,255,255),3,cv2.LINE_AA) 180 | 181 | # Check the class of the top detected object by looking at classes[0][0]. 182 | # If the top detected object is a cat (17) or a dog (18) (or a teddy bear (88) for test purposes), 183 | # find its center coordinates by looking at the boxes[0][0] variable. 184 | # boxes[0][0] variable holds coordinates of detected objects as (ymin, xmin, ymax, xmax) 185 | if (((int(classes[0][0]) == 17) or (int(classes[0][0] == 18) or (int(classes[0][0]) == 88))) and (pause == 0)): 186 | x = int(((boxes[0][0][1]+boxes[0][0][3])/2)*IM_WIDTH) 187 | y = int(((boxes[0][0][0]+boxes[0][0][2])/2)*IM_HEIGHT) 188 | 189 | # Draw a circle at center of object 190 | cv2.circle(frame,(x,y), 5, (75,13,180), -1) 191 | 192 | # If object is in inside box, increment inside counter variable 193 | if ((x > TL_inside[0]) and (x < BR_inside[0]) and (y > TL_inside[1]) and (y < BR_inside[1])): 194 | inside_counter = inside_counter + 1 195 | 196 | # If object is in outside box, increment outside counter variable 197 | if ((x > TL_outside[0]) and (x < BR_outside[0]) and (y > TL_outside[1]) and (y < BR_outside[1])): 198 | outside_counter = outside_counter + 1 199 | 200 | # If pet has been detected inside for more than 10 frames, set detected_inside flag 201 | # and send a text to the phone. 202 | if inside_counter > 10: 203 | detected_inside = True 204 | message = client.messages.create( 205 | body = 'Your pet wants outside!', 206 | from_=twilio_number, 207 | to=my_number 208 | ) 209 | inside_counter = 0 210 | outside_counter = 0 211 | # Pause pet detection by setting "pause" flag 212 | pause = 1 213 | 214 | # If pet has been detected outside for more than 10 frames, set detected_outside flag 215 | # and send a text to the phone. 216 | if outside_counter > 10: 217 | detected_outside = True 218 | message = client.messages.create( 219 | body = 'Your pet wants inside!', 220 | from_=twilio_number, 221 | to=my_number 222 | ) 223 | inside_counter = 0 224 | outside_counter = 0 225 | # Pause pet detection by setting "pause" flag 226 | pause = 1 227 | 228 | # If pause flag is set, draw message on screen. 229 | if pause == 1: 230 | if detected_inside == True: 231 | cv2.putText(frame,'Pet wants outside!',(int(IM_WIDTH*.1),int(IM_HEIGHT*.5)),font,3,(0,0,0),7,cv2.LINE_AA) 232 | cv2.putText(frame,'Pet wants outside!',(int(IM_WIDTH*.1),int(IM_HEIGHT*.5)),font,3,(95,176,23),5,cv2.LINE_AA) 233 | 234 | if detected_outside == True: 235 | cv2.putText(frame,'Pet wants inside!',(int(IM_WIDTH*.1),int(IM_HEIGHT*.5)),font,3,(0,0,0),7,cv2.LINE_AA) 236 | cv2.putText(frame,'Pet wants inside!',(int(IM_WIDTH*.1),int(IM_HEIGHT*.5)),font,3,(95,176,23),5,cv2.LINE_AA) 237 | 238 | # Increment pause counter until it reaches 30 (for a framerate of 1.5 FPS, this is about 20 seconds), 239 | # then unpause the application (set pause flag to 0). 240 | pause_counter = pause_counter + 1 241 | if pause_counter > 30: 242 | pause = 0 243 | pause_counter = 0 244 | detected_inside = False 245 | detected_outside = False 246 | 247 | # Draw counter info 248 | cv2.putText(frame,'Detection counter: ' + str(max(inside_counter,outside_counter)),(10,100),font,0.5,(255,255,0),1,cv2.LINE_AA) 249 | cv2.putText(frame,'Pause counter: ' + str(pause_counter),(10,150),font,0.5,(255,255,0),1,cv2.LINE_AA) 250 | 251 | return frame 252 | 253 | #### Initialize camera and perform object detection #### 254 | 255 | # The camera has to be set up and used differently depending on if it's a 256 | # Picamera or USB webcam. 257 | 258 | ### Picamera ### 259 | if camera_type == 'picamera': 260 | # Initialize Picamera and grab reference to the raw capture 261 | camera = PiCamera() 262 | camera.resolution = (IM_WIDTH,IM_HEIGHT) 263 | camera.framerate = 10 264 | rawCapture = PiRGBArray(camera, size=(IM_WIDTH,IM_HEIGHT)) 265 | rawCapture.truncate(0) 266 | 267 | # Continuously capture frames and perform object detection on them 268 | for frame1 in camera.capture_continuous(rawCapture, format="bgr",use_video_port=True): 269 | 270 | t1 = cv2.getTickCount() 271 | 272 | # Acquire frame and expand frame dimensions to have shape: [1, None, None, 3] 273 | # i.e. a single-column array, where each item in the column has the pixel RGB value 274 | frame = frame1.array 275 | frame.setflags(write=1) 276 | 277 | # Pass frame into pet detection function 278 | frame = pet_detector(frame) 279 | 280 | # Draw FPS 281 | cv2.putText(frame,"FPS: {0:.2f}".format(frame_rate_calc),(30,50),font,1,(255,255,0),2,cv2.LINE_AA) 282 | 283 | # All the results have been drawn on the frame, so it's time to display it. 284 | cv2.imshow('Object detector', frame) 285 | 286 | # FPS calculation 287 | t2 = cv2.getTickCount() 288 | time1 = (t2-t1)/freq 289 | frame_rate_calc = 1/time1 290 | 291 | # Press 'q' to quit 292 | if cv2.waitKey(1) == ord('q'): 293 | break 294 | 295 | rawCapture.truncate(0) 296 | 297 | camera.close() 298 | 299 | ### USB webcam ### 300 | 301 | elif camera_type == 'usb': 302 | # Initialize USB webcam feed 303 | camera = cv2.VideoCapture(0) 304 | ret = camera.set(3,IM_WIDTH) 305 | ret = camera.set(4,IM_HEIGHT) 306 | 307 | # Continuously capture frames and perform object detection on them 308 | while(True): 309 | 310 | t1 = cv2.getTickCount() 311 | 312 | # Acquire frame and expand frame dimensions to have shape: [1, None, None, 3] 313 | # i.e. a single-column array, where each item in the column has the pixel RGB value 314 | ret, frame = camera.read() 315 | 316 | # Pass frame into pet detection function 317 | frame = pet_detector(frame) 318 | 319 | # Draw FPS 320 | cv2.putText(frame,"FPS: {0:.2f}".format(frame_rate_calc),(30,50),font,1,(255,255,0),2,cv2.LINE_AA) 321 | 322 | # All the results have been drawn on the frame, so it's time to display it. 323 | cv2.imshow('Object detector', frame) 324 | 325 | # FPS calculation 326 | t2 = cv2.getTickCount() 327 | time1 = (t2-t1)/freq 328 | frame_rate_calc = 1/time1 329 | 330 | # Press 'q' to quit 331 | if cv2.waitKey(1) == ord('q'): 332 | break 333 | 334 | camera.release() 335 | 336 | cv2.destroyAllWindows() 337 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tutorial to set up TensorFlow Object Detection API on the Raspberry Pi 2 | 3 | 4 |
5 |
6 |
46 |
47 |
112 |
113 |
143 |
144 |
161 |
162 |
169 |
170 |
175 |
176 |
183 |
184 |
191 |
192 |