From 05471f88432c6ec9ead38f2dcdaf56785b307243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariano=20Falc=C3=B3n?= Date: Wed, 1 Nov 2017 22:11:13 -0300 Subject: [PATCH] commit inicial --- raspberry/getpics.sh | 7 ++ raspberry/webcam.sh | 5 + taxiornot.ipynb | 275 +++++++++++++++++++++++++++++++++++++++++++ vgg16.py | 231 ++++++++++++++++++++++++++++++++++++ 4 files changed, 518 insertions(+) create mode 100644 raspberry/getpics.sh create mode 100644 raspberry/webcam.sh create mode 100644 taxiornot.ipynb create mode 100755 vgg16.py diff --git a/raspberry/getpics.sh b/raspberry/getpics.sh new file mode 100644 index 0000000..3ce4378 --- /dev/null +++ b/raspberry/getpics.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +while true +do + /home/pi/webcam.sh + sleep 5 +done diff --git a/raspberry/webcam.sh b/raspberry/webcam.sh new file mode 100644 index 0000000..abcdc42 --- /dev/null +++ b/raspberry/webcam.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +DATE=$(date +”%Y-%m-%d_%H%M%S”) + +fswebcam -r 640×480 –no-banner /home/pi/webcam/$DATE.jpg diff --git a/taxiornot.ipynb b/taxiornot.ipynb new file mode 100644 index 0000000..537e113 --- /dev/null +++ b/taxiornot.ipynb @@ -0,0 +1,275 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Taxiornot" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Basic setup" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Importamos la clase vgg16 y luego la instanciamos" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Using TensorFlow backend.\n" + ] + } + ], + "source": [ + "from vgg16 import Vgg16" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "vgg = Vgg16()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "batch_size=4" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "path = \"data/tachornot/\"" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 236 images belonging to 2 classes.\n", + "Found 62 images belonging to 2 classes.\n" + ] + } + ], + "source": [ + "batches = vgg.get_batches(path+'train', batch_size=batch_size)\n", + "val_batches = vgg.get_batches(path+'valid', batch_size=batch_size)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Calling *finetune()* modifies the model such that it will be trained based on the data in the batches provided - in this case, to predict either 'dog' or 'cat'." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "vgg.finetune(batches)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, we *fit()* the parameters of the model using the training data, reporting the accuracy on the validation set after every epoch. (An *epoch* is one full pass through the training data.)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 1/10\n", + "236/236 [==============================] - 4s - loss: 0.9080 - acc: 0.7500 - val_loss: 0.2648 - val_acc: 0.8710\n", + "Epoch 2/10\n", + "236/236 [==============================] - 3s - loss: 0.4089 - acc: 0.8475 - val_loss: 0.1603 - val_acc: 0.9516\n", + "Epoch 3/10\n", + "236/236 [==============================] - 3s - loss: 0.2968 - acc: 0.9068 - val_loss: 0.2070 - val_acc: 0.9355\n", + "Epoch 4/10\n", + "236/236 [==============================] - 3s - loss: 0.3114 - acc: 0.8983 - val_loss: 0.2002 - val_acc: 0.9516\n", + "Epoch 5/10\n", + "236/236 [==============================] - 3s - loss: 0.1200 - acc: 0.9449 - val_loss: 0.0704 - val_acc: 0.9839\n", + "Epoch 6/10\n", + "236/236 [==============================] - 3s - loss: 0.2147 - acc: 0.9237 - val_loss: 0.0279 - val_acc: 1.0000\n", + "Epoch 7/10\n", + "236/236 [==============================] - 3s - loss: 0.1917 - acc: 0.9364 - val_loss: 0.0688 - val_acc: 0.9516\n", + "Epoch 8/10\n", + "236/236 [==============================] - 3s - loss: 0.1763 - acc: 0.9449 - val_loss: 0.0359 - val_acc: 0.9839\n", + "Epoch 9/10\n", + "236/236 [==============================] - 3s - loss: 0.2478 - acc: 0.9153 - val_loss: 0.0245 - val_acc: 1.0000\n", + "Epoch 10/10\n", + "236/236 [==============================] - 3s - loss: 0.1889 - acc: 0.9364 - val_loss: 0.0390 - val_acc: 0.9839\n" + ] + } + ], + "source": [ + "vgg.fit(batches, val_batches, nb_epoch=10)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "imgs,labels = next(val_batches)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'plots' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mplots\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mimgs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtitles\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mlabels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'plots' is not defined" + ] + } + ], + "source": [ + "plots(imgs, titles=labels)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.13" + }, + "nav_menu": {}, + "nbpresent": { + "slides": { + "28b43202-5690-4169-9aca-6b9dabfeb3ec": { + "id": "28b43202-5690-4169-9aca-6b9dabfeb3ec", + "prev": null, + "regions": { + "3bba644a-cf4d-4a49-9fbd-e2554428cf9f": { + "attrs": { + "height": 0.8, + "width": 0.8, + "x": 0.1, + "y": 0.1 + }, + "content": { + "cell": "f3d3a388-7e2a-4151-9b50-c20498fceacc", + "part": "whole" + }, + "id": "3bba644a-cf4d-4a49-9fbd-e2554428cf9f" + } + } + }, + "8104def2-4b68-44a0-8f1b-b03bf3b2a079": { + "id": "8104def2-4b68-44a0-8f1b-b03bf3b2a079", + "prev": "28b43202-5690-4169-9aca-6b9dabfeb3ec", + "regions": { + "7dded777-1ddf-4100-99ae-25cf1c15b575": { + "attrs": { + "height": 0.8, + "width": 0.8, + "x": 0.1, + "y": 0.1 + }, + "content": { + "cell": "fe47bd48-3414-4657-92e7-8b8d6cb0df00", + "part": "whole" + }, + "id": "7dded777-1ddf-4100-99ae-25cf1c15b575" + } + } + } + }, + "themes": {} + }, + "toc": { + "navigate_menu": true, + "number_sections": true, + "sideBar": true, + "threshold": 6, + "toc_cell": false, + "toc_section_display": "block", + "toc_window_display": false + }, + "widgets": { + "state": {}, + "version": "1.1.2" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/vgg16.py b/vgg16.py new file mode 100755 index 0000000..071f5b6 --- /dev/null +++ b/vgg16.py @@ -0,0 +1,231 @@ +from __future__ import division, print_function + +import os, json +from glob import glob +import numpy as np +from scipy import misc, ndimage +from scipy.ndimage.interpolation import zoom + +from keras import backend as K +from keras.layers.normalization import BatchNormalization +from keras.utils.data_utils import get_file +from keras.models import Sequential +from keras.layers.core import Flatten, Dense, Dropout, Lambda +from keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D +from keras.layers.pooling import GlobalAveragePooling2D +from keras.optimizers import SGD, RMSprop, Adam +from keras.preprocessing import image + +# In case we are going to use the TensorFlow backend we need to explicitly set the Theano image ordering +from keras import backend as K +K.set_image_dim_ordering('th') + + +vgg_mean = np.array([123.68, 116.779, 103.939], dtype=np.float32).reshape((3,1,1)) +def vgg_preprocess(x): + """ + Subtracts the mean RGB value, and transposes RGB to BGR. + The mean RGB was computed on the image set used to train the VGG model. + + Args: + x: Image array (height x width x channels) + Returns: + Image array (height x width x transposed_channels) + """ + x = x - vgg_mean + return x[:, ::-1] # reverse axis rgb->bgr + + +class Vgg16(): + """ + The VGG 16 Imagenet model + """ + + + def __init__(self): + self.FILE_PATH = 'http://files.fast.ai/models/' + self.create() + self.get_classes() + + + def get_classes(self): + """ + Downloads the Imagenet classes index file and loads it to self.classes. + The file is downloaded only if it not already in the cache. + """ + fname = 'imagenet_class_index.json' + fpath = get_file(fname, self.FILE_PATH+fname, cache_subdir='models') + with open(fpath) as f: + class_dict = json.load(f) + self.classes = [class_dict[str(i)][1] for i in range(len(class_dict))] + + def predict(self, imgs, details=False): + """ + Predict the labels of a set of images using the VGG16 model. + + Args: + imgs (ndarray) : An array of N images (size: N x width x height x channels). + details : ?? + + Returns: + preds (np.array) : Highest confidence value of the predictions for each image. + idxs (np.ndarray): Class index of the predictions with the max confidence. + classes (list) : Class labels of the predictions with the max confidence. + """ + # predict probability of each class for each image + all_preds = self.model.predict(imgs) + # for each image get the index of the class with max probability + idxs = np.argmax(all_preds, axis=1) + # get the values of the highest probability for each image + preds = [all_preds[i, idxs[i]] for i in range(len(idxs))] + # get the label of the class with the highest probability for each image + classes = [self.classes[idx] for idx in idxs] + return np.array(preds), idxs, classes + + + def ConvBlock(self, layers, filters): + """ + Adds a specified number of ZeroPadding and Covolution layers + to the model, and a MaxPooling layer at the very end. + + Args: + layers (int): The number of zero padded convolution layers + to be added to the model. + filters (int): The number of convolution filters to be + created for each layer. + """ + model = self.model + for i in range(layers): + model.add(ZeroPadding2D((1, 1))) + model.add(Convolution2D(filters, 3, 3, activation='relu')) + model.add(MaxPooling2D((2, 2), strides=(2, 2))) + + + def FCBlock(self): + """ + Adds a fully connected layer of 4096 neurons to the model with a + Dropout of 0.5 + + Args: None + Returns: None + """ + model = self.model + model.add(Dense(4096, activation='relu')) + model.add(Dropout(0.5)) + + + def create(self): + """ + Creates the VGG16 network achitecture and loads the pretrained weights. + + Args: None + Returns: None + """ + model = self.model = Sequential() + model.add(Lambda(vgg_preprocess, input_shape=(3,224,224), output_shape=(3,224,224))) + + self.ConvBlock(2, 64) + self.ConvBlock(2, 128) + self.ConvBlock(3, 256) + self.ConvBlock(3, 512) + self.ConvBlock(3, 512) + + model.add(Flatten()) + self.FCBlock() + self.FCBlock() + model.add(Dense(1000, activation='softmax')) + + fname = 'vgg16.h5' + model.load_weights(get_file(fname, self.FILE_PATH+fname, cache_subdir='models')) + + + def get_batches(self, path, gen=image.ImageDataGenerator(), shuffle=True, batch_size=8, class_mode='categorical'): + """ + Takes the path to a directory, and generates batches of augmented/normalized data. Yields batches indefinitely, in an infinite loop. + + See Keras documentation: https://keras.io/preprocessing/image/ + """ + return gen.flow_from_directory(path, target_size=(224,224), + class_mode=class_mode, shuffle=shuffle, batch_size=batch_size) + + + def ft(self, num): + """ + Replace the last layer of the model with a Dense (fully connected) layer of num neurons. + Will also lock the weights of all layers except the new layer so that we only learn + weights for the last layer in subsequent training. + + Args: + num (int) : Number of neurons in the Dense layer + Returns: + None + """ + model = self.model + model.pop() + for layer in model.layers: layer.trainable=False + model.add(Dense(num, activation='softmax')) + self.compile() + + def finetune(self, batches): + """ + Modifies the original VGG16 network architecture and updates self.classes for new training data. + + Args: + batches : A keras.preprocessing.image.ImageDataGenerator object. + See definition for get_batches(). + """ + self.ft(batches.nb_class) + classes = list(iter(batches.class_indices)) # get a list of all the class labels + + # batches.class_indices is a dict with the class name as key and an index as value + # eg. {'cats': 0, 'dogs': 1} + + # sort the class labels by index according to batches.class_indices and update model.classes + for c in batches.class_indices: + classes[batches.class_indices[c]] = c + self.classes = classes + + + def compile(self, lr=0.001): + """ + Configures the model for training. + See Keras documentation: https://keras.io/models/model/ + """ + self.model.compile(optimizer=Adam(lr=lr), + loss='categorical_crossentropy', metrics=['accuracy']) + + + def fit_data(self, trn, labels, val, val_labels, nb_epoch=1, batch_size=64): + """ + Trains the model for a fixed number of epochs (iterations on a dataset). + See Keras documentation: https://keras.io/models/model/ + """ + self.model.fit(trn, labels, nb_epoch=nb_epoch, + validation_data=(val, val_labels), batch_size=batch_size) + + + def fit(self, batches, val_batches, nb_epoch=1): + """ + Fits the model on data yielded batch-by-batch by a Python generator. + See Keras documentation: https://keras.io/models/model/ + """ + self.model.fit_generator(batches, samples_per_epoch=batches.nb_sample, nb_epoch=nb_epoch, + validation_data=val_batches, nb_val_samples=val_batches.nb_sample) + + + def test(self, path, batch_size=8): + """ + Predicts the classes using the trained model on data yielded batch-by-batch. + + Args: + path (string): Path to the target directory. It should contain one subdirectory + per class. + batch_size (int): The number of images to be considered in each batch. + + Returns: + test_batches, numpy array(s) of predictions for the test_batches. + + """ + test_batches = self.get_batches(path, shuffle=False, batch_size=batch_size, class_mode=None) + return test_batches, self.model.predict_generator(test_batches, test_batches.nb_sample) +