How to hide an image inside another image?

At first glance, there's nothing special on below image..

..but in fact, on this picture is hidden another one:

It was done, by using steganography, the second image was stored on two lowest bits of the pixels. On two bits we may save values from 0 to 7, pixel in the image may have values from 0 to 255, so such small change is not visible to the human eye.

I used this python script:

#!/usr/bin/env python
import sys
import Image, ImageOps

def extract_image(from_image, s=4):
    data = Image.open(from_image) 
    for x in range(data.size[0]):
        for y in range(data.size[1]):
            p = data.getpixel((x, y))
            red   = (p[0] % s) * 255 / s
            green = (p[1] % s) * 255 / s
            blue  = (p[2] % s) * 255 / s
            data.putpixel((x, y), (red, green, blue))
    return data

def hide_image(public_img, secret_img, s=4):
    data = Image.open(public_img)
    key =  ImageOps.autocontrast(Image.open(secret_img))
    for x in range(data.size[0]):
        for y in range(data.size[1]):
            p = data.getpixel((x, y))
            q = key.getpixel((x, y))
            red   = p[0] - (p[0] % s) + (s * q[0] / 255)
            green = p[1] - (p[1] % s) + (s * q[1] / 255)
            blue  = p[2] - (p[2] % s) + (s * q[2] / 255)
            data.putpixel((x, y), (red, green, blue))
    return data

def show_help():
        print "wrong usage, try:"
        print "%s show from.jpg" % sys.argv[0]
        print "%s hide public.jpg secret.jpg" % sys.argv[0]

if __name__=="__main__":

    if len(sys.argv) != 3 and len(sys.argv) != 4:
       show_help()

    elif sys.argv[1] == "show":
        print "image will be saved as extracted.png"
        extract_image(sys.argv[2]).save("extracted.png");

    elif sys.argv[1] == "hide":
        hide_image(sys.argv[2], sys.argv[3]).save("hidden.png")

    else:
        show_help()

Below is example of usage, first line shows how to hide image, second shows how to extract image.

bash-3.2$ ./hider.py hide kosciol_Marii_Magdaleny.jpg GOR-Rabka.JPG  
bash-3.2$ ./hider.py show hidden.png 

13 comments:

  1. The script collapses if images have different sizes.

    ReplyDelete
    Replies
    1. Hi,

      you're right, but IMHO it would fix would overcomplicate the script, image can be adjusted in Gimp or similar program.

      If a simple resize is ok for you, then you can probably replace this line:
      key = ImageOps.autocontrast(Image.open(secret_img))

      by this one:
      key = ImageOps.autocontrast(Image.open(secret_img).resize(data.size))

      Robert

      Delete
  2. would be great to have a browser plugin that would check those bits in each image and analyze if there is a hidden image in them. it's so simple algorithm that i'm sure it's used somewhere on the internet

    ReplyDelete
  3. hey,
    how do you get the modules you import at the top of your program?

    ReplyDelete
    Replies
    1. Hi Jacob,

      what operating system are you using?

      The script was written for Python 2.x versions, not the current one, so you need to have installed some version of the Python 2.x.

      The only modules you need to have installed by yourself are Image, ImageOps, they came from the PIL (python image library). Note: PIL library is an abandoned project.

      Sorry for late replay, if you have any questions, please tell me.

      Have fun with this script :-)


      -Bob

      Delete
  4. Cool post!
    As it turns out, I have created an iPhone/iPad app that performs a similar operation (was just approved). This app also allows you to hide one photo within another. It's free so please try it out: https://itunes.apple.com/us/app/veil-hide-and-reveal-photos/id1171571496

    ReplyDelete
  5. That's a great code and idea!

    How about using a password to hide image and then ask the password to show? would it be possible?

    thanks!!!

    ReplyDelete
  6. It keeps saying that it doesn't support the image mode of the images, even though they're both rbg mode pngs

    ReplyDelete
    Replies
    1. The error is coming from ImageOps, line 59. Here's the traceback.

      Traceback (most recent call last):
      File "C:\Users\alexa\Pictures\0\imageHider.py", line 44, in
      hide_image(sys.argv[2], sys.argv[3]).save("hidden.png")
      File "C:\Users\alexa\Pictures\0\imageHider.py", line 18, in hide_image
      key = ImageOps.autocontrast(Image.open(secret_img))
      File "C:\Python27\lib\site-packages\PIL\ImageOps.py", line 138, in autocontrast
      return _lut(image, lut)
      File "C:\Python27\lib\site-packages\PIL\ImageOps.py", line 59, in _lut
      raise IOError, "not supported for this image mode"
      IOError: not supported for this image mode

      I'm not sure what I could be doing wrong. (although I am new to python)

      Delete
  7. Do you know how to do this in Java?

    ReplyDelete