【问题标题】:Using PIL to insert greyscale image into RGB image by inserting greyscale values in RGB tuple使用 PIL 通过在 RGB 元组中插入灰度值将灰度图像插入 RGB 图像
【发布时间】:2013-05-10 02:35:26
【问题描述】:

我正在编写几个函数,第一个函数将灰度位图图像插入另一个彩色位图图像。现在我的目标基本上是获取灰度像素图像的每个数字(例如 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)

谁能给点建议?

【问题讨论】:

    标签: python python-imaging-library


    【解决方案1】:

    你在正确的轨道上。这就是您操作像素的方式,尽管您可以使用pixel access objects 更快地完成操作,如下所示。

    除了提取和设置正确的数字外,这一切都非常简单。在此示例中,我通过除以 10 的幂和使用模运算符来完成此操作,尽管还有其他方法。希望 cmets 解释得足够清楚。

    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)
    

    【讨论】:

      【解决方案2】:

      如果您对图像加水印感兴趣,不妨看看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)))
      

      【讨论】:

        猜你喜欢
        • 2018-08-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-26
        • 2014-12-22
        • 2015-07-11
        • 1970-01-01
        相关资源
        最近更新 更多