0

While learning numpy, I wrote code which doing LSB(steganography) encryption:

def str2bits_nparray(s):
    return np.array(map(int, (''.join(map('{:07b}'.format, bytearray(s))))), dtype=np.bool)

def LSB_encode(img, msg, channel):
    msg_bits = str2bits_nparray(msg)
    xor_mask = np.zeros_like(img, dtype=np.bool)
    xor_mask[:, :, channel].flat[:len(msg_bits)] = np.ones_like(msg_bits, dtype=np.bool)
    img[xor_mask] = img[xor_mask] >> 1 << 1 | msg_bits


msg = 'A' * 1000
img_name = 'screenshot.png'
chnl = 2
img = imread(img_name)
LSB_encode(img, msg, chnl)

Code works fine, but when i'm trying to made chnl = [2, 1] this line:

xor_mask[:, :, channel].flat[:len(msg_bits)] = np.ones_like(msg_bits, dtype=np.bool)

doesnt assign value to xor_mask with

xor_mask[:, :,[2, 1]].flat[:len(msg_bits)]

Is there way to fix this?

I tryed solution with for-loop over channels:

for ch in channel:
    xor_mask[:, :, ch].flat[:len(msg_bits)] = np.ones_like(msg_bits, dtype=np.bool)

But this is doing not that i want from

xor_mask[:, :,[2, 1]].flat[:len(msg_bits)] = np.ones_like(msg_bits, dtype=np.bool)

1 Answer 1

1

IIUC, here's an approach to get the linear indices, then slice to the length of no. of elems required to be set and then perform the setting -

m,n,r = xor_mask.shape  # Store shape info

# Create range arrays corresponding to those shapes
x,y,z = np.ix_(np.arange(m),np.arange(n),channel)

# Get the indices to be set and finaally perform the setting
idx = (x*n*r + y*r + z).ravel()[:len(msg_bits)]
xor_mask.ravel()[idx] = 1

Sample run -

In [180]: xor_mask
Out[180]: 
array([[[25, 84, 37, 96, 72, 84, 91],
        [94, 56, 78, 71, 48, 65, 98]],

       [[33, 56, 14, 92, 90, 64, 76],
        [71, 71, 77, 31, 96, 36, 49]]])

In [181]: # Other inputs
     ...: channel = np.array([2,1])
     ...: msg_bits = np.array([2,3,6,1,4])
     ...: 

In [182]: m,n,r = xor_mask.shape  # Store shape info
     ...: x,y,z = np.ix_(np.arange(m),np.arange(n),channel)
     ...: idx = (x*n*r + y*r + z).ravel()[:len(msg_bits)]
     ...: xor_mask.ravel()[idx] = 1
     ...: 

In [183]: xor_mask # First 5 elems from flattend version
                   # of xor_mask[:,:,channel] set as 1 
                   # as len(msg_bits) = 5.
Out[183]: 
array([[[25,  1,  1, 96, 72, 84, 91],
        [94,  1,  1, 71, 48, 65, 98]],

       [[33, 56,  1, 92, 90, 64, 76],
        [71, 71, 77, 31, 96, 36, 49]]])

Instead, if you were trying to set for all elems across all dimensions in 3D input array along the first of channel : 2 and then along the second one 1 and so on, we need to create idx differently, like so -

idx = (x*n*r + y*r + z).transpose(2,0,1).ravel()[:len(msg_bits)]
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.