How does bit-wise operation work for encrypting grayscale images?
I have solved it.

The main reason was that I'm supposed to use LOGICAL operations instead of BITWISE operations.

The new results: https://imgur.com/a/NLa5Q8J

my function:

def logical_xor(message, key, op):
    message = np.asarray(message)
    key = np.asarray(key)
    new_image = []
    row=0
    col=0
    while row &lt; len(message):
        new_row = []
        col = 0
        while col &lt; len(message[row]):
            if op == 'and':
                new_row.append(message[row, col] and key[row, col]) # and instead of &amp;
            elif op == 'or':
                new_row.append(message[row, col] or key[row, col])
            else:
                new_row.append(message[row, col] !=  key[row, col]) # xor
            col += 1
        new_image.append(new_row)
        row+=1
    return new_image


but before using this function, I had to do this:

from PIL import Image
m = Image.open("test_image.jpg") # message
m = m.convert("1") # converts to boolean?