2013-05-10 5 views
4

여러 함수를 쓰고 있습니다. 첫 번째 함수는 그레이 스케일 비트 맵 이미지를 다른 색상 비트 맵 이미지에 삽입합니다. 이제 내 목표는 기본적으로 그레이 스케일 픽셀 이미지 (예 : 123)의 각 자릿수를 가져 와서 모든 RGB 픽셀 (244, 244, 244)의 끝자리를 대체하므로 기본적으로 끝날 것입니다 (241, 242, 243). 본질적으로 이는 그레이 스케일 이미지로 컬러 이미지를 워터 마킹합니다.PIL을 사용하여 그레이 스케일 값을 RGB 튜플에 삽입하여 그레이 스케일 이미지를 삽입합니다.

다음 코드는 지금까지 가지고있는 목록에서 튜플 값을 반환 할 수 있습니다. 더 큰 이미지에서 더 작은 회색조 이미지의 크기를 조정하는 방법을 모르겠습니다.

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) 

누구로부터 조언을 제공받을 수 있습니까?

답변

0

당신은 올바른 길을 가고 있습니다. 그것이 픽셀을 조작하는 방법입니다. 아래에 표시된 것처럼 pixel access objects을 사용하여 조금 더 빠르게 할 수 있습니다.

정확한 숫자를 추출하고 설정하는 것을 제외하면 모두 매우 간단합니다. 이 예제에서는 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) 
+0

이 사용 해보세요나요? –

0

, 당신은 steganography를 살펴 할 수 있습니다. 예를 들어, 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))) 
관련 문제