Search code examples
python-imaging-libraryimage-compression

Saving an Image stored in BytesIO in pillow


I have an image stored as BytesIO of Pillow and I need to save it to a file with some header information (containing textual attributes) that I need to add specific to my problem. I need the bytes to be represented according to some image compression format. Would that be possible? If yes, how it can be done? I also need to store more than one image in the file.


Solution

  • Storing more than one image in a file is problematic for PNG, JPEG and the most of the common formats. One option for that is TIFF - not sure if that works for you?

    Here's how you can store some additional text in a PNG at least:

    #!/usr/bin/env python3
    
    from PIL.PngImagePlugin import Image, PngInfo
    
    # Create empty metadata and add a couple of text strings
    metadata = PngInfo()
    metadata.add_text("Key1:","Value1")
    metadata.add_text("Key2:","Value2")
    
    # Create red image and save with metadata embedded
    im = Image.new('RGB',(64,64),'red')
    im.save("result.png", pnginfo=metadata)
    

    enter image description here

    If you check that with pngcheck you will see:

    pngcheck -7v result.png
    

    Sample Output

    File: result.png (200 bytes)
      chunk IHDR at offset 0x0000c, length 13
        64 x 64 image, 24-bit RGB, non-interlaced
      chunk tEXt at offset 0x00025, length 12, keyword: Key1:
        Value1
      chunk tEXt at offset 0x0003d, length 12, keyword: Key2:
        Value2
      chunk IDAT at offset 0x00055, length 95
        zlib: deflated, 32K window, default compression
      chunk IEND at offset 0x000c0, length 0
    No errors detected in result.png (5 chunks, 98.4% compression).
    

    Here's how to save 3 images and a comment in a single TIFF file:

    from PIL import Image 
    from PIL.TiffImagePlugin import ImageFileDirectory_v2, TiffTags 
    
    # Create a structure to hold meta-data
    ifd = ImageFileDirectory_v2() 
    ifd[270] = 'Some Funky Comment' 
    ifd.tagtype[270] = TiffTags.ASCII 
    
    # Create red image and save with metadata embedded 
    im1 = Image.new('RGB',(50,50),'red') 
    im2 = Image.new('RGB',(64,64),'green') 
    im3 = Image.new('RGB',(80,80),'blue') 
    im1.save("result.tif", append_images[im2,im3], save_all=True, tiffinfo=ifd)
    

    And check that with:

    tiffinfo -v result.tif
    

    Sample Output

    TIFF Directory at offset 0x8 (8)
      Image Width: 50 Image Length: 50
      Bits/Sample: 8
      Compression Scheme: None
      Photometric Interpretation: RGB color
      Samples/Pixel: 3
      Rows/Strip: 50
      Planar Configuration: single image plane
      ImageDescription: Some Funky Comment
    TIFF Directory at offset 0x1e08 (7688)
      Image Width: 64 Image Length: 64
      Bits/Sample: 8
      Compression Scheme: None
      Photometric Interpretation: RGB color
      Samples/Pixel: 3
      Rows/Strip: 64
      Planar Configuration: single image plane
      ImageDescription: Some Funky Comment
    TIFF Directory at offset 0x4eb8 (20152)
      Image Width: 80 Image Length: 80
      Bits/Sample: 8
      Compression Scheme: None
      Photometric Interpretation: RGB color
      Samples/Pixel: 3
      Rows/Strip: 80
      Planar Configuration: single image plane
      ImageDescription: Some Funky Comment
    

    enter image description here


    You can then extract the images on the command-line with ImageMagick like this.

    To extract first image:

    magick result.tif[0] first.png
    

    To extract last image:

    magick result.tif[-1] last.png
    

    To extract all three images:

    magick result.tif image-%d.png
    

    Result

    -rw-r--r--  1 mark  staff  457 21 Jan 08:11 image-0.png
    -rw-r--r--  1 mark  staff  458 21 Jan 08:11 image-1.png
    -rw-r--r--  1 mark  staff  460 21 Jan 08:11 image-2.png 
    

    Note: Use convert in place of magick above if you are running v6 ImageMagick.

    Keywords: Python, PIL, image processing, multiple images, TIF, comment, tiffinfo, IFD, PNG tEXt.