





А как насчет Exif? Вероятно, это не так безопасно, как вы думаете, но большинство пользователей даже не знают, что он существует, и если вы сделаете так легко читать информацию о водяных знаках, те, кому это небезразлично, все равно смогут это сделать.
Я не думаю, что есть библиотека, которая делает это из коробки. Если вы хотите реализовать свой собственный, я бы определенно выбрал Библиотека изображений Python (PIL).
Это рецепт из поваренной книги Python, который использует PIL для добавления водяного знака видимый к изображению. Если этого достаточно для ваших нужд, вы можете использовать это, чтобы добавить водяной знак с достаточной прозрачностью, чтобы он был виден только в том случае, если вы знаете, что ищете.
Возможно, вы захотите изучить стеганографию; который скрывает данные внутри изображений. Есть формы, которые не потеряются, если вы конвертируете в формат с потерями или даже обрезаете части изображения.
Я собирался опубликовать ответ, похожий на Тьфу. Я бы посоветовал поместить в изображение небольшой TXT-файл с описанием источника изображения (и, возможно, небольшой отчет об авторских правах, если он применим) таким образом, чтобы его было трудно обнаружить и сломать.
Я не уверен, насколько важно быть нерушимым, но простым решением может быть просто добавить текстовый файл в конец изображения. Что-то вроде «Это изображение принадлежит ...».
Если вы откроете изображение в средстве просмотра / браузере, оно будет выглядеть как обычный jpeg, но если вы откроете его в текстовом редакторе, последняя строка будет читаемой.
Тот же метод позволяет вам включить в изображение реальный файл. (скрыть файл внутри изображения) Я обнаружил, что это немного случайно, но файлы 7-zip, похоже, работают. Внутри изображения можно было спрятать всевозможные вкусности, связанные с копирайтингом.
Опять же, это невозможно сломать никаким воображением, но это совершенно невидимо невооруженным глазом.
Некоторые форматы изображений имеют заголовки, в которых также можно хранить произвольную информацию. Например, в Спецификация PNG есть блок, в котором вы можете хранить текстовые данные. Это похоже на ответы выше, но без добавления случайных данных к самим данным изображения.
Я ищу «небьющиеся» водяные знаки, поэтому данные, хранящиеся в exif, или метаданные изображения отсутствуют.
Пока я ждал ответов, я нашел в Интернете кое-что интересное: http://www.cosy.sbg.ac.at/~pmeerw/Watermarking/
Существует магистерская диссертация, которая довольно исчерпывающе описывает алгоритмы и их характеристики (что они делают и насколько они нерушимы). У меня нет времени читать это всесторонне, но это выглядит серьезно. Существуют алгоритмы, которые поддерживают сжатие JPEG, кадрирование, гамма-коррекцию или уменьшение масштаба. Это C, но я могу перенести его на Python или использовать библиотеки C из Python.
Тем не менее, это с 2001 года, и я думаю, что 7 лет - это большой срок в этой области :( Есть ли у кого-нибудь похожие и более свежие вещи?
Я использую следующий код. Для этого требуется PIL:
def reduceOpacity(im, opacity):
"""Returns an image with reduced opacity."""
assert opacity >= 0 and opacity <= 1
if im.mode != 'RGBA':
im = im.convert('RGBA')
else:
im = im.copy()
alpha = im.split()[3]
alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
im.putalpha(alpha)
return im
def watermark(im, mark, position, opacity=1):
"""Adds a watermark to an image."""
if opacity < 1:
mark = reduceOpacity(mark, opacity)
if im.mode != 'RGBA':
im = im.convert('RGBA')
# create a transparent layer the size of the image and draw the
# watermark in that layer.
layer = Image.new('RGBA', im.size, (0,0,0,0))
if position == 'tile':
for y in range(0, im.size[1], mark.size[1]):
for x in range(0, im.size[0], mark.size[0]):
layer.paste(mark, (x, y))
elif position == 'scale':
# scale, but preserve the aspect ratio
ratio = min(float(im.size[0]) / mark.size[0], float(im.size[1]) / mark.size[1])
w = int(mark.size[0] * ratio)
h = int(mark.size[1] * ratio)
mark = mark.resize((w, h))
layer.paste(mark, ((im.size[0] - w) / 2, (im.size[1] - h) / 2))
else:
layer.paste(mark, position)
# composite the watermark with the layer
return Image.composite(layer, im, layer)
img = Image.open('/path/to/image/to/be/watermarked.jpg')
mark1 = Image.open('/path/to/watermark1.png')
mark2 = Image.open('/path/to/watermark2.png')
img = watermark(img, mark1, (img.size[0]-mark1.size[0]-5, img.size[1]-mark1.size[1]-5), 0.5)
img = watermark(img, mark2, 'scale', 0.01)
Водяной знак слишком тусклый, чтобы его можно было увидеть. Только сплошное цветное изображение действительно покажет это. Я могу использовать его для создания изображения, на котором нет водяного знака, но если я сделаю побитовое вычитание, используя исходное изображение, я могу продемонстрировать, что мой водяной знак есть.
Если вы хотите увидеть, как это работает, перейдите на TylerGriffinPhotography.com. Каждое изображение на сайте помечено водяным знаком дважды: один раз с водяным знаком в правом нижнем углу с непрозрачностью 50% (5 пикселей от края) и один раз по всему изображению с непрозрачностью 1% (с использованием «масштаба», который масштабирует водяной знак до все изображение). Сможете ли вы понять, что это за вторая форма водяного знака с низкой непрозрачностью?
Хм? Какое исходное изображение? Чем это отличается от любых других водяных знаков?
@tghw Майлз говорит, что, поскольку исходное изображение и изображение new неотличимы на глаз, он мог просто создать свой собственный original, который слегка отличался от вашего (используя тот же код, который вы разместили выше), чтобы отобразить свой собственный водяной знак, и заявить изображение его собственное.
@will За исключением того, что на новом «оригинале» все еще будет мой водяной знак, поэтому я мог показать не только то, что мой действительно был оригиналом, но и то, что он нанес свой водяной знак на мой водяной знак.
@tghw, если суть этого тонкого водяного знака в том, что вы его не видите (и, по сути, это стеганография), как вы можете доказать, что ваш оригинал является оригиналом?
@will, это вопрос судов и закона об авторском праве, а не технологии шифрования.
Ну, с невидимыми водяными знаками не все так просто. Проверьте digimarc, какие деньги они на нем заработали. Не существует бесплатного кода C / Python, который написал бы одинокий гений, оставьте его для бесплатного использования. Я реализовал свой собственный алгоритм, и название инструмента - SignMyImage. Погуглите, если интересно ... F>
Более новый (2005 г.) часто задаваемый вопрос о цифровых водяных знаках можно найти по адресу watermarkingworld.org.
Если вы говорите о стеганография, вот старый не слишком модный модуль, который я однажды сделал для друга (код Python 2.x):
from __future__ import division
import math, os, array, random
import itertools as it
import Image as I
import sys
def encode(txtfn, imgfn):
with open(txtfn, "rb") as ifp:
txtdata= ifp.read()
txtdata= txtdata.encode('zip')
img= I.open(imgfn).convert("RGB")
pixelcount= img.size[0]*img.size[1]
## sys.stderr.write("image %dx%d\n" % img.size)
factor= len(txtdata) / pixelcount
width= int(math.ceil(img.size[0]*factor**.5))
height= int(math.ceil(img.size[1]*factor**.5))
pixelcount= width * height
if pixelcount < len(txtdata): # just a sanity check
sys.stderr.write("phase 2, %d bytes in %d pixels?\n" % (len(txtdata), pixelcount))
sys.exit(1)
## sys.stderr.write("%d bytes in %d pixels (%dx%d)\n" % (len(txtdata), pixelcount, width, height))
img= img.resize( (width, height), I.ANTIALIAS)
txtarr= array.array('B')
txtarr.fromstring(txtdata)
txtarr.extend(random.randrange(256) for x in xrange(len(txtdata) - pixelcount))
newimg= img.copy()
newimg.putdata([
(
r & 0xf8 |(c & 0xe0)>>5,
g & 0xfc |(c & 0x18)>>3,
b & 0xf8 |(c & 0x07),
)
for (r, g, b), c in it.izip(img.getdata(), txtarr)])
newimg.save(os.path.splitext(imgfn)[0]+'.png', optimize=1, compression=9)
def decode(imgfn, txtfn):
img= I.open(imgfn)
with open(txtfn, 'wb') as ofp:
arrdata= array.array('B',
((r & 0x7) << 5 | (g & 0x3) << 3 | (b & 0x7)
for r, g, b in img.getdata())).tostring()
findata= arrdata.decode('zip')
ofp.write(findata)
if __name__ == "__main__":
if sys.argv[1] == 'e':
encode(sys.argv[2], sys.argv[3])
elif sys.argv[1] == 'd':
decode(sys.argv[2], sys.argv[3])
Он хранит байт данных на пиксель изображения, используя: 3 наименее значимых бита синей полосы, 2 младших бита зеленой полосы и 3 младших двоичных разряда красной.
Функция кодирования: входной текстовый файл сжимается с помощью zlib, а размер входного изображения изменяется (с сохранением пропорций), чтобы гарантировать, что количество пикселей не меньше, чем сжатых байтов. Изображение PNG с тем же именем, что и входное изображение (поэтому не используйте имя файла ".png" в качестве входных данных, если вы оставите код как есть :), сохраняется, содержащее стеганографические данные.
функция декодирования: ранее сохраненные zlib-сжатые данные извлекаются из входного изображения и сохраняются без сжатия под указанным именем файла.
Я проверил, что старый код все еще работает, поэтому вот пример изображения, содержащего стеганографические данные:

Вы заметите, что добавленный шум едва заметен.
Проблема заключается в том, чтобы полагаться на исходное изображение. С помощью этого метода, используя изображение с водяными знаками, я могу создать другое «исходное» изображение, чтобы после побитового вычитания отображался водяной знак мой.