Monkey Detector Part 2: object detection in Python using Pytorch

Update 2024:

Monkey Detector online service now available for testing – see the blog post for more details.

old version below:

Python is the best for machine learning. Despite not being a Python enthusiast I thought I might just check this out as an alternative. Especially since it runs well on Raspberry Pi type devices.

Pytorch is the most popular Machine Learning framework at the moment. As with the Android version of the Monkey Detector, we will be using a pre-trained model because somebody already made one which suits our purpose.

I used Pycharm as my IDE for Python programming here – mainly because it’s free, and made by Jetbrains so it’s almost the same interface as Android Studio; which although slow as hell on my system is still a great IDE.

I’m going to assume you are running Ubuntu Linux here. If you are using a raspberry pi with raspbian then that’s probably close enough, although I don’t have one to test this on…

Detection test – check if a monkey is contained in a static image:

First things first, full disclaimer! This is mostly not my code. In order to get started I followed an excellent tutorial here: https://www.learnopencv.com/pytorch-for-beginners-image-classification-using-pre-trained-models/

You should check it out, a lot is explained there, with the opportunity to delve deeper. Since I was only interested in hacking together a simple monkey detector I copy-pasted and tweaked until I got the below code, lets call it vision.py:

#!/usr/bin/env python
from torchvision import models
import torch

dir(models)
alexnet = models.alexnet(pretrained=True)

# You will see a similar output as below
# Downloading: "https://download.pytorch.org/models/alexnet-owt- 4df8aa71.pth" to /home/hp/.cache/torch/checkpoints/alexnet-owt-4df8aa71.pth

from torchvision import transforms
transform = transforms.Compose([            #[1]
 transforms.Resize(256),                    #[2]
 transforms.CenterCrop(224),                #[3]
 transforms.ToTensor(),                     #[4]
 transforms.Normalize(                      #[5]
 mean=[0.485, 0.456, 0.406],                #[6]
 std=[0.229, 0.224, 0.225]                  #[7]
 )])

# Import Pillow
from PIL import Image

img = Image.open("/path/to/monkeyImage.jpg")
img_t = transform(img)
batch_t = torch.unsqueeze(img_t, 0)

alexnet.eval()
out = alexnet(batch_t)
print(out.shape)

# Load labels
with open('/path/to/imagenet_classes.txt') as f:
   classes = [line.strip() for line in f.readlines()]
_, indices = torch.sort(out, descending=True)
percentage = torch.nn.functional.softmax(out, dim=1)[0] * 100
[(classes[idx], percentage[idx].item()) for idx in indices[0][:5]]

prediction = [(classes[idx], percentage[idx].item()) for idx in indices[0][:5]]
print("Prediction ", prediction)

#print output to file:
with open('/path/to/f.txt', 'w') as f:
    print(prediction, file=f)  # Python 3.x

import os
os.system('/path/to/checkIfResponseContainsMonkey.sh')

In addition to the code you will need to get imagenet_classes.txt from github as well as this bash script (called by os.system). I hacked this together to check if monkeys were indeed detected by Pytorch. Save this as checkIfResponseContainsMonkey.sh and change the path of f.txt to wherever the python script puts it. Don’t forget to mark it as executable:

!/bin/bash
export DISPLAY=":0.0" 
# read output from python script f.txt:
 if grep -q "chimpanzee" /path/to/f.txt; then
     notify-send 'Monkey found'    
     echo "MONKEEEEEYYYYYSSSS"
 else
     notify-send 'Not found…………..'
     echo "monkey not found"    
 fi

*You may need to install notify-send for this to work.

sudo apt install inotify-tools

As you can see, I used grep to search for “chimpanzee”, since “monkey” was not available. The model still works great on vervets, however, obviously they are similar enough. Surprisingly it doesn’t trigger for humans, as far as I could see in a bit of testing. I tried a few images of feral looking wild humans, but no bites.

Oh and you need an image of a monkey. Here you go, take your pick.

Putting it together:

You have probably worked everything out by now, it’s just a matter of placing the checkIfResponseContainsMonkey.sh script and imagenet_classes.txt files somewhere, then linking them in the code along with your renamed monkeyImage.jpg and in Pycharm IDE at least you then just press PLAY.

Detecting monkeys in the garden:

As for pointing a camera out the window and getting Pytorch to recognize if there is a monkey out there, I hacked something together using Bash, Motion (a motion detection program for linux) and an inotify-wait while loop which monitors a folder for new images (produced by Motion) and then moves and renames them to monkeyImage.jpg in the right place for the Python script to do it’s magic.

! /bin/bash
# the idea for this from https://askubuntu.com/questions/622971/how-i-can-monitor-new-files-in-a-directory-and-move-rename-them-into-another-dir
folder=/var/lib/motion/ #default folder where motion puts it's captured images
while inotifywait -e create --format '%w%f' $folder 
     do 
         notify-send 'removing that'
         mv ls -d /var/lib/motion/* | head -1 /path/to/monkeyTest.jpg
         echo ls -d /var/lib/motion/* | head -1
         rm /var/lib/motion/*.jpg
         runuser -l  tom -c 'python3 /path/to/vision.py'
     done

This whole hacked together system could probably be fully implemented in Python with OpenCV or something like that, however for now I am quite happy with the results from my system. Now on to the next part of this series, doing the same thing with an Android app, and making the sprinkler controller.