import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

# Make sure that caffe is on the python path:
caffe_root = '../'  # this file is expected to be in {caffe_root}/examples
import sys
sys.path.insert(0, caffe_root + 'python')

import caffe

# Set the right path to your model definition file, pretrained model weights,
# and the image you would like to classify.
MODEL_FILE = '../models/bvlc_reference_caffenet/deploy.prototxt'
PRETRAINED = '../models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel'
IMAGE_FILE = '../examples/images/cat.jpg'

import os
if not os.path.isfile(PRETRAINED):
    print("Downloading pre-trained CaffeNet model...")
    !../scripts/download_model_binary.py ../models/bvlc_reference_caffenet

"""
Explanation: Classifying ImageNet: the instant Caffe way
Caffe has a Python interface, pycaffe, with a caffe.Net interface for models. There are both Python and MATLAB interfaces. While this example uses the off-the-shelf Python caffe.Classifier interface there is also a MATLAB example at matlab/caffe/matcaffe_demo.m.
Before we begin, you must compile Caffe. You should add the Caffe module to your PYTHONPATH although this example includes it automatically. If you haven't yet done so, please refer to the installation instructions. This example uses our pre-trained CaffeNet model, an ILSVRC12 image classifier. You can download it by running ./scripts/download_model_binary.py models/bvlc_reference_caffenet or let the first step of this example download it for you.
Ready? Let's start.
End of explanation
"""


caffe.set_mode_cpu()
net = caffe.Classifier(MODEL_FILE, PRETRAINED,
                       mean=np.load(caffe_root + 'python/caffe/imagenet/ilsvrc_2012_mean.npy').mean(1).mean(1),
                       channel_swap=(2,1,0),
                       raw_scale=255,
                       image_dims=(256, 256))

"""
Explanation: Loading a network is easy. caffe.Classifier takes care of everything. Note the arguments for configuring input preprocessing: mean subtraction switched on by giving a mean array, input channel swapping takes care of mapping RGB into the reference ImageNet model's BGR order, and raw scaling multiplies the feature scale from the input [0,1] to the ImageNet model's [0,255].
We will set the phase to test since we are doing testing, and will first use CPU for the computation.
End of explanation
"""


input_image = caffe.io.load_image(IMAGE_FILE)
plt.imshow(input_image)

"""
Explanation: Let's take a look at our example image with Caffe's image loading helper.
End of explanation
"""


prediction = net.predict([input_image])  # predict takes any number of images, and formats them for the Caffe net automatically
print 'prediction shape:', prediction[0].shape
plt.plot(prediction[0])
print 'predicted class:', prediction[0].argmax()

"""
Explanation: Time to classify. The default is to actually do 10 predictions, cropping the center and corners of the image as well as their mirrored versions, and average over the predictions:
End of explanation
"""


prediction = net.predict([input_image], oversample=False)
print 'prediction shape:', prediction[0].shape
plt.plot(prediction[0])
print 'predicted class:', prediction[0].argmax()

"""
Explanation: You can see that the prediction is 1000-dimensional, and is pretty sparse.
The predicted class 281 is "Tabby cat." Our pretrained model uses the synset ID ordering of the classes, as listed in ../data/ilsvrc12/synset_words.txt if you fetch the auxiliary imagenet data by ../data/ilsvrc12/get_ilsvrc_aux.sh. If you look at the top indices that maximize the prediction score, they are cats, foxes, and other cute mammals. Not unreasonable predictions, right?
Now let's classify by the center crop alone by turning off oversampling. Note that this makes a single input, although if you inspect the model definition prototxt you'll see the network has a batch size of 10. The python wrapper handles batching and padding for you!
End of explanation
"""


%timeit net.predict([input_image])

"""
Explanation: Now, why don't we see how long it takes to perform the classification end to end? This result is run from an AMD A8-6600K APU, so you may observe some performance differences.
End of explanation
"""


# Resize the image to the standard (256, 256) and oversample net input sized crops.
input_oversampled = caffe.io.oversample([caffe.io.resize_image(input_image, net.image_dims)], net.crop_dims)
# 'data' is the input blob name in the model definition, so we preprocess for that input.
caffe_input = np.asarray([net.transformer.preprocess('data', in_) for in_ in input_oversampled])
# forward() takes keyword args for the input blobs with preprocessed input arrays.
%timeit net.forward(data=caffe_input)

"""
Explanation: It may look a little slow, but note that time is spent on cropping, python interfacing, and running 10 images. For performance, if you really want to make prediction fast, you can optionally code in C++ and pipeline operations better. For experimenting and prototyping the current speed is fine.
Let's time classifying a single image with input preprocessed:
End of explanation
"""


caffe.set_mode_gpu()

"""
Explanation: OK, so how about GPU? it is actually pretty easy:
End of explanation
"""


prediction = net.predict([input_image])
print 'prediction shape:', prediction[0].shape
plt.plot(prediction[0])

"""
Explanation: Voila! Now we are in GPU mode. Let's see if the code gives the same result:
End of explanation
"""


# Full pipeline timing.
%timeit net.predict([input_image])

# Forward pass timing.
%timeit net.forward(data=caffe_input)

"""
Explanation: Good, everything is the same. And how about time consumption? The following benchmark is obtained on the same machine with a GTX 980 GPU:
End of explanation
"""


