Detecting a Specific Watermark in a Photo with Python (without SciPy)

alan picture alan · Apr 25, 2013 · Viewed 9k times · Source

I have a large number of images (hundreds of thousands) and, for each one, I need to say whether or not it has a watermark in the top right corner. The watermark is always the same and is in the same position. It takes the form of a ribbon with a symbol and some text. I'm looking for simple and fast way to do this that, ideally, doesn't use SciPy (as it's not available on the server I'm using -- but it can use NumPy)

So far, I've tried using PIL and the crop function to isolate the area of the image where the watermark should be and then compared the histograms with a RMS function (see http://snipplr.com/view/757/compare-two-pil-images-in-python/). That doesn't work very well as there are lots of errors in both directions.

Any ideas would be much appreciated. Thanks

Answer

Wesley Baugh picture Wesley Baugh · Apr 25, 2013

Another possibility is to use machine learning. My background is natural language processing (not computer vision), but I tried creating a training and testing set using the description of your problem and it seems to work (100% accuracy on unseen data).

Training set

The training set consisted of the same images with the watermark (positive example), and without the watermark (negative example).

Testing set

The testing set consists of images that were not in the training set.

Example data

If interested, you can try it with the example training and testing images.

Code:

Full version available as a gist. Excerpt below:

import glob

from classify import MultinomialNB
from PIL import Image


TRAINING_POSITIVE = 'training-positive/*.jpg'
TRAINING_NEGATIVE = 'training-negative/*.jpg'
TEST_POSITIVE = 'test-positive/*.jpg'
TEST_NEGATIVE = 'test-negative/*.jpg'

# How many pixels to grab from the top-right of image.
CROP_WIDTH, CROP_HEIGHT = 100, 100
RESIZED = (16, 16)


def get_image_data(infile):
    image = Image.open(infile)
    width, height = image.size
    # left upper right lower
    box = width - CROP_WIDTH, 0, width, CROP_HEIGHT
    region = image.crop(box)
    resized = region.resize(RESIZED)
    data = resized.getdata()
    # Convert RGB to simple averaged value.
    data = [sum(pixel) / 3 for pixel in data]
    # Combine location and value.
    values = []
    for location, value in enumerate(data):
        values.extend([location] * value)
    return values


def main():
    watermark = MultinomialNB()
    # Training
    count = 0
    for infile in glob.glob(TRAINING_POSITIVE):
        data = get_image_data(infile)
        watermark.train((data, 'positive'))
        count += 1
        print 'Training', count
    for infile in glob.glob(TRAINING_NEGATIVE):
        data = get_image_data(infile)
        watermark.train((data, 'negative'))
        count += 1
        print 'Training', count
    # Testing
    correct, total = 0, 0
    for infile in glob.glob(TEST_POSITIVE):
        data = get_image_data(infile)
        prediction = watermark.classify(data)
        if prediction.label == 'positive':
            correct += 1
        total += 1
        print 'Testing ({0} / {1})'.format(correct, total)
    for infile in glob.glob(TEST_NEGATIVE):
        data = get_image_data(infile)
        prediction = watermark.classify(data)
        if prediction.label == 'negative':
            correct += 1
        total += 1
        print 'Testing ({0} / {1})'.format(correct, total)
    print 'Got', correct, 'out of', total, 'correct'


if __name__ == '__main__':
    main()

Example output

Training 1
Training 2
Training 3
Training 4
Training 5
Training 6
Training 7
Training 8
Training 9
Training 10
Training 11
Training 12
Training 13
Training 14
Testing (1 / 1)
Testing (2 / 2)
Testing (3 / 3)
Testing (4 / 4)
Testing (5 / 5)
Testing (6 / 6)
Testing (7 / 7)
Testing (8 / 8)
Testing (9 / 9)
Testing (10 / 10)
Got 10 out of 10 correct
[Finished in 3.5s]