Tags

, , , , , , , , , , , , ,

Arkwood loves watching the Dave Channel on his TV set, but hates having to sit through the adverts. ‘Don’t fret,’ I told my seedy Belgian buddy, ‘I’ll write some python code on my Raspberry Pi which will send you an alert when the adverts have ended.’

It wasn’t too difficult to do. First I attached a webcam to my Pi and took a snap of the Dave emblem, the image the channel always uses to signal a TV show is about to start. Here it is captured on Arkwood’s TV set:

pi_advert_image_dave

Great. Now all we need to do is configure the webcam to take a snap of the TV a few times a second, and as soon as it finds a close match to the Dave emblem we can alert Arkwood. Here’s the basic program:

from webcam import Webcam
import pygame

webcam = Webcam()

# set up cymbal sound
pygame.mixer.init()
cymbal = pygame.mixer.Sound("75341__neotone__cymbol-scraped.wav")

# wait until Dave image appears on TV
webcam.detect_image('dave.jpg')

# play cymbal, as adverts have ended
cymbal.play()

We use Pygame to play a cymbal sound though a set of speakers attached to the Pi, to let my friend know his TV show is about to start.

I got my cymbal sound from the Freesound website:
75341__neotone__cymbol-scraped

Now, let’s delve a bit into how the webcam makes the match. Here’s the WebCam detect_image function:

import cv2
from datetime import datetime

class Webcam(object):

    # constructor
    def __init__(self):
        self.webcam = cv2.VideoCapture(0)
        
    # save image to disk
    def _save_image(self, path, image):
        filename = datetime.now().strftime('%Y%m%d_%Hh%Mm%Ss%f') + '.jpg'
        cv2.imwrite(path + filename, image)

    # load image from disk
    def _load_image(self, path, filename):
        return cv2.imread(path + filename)

    # wait until image is detected 
    def detect_image(self, filename):
	
        # set image threshold
        threshold = 250000

        # load image
        loaded_img = cv2.cvtColor(self._load_image('WebCam/Image/', filename), cv2.COLOR_RGB2GRAY)
	
        # loop until we match our loaded image
        # with a webcam image
        while True:

            # obtain an image from webcam
            webcam_img = cv2.cvtColor(self.webcam.read()[1], cv2.COLOR_RGB2GRAY)
            
            # compare loaded image against webcam image
            diff = cv2.absdiff(loaded_img, webcam_img)
            count = cv2.countNonZero(diff)

            # debug
            print(count)

            # if the threshold has not been breached then
            # we have matched our loaded and webcam images
            if(count < threshold):
                self._save_image('WebCam/Match/', webcam_img)
                return True

            # pause to control webcam sample rate
            cv2.waitKey(250)

Our image detection is done using the OpenCV library.

First we set a threshold value.

Next we load our Dave emblem, the image we snapped earlier.

Now we can loop until we get an image from the TV set, via our webcam, that is below the threshold and therefore a close match. Bingo! We save the webcam image (so we can eyeball it later and confirm the match) and then end the function.

Here’s a screenshot showing a reading below the threshold, with the matched image saved to file:

pi_advert

And here is that matched image:

pi_advert_match_dave

‘Hurray!’ I said to Arkwood, ‘You can now fix yourself a cup of tea during the ad break, safe in the knowledge that you will be alerted when the TV show is about to start.’

‘Yeah,’ he retorted, ‘But now I hate those fuckin’ cymbals. They set my nerves on edge.’

Well, as Chas and Dave once said, there Ain’t No Pleasing You.

PS.

In order to test the program through and obtain a suitable threshold, I saved a bunch of webcam images during a typical advert break and replayed them on my Raspberry Pi. Here’s some amendments I made to the code to replay these sample images:

import os

#while True:
for sample_file in os.listdir("WebCam/Sample/"):

#webcam_img = cv2.cvtColor(self.webcam.read()[1], cv2.COLOR_RGB2GRAY)
webcam_img = cv2.cvtColor(self._load_image('WebCam/Sample/', sample_file), cv2.COLOR_RGB2GRAY)

It also helps if you have something like a Sky+HD box, so you can rewind and play the TV adverts over and over, to confirm that the program works real-time.

Obtaining a suitable threshold value is a bit of trial and error. Also, I was testing during early evening and found that as dusk rolled in it played havoc with my threshold (I’m guessing the reduced daylight affected image detection).

Here’s some other images that were a close match, as I relaxed the threshold:

pi_advert_match_dave2

pi_advert_match_dave3

And some images which had no chance of being a close match:

Co-op advert
pi_advert_nomatch_coop

T K Maxx advert
pi_advert_nomatch_tkmaxx

Note all images are in JPEG format.

Ciao!