A toolset to help splitting images into their components and to perform logical masks on pixel values.
Especially amongst Geocachers it's a common riddle to hide information (e.g. coordinates) in an imagefile, but since EXIF-based hideouts are now well known, people search for more advanced steganography-methods to hide data.
A very common, but kinda hard to detect method (at least with most image-processing tools) is to make use of the fact, that changing the LSB (least significant bit) of the components (e.g. red, green, blue) of a pixel makes no difference for the human eye.
With the help of this toolset it's possible to extract that data easily.
To run this toolset properly, one needs to have the following libraries installed:
If you don't want to install them all by hand, I recommend a Python distrubution like Anaconda which comes with all needed libaries pre-installed.
Usage of the toolset is easy. Just get an instance of ImageTools, load the image you want to process and you're ready to go.
from imagetools import ImageTools
it = ImageTools()
it.Load("image.png")
Or more condensed:
from imagetools import ImageTools
it = ImageTools("image.png")
All methods in the upcoming examples yield iterators to PIL.Image.Image objects. If you rather prefer the raw data, pass toImage=False
to the method and you'll receive and iterator to the raw numpy.ndarrays instead.
If you want to split an image into its components, e.g. splitting a RGB-image into the red, green and blue component, call the SplitComponents
method:
from imagetools import ImageTools
it = ImageTools("image.png")
for i, component in enumerate(it.SplitComponents()):
component.save("component_%d.png" % (i))
If you need the bit-layers of an image, use SplitBitLayers
. Note: This will not seperate the image by its components, so for a normal 8-bit RGB image, it will return 8 images. If you also want to split the image by its components have a look at the next method!
from imagetools import ImageTools
it = ImageTools("image.png")
for i, layer in enumerate(it.SplitBitLayers()):
layer.save("bitlayer_%d.png" % (i))
If you want your layer to have more than one bit, just pass the bits
parameter with the number of desired bits. Note that the layers will overlap!
If you want to combine the SplitComponents
and SplitBitLayers
methods, use SplitBitLayersComponents
(who would have guessed it 😄). Note: The method will yield an array containing the bit-layers for each component.
from imagetools import ImageTools
it = ImageTools("image.png")
for c, component in enumerate(it.SplitBitLayersComponents()):
for l, layer in enumerate(component):
layer.save("component_%d_layer_%d.png" % (c, l))
Or course you can also pass the bits
parameter.
Sometimes you want to apply a special mask to a component of an image, e.g. "invert bit 0 and 2 of the second component".
This can be done via the three mask-methods GetAndMaskedComponent
, GetOrMaskedComponent
, GetXorMaskedComponent
.
Note: All three methods only work on a specific component, if you want to perform the same operation on all components, or maybe even different operations on different components, have a look at GetMaskedComponents
.
This will perform a logic AND-operation on all values of a given component.
For example the following code sets every second bit of the second component to zero.
from imagetools import ImageTools
it = ImageTools("image.png")
it.GetAndMaskedComponent(1, 0xaa).save("and_masked.png")
If you want to left-shift the values (useful if you only keep lower bits), pass the shift
parameter.
Performs a logic OR-operation on all values of a given component, have a look at GetAndMaskedComponent
for details.
Performs a logic XOR-operation on all values of a given component, have a look at GetAndMaskedComponent
for details.
If you want to perform multiple masks on multiple components, or just multiple masks on a single component, use GetMaskedComponents
.
To properly call that function you need to pass an array which consits of sub-arrays that look like the following:
[MASK_TYPE, COMPONENT, MASK(, SHIFT)]
MASK_TYPE can have the following values:
- "&": AND-Mask
- "|": OR-Mask
- "^": XOR-Mask
For example if you want to perform AND 0xaa
on the first component, OR 0xbb
on the second component and XOR 0xcc
on the third component, the code looks like the following:
from imagetools import ImageTools
it = ImageTools("image.png")
masks = [
["&", 0, 0xaa],
["|", 1, 0xbb],
["^", 2, 0xcc]
]
it.GetMaskedComponents(masks).save("masked.png")
If you want to perform the masks from above all on the first component, the masks would look like the following:
masks = [
["&", 0, 0xaa],
["|", 0, 0xbb],
["^", 0, 0xcc]
]
Note: This will return an image with only one component, the untouched components won't be added. If you would like to perserve them, just add an AND 0xff
or XOR 0x00
mask:
masks = [
["&", 0, 0xaa],
["|", 0, 0xbb],
["^", 0, 0xcc],
["&", 1, 0xff],
["&", 2, 0xff]
]
If you would like to create a new component for each mask instead of performing the masks all on the same component, just pass the multiMask=False
parameter:
from imagetools import ImageTools
it = ImageTools("image.png")
masks = [
["&", 0, 0xaa],
["|", 0, 0xbb],
["^", 0, 0xcc]
]
it.GetMaskedComponents(masks, multiMask=False).save("masked.png")
This will return an image with three components.