Encode an image with Feistel cipher and Python - python

I'm trying to encode an image using the Feistel cipher. The idea was to take an image file, read byte per byte the image, encode every byte with my Feistel cipher, and re-create a new image with the encoded byte generated by the cipher.
Unfortunately, I've found out most of the common image formats use headers that, once encoded, make the encoded image corrupted.
Having a look at the PIL package I've found out the PIL.Image.frombytes function is able to create an image object given a BytesIO object. With the image object, I'm able to recreate the image with the save function
My issue now is, how to open an image and read the actual image payload in bytes that I need to process with my Feistel cipher.
If I use the code
with open('image.jpg', 'rb') as file:
data = file.read()
I read the whole file, including the headers, which I don't need

The best solution is to encode every pixel of the image by themselves and then re-create the image with the new pixels encoded
#open image file to process
im = Image.open(file_name, 'r')
#get pixel value
pix_val = list(im.getdata())
#get dimensions
width, height = im.size
tmp = 0
#create image file processed
imge = Image.new("RGB", (width, height))
pix = imge.load() #load the new pixel color
# create image encoded
for x in range(height):
for y in range(width):
#double loop to encode every pixel
pix[y,x] = (encode(pix_val[tmp][0]),encode(pix_val[tmp][1]),encode(pix_val[tmp][2]))
tmp += 1
take note that the function encode should be your Feistel cipher function. In this case, it takes an 8-bit integer and returns an 8-bit integer (which is the representation of each pixel's color but it can be edited according to your needs

Related

How can i import an image file into python , read it as an array, then output the array as same image file type

I am tasked with writing a program that can take an image file as input, then encrypt the image with some secondary code that i have already written, and finally output the encrypted image file.
I would like to import an image, make it a 1d array of numbers, perform some encryption on this 1d array (which will make it into a 2d array which i will flatten), and then be able to output the encrypted 1d array as an image file after converting it back to whatever format it was in on input.
I am wondering how this can be done, and details about what types of image files can be accepted, and what libraries may be required.
Thanks
EDIT:
this is some code i have used, img_arr stores the image in an array of integers, max 255. This is what i want, however now i require to convert back into the original format, after i have performed some functions on img_arr.
from PIL import Image
img = Image.open('testimage.jfif')
print('img first: ',img)
img = img.tobytes()
img_arr=[]
for x in img:
img_arr.append(x)
img2=Image.frombytes('RGB',(460,134),img)
print('img second: ',img2)
my outputs are slightly different
img first: <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=460x134 at 0x133C2D6F970>
img second: <PIL.Image.Image image mode=RGB size=460x134 at 0x133C2D49EE0>
In programming, Base64 is a group of binary-to-text encoding schemes that represent binary data (more specifically, a sequence of 8-bit bytes) in an ASCII string format by translating the data into a radix-64 representation.
Fortunately, you can encode and decode the image binary file with python based on base64. The following link helps you.
Encoding an image file with base64

Encoding gif to base64 without writing to disk python

I am able to encode a gif to base64 by saving the data first.
imageio.mimsave(output_fn, [img_as_ubyte(frame) for frame in gif], fps=original_fps)
with open(output_fn, "rb") as gif_file:
detect_base64 = 'data:image/gif;base64,{}'.format(base64.b64encode(gif_file.read()).decode())
I need to find a way to encode the gif above in the form an array of images, with the corresponding fps into base64 without the need of saving it into output_fn first.
The general approach is to use a BytesIO as a replacement for an open file, i.e.
gif_file = io.BytesIO()
imageio.mimsave(gif_file, [img_as_ubyte(frame) for frame in gif], 'GIF', fps=original_fps)
detect_base64 = 'data:image/gif;base64,{}'.format(base64.b64encode(gif_file.getvalue()).decode())

Why is base64encoded output different for the same image?

This function below reads an image and converts it into base64 image string
def getBase64Image(filePath):
with open(filePath, "rb") as img_file:
my_string = base64.b64encode(img_file.read())
my_string = my_string.decode('utf-8')
return my_string
However, the function below take image as array(loaded from OpenCV) and converts it into base64 image string
def convertToBase64(image):
image.tobytes()
my_string = base64.b64encode(image)
my_string = my_string.decode('utf-8')
return my_string
The output string from the first function differs from the string produced by the second function. Why is that?
Ideally, I want the second function to produce the same base64 string as the first function.
Please, can someone guide me on how I can achieve this?
You first function uses the PNG/JPG image data "as-is" and encodes it.
Your second function uses RAW bytes from the RGB or grayscale representation of the image and encodes that. If you want to convert RAW RGB to an image, you may use cv2.imencode() which will ouput PNG or JPG or whatever you like.
def convertToBase64(image):
#image.tobytes() -- don't need this
_, converted = cv2.imencode( '.png', image) # here the magic happens
my_string = base64.b64encode(converted) # and here
my_string = my_string.decode('utf-8')
return my_string
And yeah, just in case it's not clear. You DON'T have to save the encoded image anywhere, it's all happening in memory.
You are encoding fundamentally different structures. The first method is reading in the bytes of a compressed image format such as jpeg or png. Even if it is a bitmap image, there is a lot of extra data stored in the image that is not in the raw array data.
The second method is taking an h x w x 3 array of the pixel data, converting it to a byte string, and then 64bit encoding it. You can see the difference by comparing the byte strings of black and white data array compared to the saved bitmap image.

Tensorflow: How to encode and read bmp images?

I am trying to read .bmp images, do some augmentation on these, save them to a .tfrecords file and then open the .tfrecords files and use the images for image classification. I know that there is a tf.image.encode_jpeg() and a tf.image.encode_png() function, but there is no tf.image.encode_bmp() function. I know that .bmp images are uncompressed, so I've tried to simply base64-encode, np.tostring() and np.tobytes() the images, but I get the following error when trying to decode these formats:
tensorflow.python.framework.errors_impl.InvalidArgumentError: channels attribute 3 does not match bits per pixel from file <some long number>
My take is that tensorflow, in its encoding to jpeg or png, does something extra with the byte encoding of the images; saving information about array dimensionality, etc. However, I am quite clueless about this, so any help would be great!
Some code to show what it is I am trying to achieve:
with tf.gfile.FastGFile(filename, 'rb') as f:
image_data = f.read()
bmp_data = tf.placeholder(dtype=tf.string)
decode_bmp = tf.image.decode_bmp(self._decode_bmp_data, channels=3)
augmented_bmp = <do some augmentation on decode_bmp>
sess = tf.Session()
np_img = sess.run(augmented_bmp, feed_dict={bmp_data: image_data})
byte_img = np_img.tostring()
# Write byte_img to file using tf.train.Example
writer = tf.python_io.TFRecordWriter(<output_tfrecords_filename>)
example = tf.train.Example(features=tf.train.Features(feature={
'encoded_img': tf.train.Feature(bytes_list=tf.train.BytesList(value=[byte_img])}))
writer.write(example.SerializeToString())
# Read img from file
dataset = tf.data.TFRecordDataset(<img_file>)
dataset = dataset.map(parse_img_fn)
The parse_img_fn may be condensed to the following:
def parse_img_fn(serialized_example):
features = tf.parse_single_example(serialized_example, feature_map)
image = features['encoded_img']
image = tf.image.decode_bmp(image, channels=3) # This is where the decoding fails
features['encoded_img']
return features
in your comment, surely you mean encode instead of encrypt
The BMP file format is quite simplistic, consisting of a bunch of headers and pretty much raw pixel data. This is why BMP images are so big. I suppose this is also why TensorFlow developers did not bother to write a function to encode arrays (representing images) into this format. Few people still use it. It is recommended to use PNG instead, which performs lossless compression of the image. Or, if you can deal with lossy compression, use JPG.
TensorFlow doesn't do anything special for encoding images. It just returns the bytes that represent the image in that format, similar to what matplotlib does when you do save_fig (except MPL also writes the bytes to a file).
Suppose you produce a numpy array where the top rows are 0 and the bottom rows are 255. This is an array of numbers which, if you think it as a picture, would represent 2 horizontal bands, the top one black and the bottom one white.
If you want to see this picture in another program (GIMP) you need to encode this information in a standard format, such as PNG. Encoding means adding some headers and metadata and, optionally, compressing the data.
Now that it is a bit more clear what encoding is, I recommend you work with PNG images.
with tf.gfile.FastGFile('image.png', 'rb') as f:
# get the bytes representing the image
# this is a 1D array (string) which includes header and stuff
raw_png = f.read()
# decode the raw representation into an array
# so we have 2D array representing the image (3D if colour)
image = tf.image.decode_png(raw_png)
# augment the image using e.g.
augmented_img = tf.image.random_brightness(image)
# convert the array back into a compressed representation
# by encoding it into png
# we now end up with a string again
augmented_png = tf.image.encode_png(augmented_img, compression=9)
# Write augmented_png to file using tf.train.Example
writer = tf.python_io.TFRecordWriter(<output_tfrecords_filename>)
example = tf.train.Example(features=tf.train.Features(feature={
'encoded_img': tf.train.Feature(bytes_list=tf.train.BytesList(value=[augmented_png])}))
writer.write(example.SerializeToString())
# Read img from file
dataset = tf.data.TFRecordDataset(<img_file>)
dataset = dataset.map(parse_img_fn)
There are a few important pieces of advice:
don't use numpy.tostring. This returns a HUUGE representation because each pixel is represented as a float, and they are all concatenated. No compression, nothing. Try and check the file size :)
no need to pass back into python by using tf.Session. You can perform all the ops on TF side. This way you have an input graph which you can reuse as part of an input pipeline.
There is no encode_bmp in the tensorflow main package, but if you import tensorflow_io (also a Google officially supported package) you can find the encode_bmp method there.
For the documentation see:
https://www.tensorflow.org/io/api_docs/python/tfio/image/encode_bmp

Extracting images from tfrecords files with protobuf without running a TensorFlow session

I'm using TensorFlow in Python and I have the data stored in TFRecords files containing tf.train.Example protocol buffers.
I'm trying to extract the fields stored in each example (in the code example below these are height, width, image), without the need to run a TensorFlow session. And by trial and error I found the following code to work OK:
import numpy as np
import tensorflow as tf
def _im_feature_to_im(example, key):
feature_ser = example.features.feature[key].bytes_list.SerializeToString()
feature_ser_clean = feature_ser[4:]
image = np.fromstring(feature_ser_clean, dtype=np.uint8).reshape((height, width))
return image
for serialized_example in tf.python_io.tf_record_iterator(tfrec_filename):
example = tf.train.Example()
example.ParseFromString(serialized_example)
# traverse the Example format to get data
height = example.features.feature['height'].int64_list.value[0]
width = example.features.feature['width'].int64_list.value[0]
image = _im_feature_to_im(example, 'image')
So:
int fields are extracted easily.
But my question is regarding the extraction of the image: why do I have to remove 4 bytes from the start of the bytes array in order to get the original image? Is there some header there?
That's the key for protocol buffer encoding.
https://developers.google.com/protocol-buffers/docs/encoding
You can print it out and follow the instructions at the above website to decode it. Most likely it's some encoding of tag = 1, type = 2, length = height * width.
Hope that helps!
Sherry
What you are doing in _im_feature_to_im() is to encode a message to string by calling .SerializeToString() and then to decode it by hand by removing the first 4 bytes (or, as you said in the comment, by removing all the bytes with the MSB set). This is just a redundant operation.
Instead, you can get your image by accessing the value property:
image_string = example.features.feature[key].bytes_list.value[0]
Note that this is an array of one element, hence the [0] at the end.
You can then construct the array from this, like you did:
image_arr = np.frombuffer(image_string, dtype=np.uint8)
Now, many times the images are put in tfrecords with their encoded representation (e.g. PNG or JPG), because it takes significantly less space than the raw bytes. What this means is that you should then decode the image. Tensorflow has the decode_image(...) function for this, but it will return a tensor, and you want to do this without a TF session.
You can use OpenCV to decode the image representation without a TF Session:
import cv2
image = cv2.imdecode(image_arr, cv2.IMREAD_UNCHANGED)
assert image is not None, "Could not decode image"

Categories