Hackster is hosting Hackster Holidays, Finale: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Tuesday!Stream Hackster Holidays, Finale on Tuesday!
Justin Lutz
Published © GPL3+

Don't let the mice in, close the garage door!

At the end of the day, I will get a text and email (with photo) indicating if a garage door is opened or closed.

IntermediateFull instructions provided10 hours75
Don't let the mice in, close the garage door!

Things used in this project

Hardware components

AIY Vision
Google AIY Vision
I just happened to repurpose this kit but you can you anything that has a camera and can do image classification.
×1
smart plug
×1

Software apps and online services

Google Colab
SMS Messaging API
Twilio SMS Messaging API

Story

Read more

Schematics

Google AIY Vision Kit

No schematics needed. Just leverage the kit.

Code

Garage Image Classifier Python code

Python
Runs with 2 arguments: the model name and labels.txt file.
#!/usr/bin/env python3
#
# Copyright 2017 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Script to run generic MobileNet based classification model."""
import argparse
#Justin is hijacking this code to develop a program to identify if the garage door is open or not
#model name is retrained_graph3.binaryproto

from picamera import PiCamera, Color
import numpy as np

from aiy.vision import inference
from aiy.vision.models import utils

from twilio.rest import Client
import os

import smtplib
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

HOSTNAME = smtp_server_here
USERNAME = 'email_addr'
PASSWORD = 'pword'
RECIPIENT_ADDRESS = ['xxxxxxx@mail.com', 'xxxxxx@mail.com']

def sendMail(file_str, SUBJECT):
	EMAIL_BODY = SUBJECT
	
	msg = MIMEMultipart('related')
	msg['Subject'] = SUBJECT
	msg['From'] = USERNAME
	msg['To'] = ", ".join(RECIPIENT_ADDRESS)

	html = """\
	<html>
	  <head></head>
		<body>
		  <img src="cid:image1" alt="Logo" style="width:600px;height:400px;"><br>
		   <p><h4 style="font-size:15px;">Here is the current garage door status.</h4></p>           
		</body>
	</html>
	"""
	# Record the MIME types of text/html.
	part2 = MIMEText(html, 'html')

	# Attach parts into message container.
	msg.attach(part2)

	fp = open(file_str, 'rb')
	msgImage = MIMEImage(fp.read())
	fp.close()

	msgImage.add_header('Content-ID', '<image1>')
	msg.attach(msgImage)

	server = smtplib.SMTP(HOSTNAME, 587)
	server.ehlo()
	server.starttls()
	server.login(USERNAME, PASSWORD)	
	server.sendmail(USERNAME, RECIPIENT_ADDRESS, msg.as_string())
	server.quit()
	print ("Email sent. Logging Out...")

def read_labels(label_path):
    with open(label_path) as label_file:
        return [label.strip() for label in label_file.readlines()]


def get_message(result, threshold, top_k):
    if result:
        return 'Detecting:\n %s' % '\n'.join(result)

    return 'Nothing detected when threshold=%.2f, top_k=%d' % (threshold, top_k)


def process(result, labels, tensor_name, threshold, top_k):
    """Processes inference result and returns labels sorted by confidence."""
    # MobileNet based classification model returns one result vector.
    assert len(result.tensors) == 1
    tensor = result.tensors[tensor_name]
    probs, shape = tensor.data, tensor.shape
    assert shape.depth == len(labels)
    pairs = [pair for pair in enumerate(probs) if pair[1] > threshold]
    pairs = sorted(pairs, key=lambda pair: pair[1], reverse=True)
    pairs = pairs[0:top_k]
    return [' %s (%.2f)' % (labels[index], prob) for index, prob in pairs]


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--model_path', required=True,
        help='Path to converted model file that can run on VisionKit.')
    parser.add_argument('--label_path', required=True,
        help='Path to label file that corresponds to the model.')
    args = parser.parse_args()

    #hard code input parameters (these used to be command line parameters)
    input_height = 160
    input_width = 160
    input_depth = 3
    input_mean = 128.0
    input_std = 128.0
    num_frames = 100 
    output_layer = 'final_result'
    threshold = 0.1
    top_k = 3
    show_fps = False

    #use mobilenet image classifier
    model = inference.ModelDescriptor(
        name='mobilenet_based_classifier',
        input_shape=(1, input_height, input_width, input_depth),
        input_normalizer=(input_mean, input_std),
        compute_graph=utils.load_compute_graph(args.model_path))
    #labels are open and closed
    labels = read_labels(args.label_path)
    prob_arr = []
    with PiCamera(sensor_mode=4, resolution=(1640, 1232), framerate=30) as camera:

        with inference.CameraInference(model) as camera_inference:
            for result in camera_inference.run(num_frames):
                processed_result = process(result, labels, output_layer,
                                           threshold, top_k)
                message = get_message(processed_result, threshold, top_k)
                if show_fps:
                    message += '\nWith %.1f FPS.' % camera_inference.rate
                prob_arr.append(message)

            #get a screenshot of the garage    
            camera.capture('garage_state.jpg')
            #email the current state of the garage and a screenshot
            last_meas = prob_arr[-1]
            str_split = last_meas.split(" (") #splits status and probability
            status_split = str_split[0].split("\n ") #splits Detecting: \n open string
            email_subject = "Garage door is" + status_split[1] + " (Probability: " + str_split[1]
            sendMail('garage_state.jpg',email_subject)
            #get twilio info
            account_sid = os.environ['ID']
            auth_token = os.environ['TOKEN']
            
            client = Client(account_sid, auth_token)

            client.api.account.messages.create(
                to="xxxxxxx",
                from_="xxxxxxxx",
                body=email_subject)
            client.api.account.messages.create(
                to="xxxxxxxxx",
                from_="xxxxxxxx",
                body=email_subject)

if __name__ == '__main__':
    main()

Credits

Justin Lutz
23 projects • 38 followers
Quality manager by day, tinkerer by night. Avid runner. You can tell I'm a dad because of my jokes.

Comments