通过在RGB元组中插入灰度值,使用PIL将灰度图像插入RGB图像 [英] Using PIL to insert greyscale image into RGB image by inserting greyscale values in RGB tuple

查看:102
本文介绍了通过在RGB元组中插入灰度值,使用PIL将灰度图像插入RGB图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写几个函数,第一个函数将灰度位图图像插入到另一个颜色位图图像中.现在,我的目标基本上是获取灰度像素图像的每个数字(例如123)并替换每个RGB像素(244、244、244)的结束数字,因此它基本上会像这样(241、242、243)结束).从本质上讲,这是在彩色图像上添加灰度图像.

I am writing several functions, the first one inserts a greyscale bitmap image into another colour bitmap image. Now my aim is basically to take each digit of the greyscale pixel image (eg. 123) and replace the end digit of every RGB pixel (244, 244, 244), so it would basically end up like this (241, 242, 243). Essentially this is watermarking the colour image with the greyscale image.

到目前为止,以下代码是我可以得到的,我能够返回列表中的元组值,我只是不知道如何在较大图像中操纵较小灰度图像大小的空间.

The following code is what I have so far, i'm able to return the tuple values in a list, i just do not know how to manipulate a space the size of a smaller greyscale image in a larger image.

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)

有人可以提供一些建议吗?

Can anyone offer some advice?

推荐答案

如果您对图像加水印感兴趣,则可能需要看看隐秘术.例如, Digital_Sight 是该概念的有效示例,可以用作存储所用文本的基础.作为水印.要研究修改图像中的各种像素位如何改变其质量,您可能需要试用 Color_Disruptor ,然后再决定要覆盖哪些数据.

If you are interested in watermarking images, you might want to take a look at steganography. As an example, Digital_Sight is a working demonstration of the concept and could be used as a basis for storing text used as a watermark. To study how modifying various pixel-bits in an image can alter its quality, you might want to play around with Color_Disruptor before deciding what data to overwrite.

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

这篇关于通过在RGB元组中插入灰度值,使用PIL将灰度图像插入RGB图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆