Ubdus Samad
Ubdus Samad

Reputation: 1215

Optimize element modification operation in an numpy array

So my code does a very basic form of image manipulation and stores a string into an image.

It achieves this primarily by converting image into a numpy array (x*y*3) and then making every numeric element odd at first.

Thus the array is now like : [ [ [odd_clr_val,odd_clr_val,odd_clr_vlue] ... ] .... ]

Now what I do is convert the text to be stored into binary data and modify as much of array as needed to elements having odd parity for representing zero and even for 1.

Now I just read it back with a simple program.

The code looks like:

from PIL import Image
import numpy as np
import time
#if the value of pixel is:
#Odd = 0 , even = 1
#We make every element odd

img = Image.open('Desktop/test.jpg')

arr = np.array(img)
x,y,z  = np.shape(arr)
count = 0
initial = time.time()

#This nested loop makes every element odd but is very slow and in need to be optimized
for i in range(x):
    for j in range(y):
        count += 1
        k = arr[i][j]
        arr[i][j][0] =  k[0] + int(not(k[0]%2)) # adds 1 if k[i] is odd else 0
        arr[i][j][1] =  k[1] + int(not(k[1]%2))
        arr[i][j][2] =  k[2] + int(not(k[2]%2))

print("Time delta: %f"%(time.time() - initial ))
print("The amount of data you can store in this image is: %d kiBs"%((count*3)/1024))
#every element of this image is odd now

z = input("Enter the string:")

long_array = []
for i in z:
    long_array += list(map(int,list(format(ord(i), '#010b')[2:])))


#everything is in binary now

counter = 0
try:
    for i in range(x):
        for j in range(y):
            k = arr[i][j]

            arr[i][j][0] = k[0] if not(long_array[counter]) else k[0]+1
            counter += 1

            arr[i][j][1] = k[1] if not(long_array[counter]) else k[1]+1
            counter += 1

            arr[i][j][2] = k[2] if not(long_array[counter]) else k[2]+1
            counter += 1
except IndexError:
    print("Done")
except:
    print("An unidentified error occured!")

image = Image.fromarray(arr)

image.show()

image.save("secret.png")

My problem is that i can't optimize the upper loop of my code as it takes about 16ish seconds to finish (with a 800x600x3 picture matrix). Adding to that the lower loop of my code is super fast in comparison to the upper one.

So is there a way to optimize my upper loop using some numpy magic?

Upvotes: 1

Views: 69

Answers (1)

Paul Panzer
Paul Panzer

Reputation: 53029

You can use bitwise arithmetic. Making all pixels odd can be done in one line:

arr |= 1

Embedding your bit string:

arr.ravel()[:len(long_array)] += np.array(long_array, arr.dtype)

Btw., adding one can create obvious pixel changes because of overflow. For example bright red (255, 1, 1) will become black (0, 2, 2). You can avoid this by instead subtracting one.

Upvotes: 1

Related Questions