<jupyter_start><jupyter_text>Least Significant Bit (LSB)<jupyter_code>import os
import numpy as np
import cv2
import matplotlib.pyplot as plt

# get required images
if not os.path.isdir('Images'):
  !wget -O watermarking-images.zip "https://drive.google.com/uc?export=download&id=1GOz-jcICYEFZwMgzZdNEP3j7ZMDXIOuo"
  !unzip watermarking-images.zip<jupyter_output>--2021-10-04 10:32:45--  https://drive.google.com/uc?export=download&id=1GOz-jcICYEFZwMgzZdNEP3j7ZMDXIOuo
Resolving drive.google.com (drive.google.com)... 192.168.127.12, 172.16.31.10, 172.16.58.3, ...
Connecting to drive.google.com (drive.google.com)|192.168.127.12|:443... connected.
HTTP request sent, awaiting response... 302 Moved Temporarily
Location: https://doc-04-9g-docs.googleusercontent.com/docs/securesc/ha0ro937gcuc7l7deffksulhg5h7mbp1/qfun9s8b0ajt28hfku1ftomgse7ab66g/1633343550000/05147498317285990661/*/1GOz-jcICYEFZwMgzZdNEP3j7ZMDXIOuo?e=download [following]
Warning: wildcards not supported in HTTP.
--2021-10-04 10:32:46--  https://doc-04-9g-docs.googleusercontent.com/docs/securesc/ha0ro937gcuc7l7deffksulhg5h7mbp1/qfun9s8b0ajt28hfku1ftomgse7ab66g/1633343550000/05147498317285990661/*/1GOz-jcICYEFZwMgzZdNEP3j7ZMDXIOuo?e=download
Resolving doc-04-9g-docs.googleusercontent.com (doc-04-9g-docs.googleusercontent.com)... 172.16.17.32, fc00:e968:6179::de52:7100
Connecting t[...]<jupyter_text>Embedding<jupyter_code># Read image and mark
im = cv2.imread('../Images/lena.bmp', 0)
print('Image shape: ', im.shape)

wat = cv2.imread('../Images/mark.png', 0)
print('Watermark shape: ', wat.shape)
# Flatted the images for easy embedding
res_flat = im.flatten()
wat_flat = wat.flatten()

N = res_flat.shape[0]

# Example of decoded pixel value
pixel = format(res_flat[0], "08b")
print(pixel)
print(type(pixel))
# Embedding
for i in range(N):
  im_pixel_to_binary = format(res_flat[i], "08b")
  wat_pixel_to_binary = format(wat_flat[i], "08b")
  
  # concate all the pixels until the LSB from the image
  # and the MSB of the watermark as LSB
  res_pixel = im_pixel_to_binary[:7] + wat_pixel_to_binary[0]

  res_flat[i] = int(res_pixel, 2)

# Reshape image
res = res_flat.reshape(512, 512)

# Show images side by side
plt.subplot(121)
plt.title('Original')
plt.imshow(im, cmap='gray')
plt.subplot(122)
plt.title('Watermarked')
plt.imshow(res,cmap='gray')
plt.show()

# save image
#res = Image.fromarray(res)
#res.save('watermarked.bmp')
cv2.imwrite('watermarked.bmp', res)<jupyter_output><empty_output><jupyter_text>QualityThere are several ways to compute the quality of an image.One of the most common is the Peak Signal to Noise Ratio (PSNR)$PSNR = 10 \cdot log_{10} \frac{V_{max}^2}{MSE}$<jupyter_code># Compute PSNR
psnr = cv2.PSNR(im, res)
print('PSNR: %.2fdB' % psnr)<jupyter_output>PSNR: 51.15dB<jupyter_text>Detection<jupyter_code># Read the watermarked image and flatten it
watermarked = cv2.imread('watermarked.bmp', 0)
watermarked = watermarked.flatten()

# Create empy flatten image
N = watermarked.shape[0]
extracted = np.zeros(N)

for i in range(N):
  extracted[i] = int(format(watermarked[i], "08b")[-1], 2)

# Reshape extracted image
extracted = extracted.reshape(512, 512)
# Read original watermark
mark = cv2.imread('Images/mark.png', 0)

# Show marks side by side
plt.subplot(121)
plt.title('Watermark')
plt.imshow(mark, cmap='gray')
plt.subplot(122)
plt.title('Extracted')
plt.imshow(extracted,cmap='gray')
plt.show()<jupyter_output><empty_output><jupyter_text>Exercises1. Try to change the layer in which you insert the watermark and see how it impacts the PSNR2. Compute the mean PSNR for 5 images in different layers. Examplesof expected results:| LAYER | PSNR  ||-------|-------|| 7 (LSB)   | 51.14 || 2     | 21.32 || 4     | 33.13 |3. Use the MSB (Most Significant Bit) of an image as watermark. 4. Use a smaller watermark (e.g. 64x64) and try different positions5. Apply different attacks to see how these affect the quality of the watermarkedimage and extracted watermark  Solutions<jupyter_code>########
# EX 1 #
########

im = cv2.imread('Images/lena.bmp', 0)
wat = cv2.imread('Images/mark.png', 0)

res_flat = im.flatten()
wat_flat = wat.flatten()

N = res_flat.shape[0]

# Embedding
LAYER = 3 # from 0 to 7
for i in range(N):
  im_pixel_to_binary = format(res_flat[i], "08b")
  wat_pixel_to_binary = format(wat_flat[i], "08b")
  
  res_pixel = im_pixel_to_binary[:LAYER] + wat_pixel_to_binary[0] + im_pixel_to_binary[LAYER+1:]


  res_flat[i] = int(res_pixel, 2)

res = res_flat.reshape(512, 512)

# Show images side by side
plt.figure(figsize=(15, 6))
plt.subplot(131)
plt.title('Original')
plt.imshow(im, cmap='gray')
plt.subplot(132)
plt.title('Watermark')
plt.imshow(wat,cmap='gray')
plt.subplot(133)
plt.title('Watermarked')
plt.imshow(res,cmap='gray')
plt.show()
########
# EX 2 #
########
import cv2

wat = cv2.imread('Images/mark.png', 0)
wat_flat = wat.flatten()

imagesList = ["lena.bmp", "baboon.bmp", "cameraman.tif", "woman_darkhair.tif", "bridge.tif"]
imagesList = ['Images/'+i for i in imagesList]
layers = [7, 2, 4]

for LAYER in layers:
  avg_psnr = 0
  for imName in imagesList:

    im = cv2.imread(imName, 0)
    res_flat = im.flatten()

    N = res_flat.shape[0]

    # Embedding
    for i in range(N):
      im_pixel_to_binary = format(res_flat[i], "08b")
      wat_pixel_to_binary = format(wat_flat[i], "08b")
      
      res_pixel = im_pixel_to_binary[:LAYER] + wat_pixel_to_binary[0] + im_pixel_to_binary[LAYER+1:]
      res_flat[i] = int(res_pixel, 2)

    res = res_flat.reshape(512, 512)

    # Compute PSNR
    psnr = cv2.PSNR(im, res)
    #print('PSNR: %.2fdB' % psnr)

    avg_psnr += psnr

  print('For Layer %d the average PSNR is %.2fdB' % (LAYER, avg_psnr/len(imagesList)))

########
# EX 3 #
########

im = cv2.imread('Images/lena.bmp', 0)
wat = cv2.imread('Images/baboon.bmp', 0)

res_flat = im.flatten()
wat_flat = wat.flatten()

N = res_flat.shape[0]

# Embedding
for i in range(N):
  im_pixel_to_binary = format(res_flat[i], "08b")
  wat_pixel_to_binary = format(wat_flat[i], "08b")
  
  res_pixel = im_pixel_to_binary[:-1] + wat_pixel_to_binary[0]

  res_flat[i] = int(res_pixel, 2)

res = res_flat.reshape(512, 512)

# Show images side by side
plt.figure(figsize=(15, 6))
plt.subplot(131)
plt.title('Original')
plt.imshow(im, cmap='gray')
plt.subplot(132)
plt.title('Watermark')
plt.imshow(wat,cmap='gray')
plt.subplot(133)
plt.title('Watermarked')
plt.imshow(res,cmap='gray')
plt.show()
########
# EX 4 #
########

im = cv2.imread('Images/lena.bmp', 0)
wat = cv2.imread('Images/mark.png', 0)
wat = cv2.resize(wat, (64, 64)) 
print(wat.shape)

res_flat = im.flatten()
wat_flat = wat.flatten()

N = wat_flat.shape[0]
start = 300

# Embedding
LAYER = 7 # from 0 to 7
for i in range(N):
  im_pixel_to_binary = format(res_flat[start+i], "08b")
  wat_pixel_to_binary = format(wat_flat[i], "08b")
  
  res_pixel = im_pixel_to_binary[:LAYER] + wat_pixel_to_binary[0] + im_pixel_to_binary[LAYER+1:]


  res_flat[start+i] = int(res_pixel, 2)

res = res_flat.reshape(512, 512)

# Compute PSNR
psnr = cv2.PSNR(im, res)
print('PSNR: %.2fdB' % psnr)

# Show images side by side
plt.figure(figsize=(15, 6))
plt.subplot(131)
plt.title('Original')
plt.imshow(im, cmap='gray')
plt.subplot(132)
plt.title('Watermark')
plt.imshow(wat,cmap='gray')
plt.subplot(133)
plt.title('Watermarked')
plt.imshow(res,cmap='gray')
plt.show()<jupyter_output>(64, 64)
PSNR: 69.16dB
{'fraction_non_alphanumeric': 0.15884818169528186, 'fraction_numerical': 0.061241043666351226, 'mean_word_length': 5.0989282769991755, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 18, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 5, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '171575', 'license': 'MIT', 'n_tokens_mistral': 3398, 'n_tokens_neox': 3037, 'n_words': 604, 'path': '01-lab/lab1-20210922/03-LSB-solutions.ipynb', 'repo_name': 'mfranzil-unitn/unitn-m-mds', 'size': '7405'}