使用Python(不使用SciPy)检测照片中的特定水印

5
我有大量的图像(数十万),对于每个图像,我需要判断其右上角是否有水印。水印始终相同且位于同一位置。它采用带有符号和一些文本的缎带形式。我正在寻找简单快速的方法来实现此目标,最好不要使用SciPy(因为我使用的服务器上没有该软件,但可以使用NumPy)。
到目前为止,我尝试使用PIL和裁剪函数来隔离应该出现水印的图像区域,然后使用RMS函数比较直方图(参见http://snipplr.com/view/757/compare-two-pil-images-in-python/)。但是这样做效果不佳,两个方向都存在许多错误。
非常感谢任何想法。谢谢

你也在创建水印吗?你能否在文件的停止位之后添加一些原始二进制数据,而不是创建水印呢? - Joran Beasley
水印是不透明的吗?如果是,那么我猜添加水印到图像的操作是基本幂等的。也许你可以通过比较存储的图像和已经添加了水印的版本来进行测试。 - Hammerite
@Joran-Beasley - 我不是在创建水印,不是我做的。 - alan
@Hammerite - 水印是部分透明的。我可以获取一个没有水印的文件版本,但它的分辨率不同,我想这会使事情变得复杂。 - alan
3个回答

15

另一种可能性是使用机器学习。我的背景是自然语言处理(不是计算机视觉),但我尝试使用您问题的描述创建训练和测试集,似乎可以工作(在未见过的数据上达到100%的准确度)。

训练集

训练集由具有水印的相同图像(正面示例)和没有水印的图像(负面示例)组成。

测试集

测试集包括未在训练集中出现的图像。

示例数据

如果有兴趣,可以使用示例训练和测试图像进行尝试。

代码:

完整版本可在 gist 上找到。以下是摘录:

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()

示例输出

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]

哇,太棒了!非常感谢你的回答。我刚刚在我的问题上测试过了,它的准确率达到了100%(只需更改裁剪区域大小)。 - alan
它如何找到要查找的“水印”?它是作为输入提供还是像上面的“watermark = MultinomialNB()”变量一样从训练集中学习。请确认。 - master_dodo

2
您可以随时使用restb.ai的专业图像识别API来自动化水印检测过程。
import requests

url = "https://api.restb.ai/segmentation"
querystring = {"client_key":"your-free-key-here","model_id":"re_logo","image_url":"http://demo.restb.ai/img/gallery/realestate/logos-watermarks/re_logo-1.jpg"}
response = requests.request("GET", url, params=querystring) print(response.text)

标志和水印检测演示的屏幕截图


2

水印的位置是否准确?水印是如何应用到背景图像上的?

我假设水印是部分添加或乘法函数。水印图像可能是按以下方式计算的:

resultPixel = imagePixel + (watermarkPixel*mixinValue)

mixinValue的取值范围是0.0-1.0,因此您可以通过使用(1-mixinValue)作为乘数重新应用水印来完成混合。这将导致像素与水印匹配。只需将结果图像的颜色与原始水印进行比较即可。

testPixel = resultPixel + (watermarkPixel*(1-mixinValue))
assert testPixel == watermarkPixel

当然,对水印图像进行压缩可能会导致测试像素的变化。

这听起来不错,但我没有创建这些图像,因此没有“干净”的水印副本。 - alan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接