4

最初の関数は、グレースケール ビットマップ イメージを別のカラー ビットマップ イメージに挿入します。今、私の目的は、基本的にグレースケール ピクセル イメージの各桁 (例: 123) を取得し、すべての RGB ピクセルの最後の桁 (244、244、244) を置き換えることです。したがって、基本的には次のようになります (241、242、243 )。基本的に、これはグレースケール画像でカラー画像に透かしを入れています。

次のコードは私がこれまでに持っているもので、タプル値をリストで返すことができます。大きな画像で小さなグレースケール画像のサイズのスペースを操作する方法がわかりません。

def add_watermark():
    image = Image.open()
    pixels = list(image.getdata())
    image.putdata()
    image.save()

    for i in range(img.size[0]):
        for j in range(img.size[1]):
            pixels[i,j] = (i, j, 100)

誰でもアドバイスを提供できますか?

4

2 に答える 2

0

あなたは正しい軌道に乗っています。これがピクセルの操作方法ですが、以下に示すようなピクセル アクセス オブジェクトを使用すると、もう少し高速に操作できます。

正しい数字を抽出して設定することを除いて、すべて非常に簡単です。この例では、10 の累乗で除算し、モジュロ演算子を使用してこれを行いましたが、他の方法もあります。うまくいけば、コメントはそれを十分に説明します。

from PIL import Image

def add_watermark(watermark_path, image_in_path, image_out_path):
    # Load watermark and image and check sizes and modes
    watermark = Image.open(watermark_path)
    assert watermark.mode == 'L'
    image = Image.open(image_in_path)
    assert image.mode == 'RGB'
    assert watermark.size == image.size

    # Get pixel access objects
    watermark_pixels = watermark.load()
    image_pixels = image.load()

    # Watermark each pixel
    for x in range(image.size[0]):
        for y in xrange(image.size[1]):
            # Get the tuple of rgb values and convert to a list (mutable)
            rgb = list(image_pixels[x, y])
            for i, p in enumerate(rgb):
                # Divide the watermark pixel by 100 (r), then 10 (g), then 1 (b)
                # Then take it modulo 10 to get the last digit
                watermark_digit = (watermark_pixels[x, y] / (10 ** (2 - i))) % 10
                # Divide and multiply value by 10 to zero the last digit
                # Then add the watermark digit
                rgb[i] = (p / 10) * 10 + watermark_digit
            # Convert back to a tuple and store in the image
            image_pixels[x, y] = tuple(rgb)

    # Save the image
    image.save(image_out_path)
于 2013-07-17T19:26:22.997 に答える
0

画像に透かしを入れることに興味がある場合は、ステガノグラフィーをご覧になることをお勧めします。例として、Digital_Sightは概念の実用的なデモンストレーションであり、透かしとして使用されるテキストを格納するための基礎として使用できます。画像内のさまざまなピクセル ビットを変更するとその品質がどのように変化するかを調べるには、どのデータを上書きするかを決定する前に、 Color_Disruptorを試してみることをお勧めします。


Digital_Sight

import cStringIO
from PIL import Image
import bz2
import math

################################################################################

PIXELS_PER_BLOCK = 4
BYTES_PER_BLOCK = 3
MAX_DATA_BYTES = 16777215

################################################################################

class ByteWriter:

    "ByteWriter(image) -> ByteWriter instance"

    def __init__(self, image):
        "Initalize the ByteWriter's internal variables."
        self.__width, self.__height = image.size
        self.__space = bytes_in_image(image)
        self.__pixels = image.load()

    def write(self, text):
        "Compress and write the text to the image's pixels."
        data = bz2.compress(text)
        compressed_size = len(data)
        if compressed_size > self.__space:
            raise MemoryError('There is not enough space for the data!')
        size_data = self.__encode_size(compressed_size)
        tail = '\0' * ((3 - compressed_size) % 3)
        buffer = size_data + data + tail
        self.__write_buffer(buffer)

    @staticmethod
    def __encode_size(number):
        "Convert number into a 3-byte block for writing."
        data = ''
        for _ in range(3):
            number, lower = divmod(number, 256)
            data = chr(lower) + data
        return data

    def __write_buffer(self, buffer):
        "Write the buffer to the image in blocks."
        addr_iter = self.__make_addr_iter()
        data_iter = self.__make_data_iter(buffer)
        for trio in data_iter:
            self.__write_trio(trio, addr_iter.next())

    def __make_addr_iter(self):
        "Iterate over addresses of pixels to write to."
        addr_group = []
        for x in range(self.__width):
            for y in range(self.__height):
                addr_group.append((x, y))
                if len(addr_group) == 4:
                    yield tuple(addr_group)
                    addr_group = []

    @staticmethod
    def __make_data_iter(buffer):
        "Iterate over the buffer a block at a time."
        if len(buffer) % 3 != 0:
            raise ValueError('Buffer has a bad size!')
        data = ''
        for char in buffer:
            data += char
            if len(data) == 3:
                yield data
                data = ''

    def __write_trio(self, trio, addrs):
        "Write a 3-byte block to the pixels addresses given."
        duo_iter = self.__make_duo_iter(trio)
        tri_iter = self.__make_tri_iter(duo_iter)
        for (r_duo, g_duo, b_duo), addr in zip(tri_iter, addrs):
            r, g, b, a = self.__pixels[addr]
            r = self.__set_two_bits(r, r_duo)
            g = self.__set_two_bits(g, g_duo)
            b = self.__set_two_bits(b, b_duo)
            self.__pixels[addr] = r, g, b, a

    @staticmethod
    def __make_duo_iter(trio):
        "Iterate over 2-bits that need to be written."
        for char in trio:
            byte = ord(char)
            duos = []
            for _ in range(4):
                byte, duo = divmod(byte, 4)
                duos.append(duo)
            for duo in reversed(duos):
                yield duo

    @staticmethod
    def __make_tri_iter(duo_iter):
        "Group bits into their pixel units for writing."
        group = []
        for duo in duo_iter:
            group.append(duo)
            if len(group) == 3:
                yield tuple(group)
                group = []

    @staticmethod
    def __set_two_bits(byte, duo):
        "Write a duo (2-bit) group to a pixel channel (RGB)."
        if duo > 3:
            raise ValueError('Duo bits has to high of a value!')
        byte &= 252
        byte |= duo
        return byte

################################################################################

class ByteReader:

    "ByteReader(image) -> ByteReader instance"

    def __init__(self, image):
        "Initalize the ByteReader's internal variables."
        self.__width, self.__height = image.size
        self.__pixels = image.load()

    def read(self):
        "Read data out of a picture, decompress the data, and return it."
        compressed_data = ''
        addr_iter = self.__make_addr_iter()
        size_block = self.__read_blocks(addr_iter)
        size_value = self.__block_to_number(size_block)
        blocks_to_read = math.ceil(size_value / 3.0)
        for _ in range(blocks_to_read):
            compressed_data += self.__read_blocks(addr_iter)
        if len(compressed_data) != blocks_to_read * 3:
            raise ValueError('Blocks were not read correctly!')
        if len(compressed_data) > size_value:
            compressed_data = compressed_data[:size_value]
        return bz2.decompress(compressed_data)

    def __make_addr_iter(self):
        "Iterate over the pixel addresses in the image."
        addr_group = []
        for x in range(self.__width):
            for y in range(self.__height):
                addr_group.append((x, y))
                if len(addr_group) == 4:
                    yield tuple(addr_group)
                    addr_group = []

    def __read_blocks(self, addr_iter):
        "Read data a block at a time (4 pixels for 3 bytes)."
        pixels = []
        for addr in addr_iter.next():
            pixels.append(self.__pixels[addr])
        duos = self.__get_pixel_duos(pixels)
        data = ''
        buffer = []
        for duo in duos:
            buffer.append(duo)
            if len(buffer) == 4:
                value = 0
                for duo in buffer:
                    value <<= 2
                    value |= duo
                data += chr(value)
                buffer = []
        if len(data) != 3:
            raise ValueError('Data was not decoded properly!')
        return data

    @classmethod
    def __get_pixel_duos(cls, pixels):
        "Extract bits from a given group of pixels."
        duos = []
        for pixel in pixels:
            duos.extend(cls.__extract_duos(pixel))
        return duos

    @staticmethod
    def __extract_duos(pixel):
        "Retrieve the bits stored in a pixel."
        r, g, b, a = pixel
        return r & 3, g & 3, b & 3

    @staticmethod
    def __block_to_number(block):
        "Convert a block into a number (size of data buffer)."
        value = 0
        for char in block:
            value <<= 8
            value |= ord(char)
        return value

################################################################################

def main(picture, mode, text):
    "Dispatch the various operations that can be requested."
    image = Image.open(picture)
    if image.mode != 'RGBA':
        image = image.convert('RGBA')
    if mode == 'Evaluate':
        evaluate(image)
    elif mode == 'Simulate':
        simulate(image, text)
    elif mode == 'Encode':
        encode(image, text)
    elif mode == 'Decode':
        decode(image)
    else:
        raise ValueError('Mode %r was not recognized!' % mode)

################################################################################

def evaluate(image):
    "Display the number of bytes available in the image."
    print 'Usable bytes available =', bytes_in_image(image)

def bytes_in_image(image):
    "Calculate the number of usable bytes in an image."
    blocks = blocks_in_image(image)
    usable_blocks = blocks - 1
    usable_bytes = usable_blocks * BYTES_PER_BLOCK
    return min(usable_bytes, MAX_DATA_BYTES)

def blocks_in_image(image):
    "Find out how many blocks are in an image."
    width, height = image.size
    pixels = width * height
    blocks = pixels / PIXELS_PER_BLOCK
    return blocks

################################################################################

def simulate(image, text):
    "Find out how much space the text takes in the image that was given."
    compressed_data = bz2.compress(text)
    compressed_size = len(compressed_data)
    usable_bytes = bytes_in_image(image)
    space_leftover = usable_bytes - compressed_size
    if space_leftover > 0:
        print 'You still have %s more bytes for storage.' % space_leftover
    elif space_leftover < 0:
        print 'You overfilled the image by %s bytes.' % -space_leftover
    else:
        print 'This is a perfect fit!'

################################################################################

def encode(image, text):
    "Encodes text in image and returns picture to the browser."
    mutator = ByteWriter(image)
    mutator.write(text)
    output = cStringIO.StringIO()
    image.save(output, 'PNG')
    output.seek(0)
    print 'Content-Type: image/PNG'
    print output.read()

################################################################################

def decode(image):
    "Extract the original message and deliver it to the client."
    accessor = ByteReader(image)
    buffer = accessor.read()
    print buffer

################################################################################

if __name__ == '__builtin__':
    try:
        main(cStringIO.StringIO(PICTURE), MODE, TEXT)
    except SystemExit:
        pass

Color_Disruptor

from cStringIO import StringIO
from PIL import Image
from random import randrange

def main(data, r_bits, g_bits, b_bits, a_bits):
    image = Image.open(data)
    if image.mode != 'RGBA':
        image = image.convert('RGBA')
    width, height = image.size
    array = image.load()
    data.close()
    for x in range(width):
        for y in range(height):
            r, g, b, a = array[x, y]
            r ^= randrange(r_bits)
            g ^= randrange(g_bits)
            b ^= randrange(b_bits)
            a ^= randrange(a_bits)
            array[x, y] = r, g, b, a
    data = StringIO()
    image.save(data, 'PNG')
    print 'Content-Type: image/PNG'
    print data.getvalue()

if __name__ == '__builtin__':
    main(StringIO(DATA), *map(lambda bits: 1 << int(bits), (R, G, B, A)))
于 2013-07-17T21:03:09.160 に答える