Skip to content

Instantly share code, notes, and snippets.

@Ge0rg3
Last active March 22, 2022 00:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ge0rg3/af58b802b9f8c3147a726a558f8898e3 to your computer and use it in GitHub Desktop.
Save Ge0rg3/af58b802b9f8c3147a726a558f8898e3 to your computer and use it in GitHub Desktop.
Python Pixel Value Differencing test implementation. Can iterate via ROW/COL, and optionally in zigzag.
import numpy as np
import math
from PIL import Image
img = "pvd_test.png"
RANGES = [8, 8, 16, 32, 64, 128]
SECRET_STRING = "HELLO WORLD "*50
# Generate range table
RANGE_TABLE = []
start = 0
for _range in RANGES:
RANGE_TABLE.append((start, start + _range - 1))
start += _range
# Load pixels
image = Image.open(img)
width, height = image.size
pixels_array = list(image.getdata())
def get_bits(difference):
"""
Determine the number of embeddable/extractable bits from a difference
between 2 pixels, based off the range table values.
"""
for min_range, max_range in RANGE_TABLE:
if min_range <= difference and max_range >= difference:
embeddable_bit_count = int(math.log2(max_range+1 - min_range))
return (min_range, embeddable_bit_count)
def string_to_binary(input_string):
return ''.join(format(ord(x), 'b').zfill(8) for x in input_string)
first_in_pair = True
previous_index = 0
secret_position = 0
secret_binary = string_to_binary(SECRET_STRING)
# Iteration process begins
order = "ROW"
zigzag = True
zigzag_start = "LEFT"
start_a = 0
start_b = 0
step_a = 1
step_b = 1
if order == "ROW":
end_a = height
end_b = width
else:
end_a = width
end_b = height
for a in range(start_a, end_a, step_a):
if zigzag:
if zigzag_start == "RIGHT":
zigzag_flip = 0
elif zigzag_start == "LEFT":
zigzag_flip = 1
if a % 2 == zigzag_flip:
start_b = end_b - 1
end_b = -1
step_b = -1
else:
start_b = 0
step_b = 1
if order == "ROW":
end_b = width
else:
end_b = height
for b in range(start_b, end_b, step_b):
if order == "ROW":
index = (a * width) + b
else:
index = (b * width) + a
# Iteration process over!
if first_in_pair:
previous_index = index
first_in_pair = False
else:
# Get difference between two pixel values
pixel_difference = pixels_array[previous_index] - pixels_array[index]
# Find the minimum range and number of embeddable bits from range table
min_range, bit_count = get_bits(abs(pixel_difference))
# Calculate which int to add to min range for given embeddable bit count
bits_to_embed = secret_binary[secret_position:secret_position+bit_count]
int_to_embed = int(bits_to_embed, 2)
# Find the new difference that will be used between the new pixels
new_pixel_difference = min_range + int_to_embed
# Change pixel values to fit the new difference (algorithm sourced directly from Wu-Tsai paper)
if pixel_difference < 0:
m = pixel_difference - (new_pixel_difference * -1)
else:
m = pixel_difference - new_pixel_difference
m /= 2
if pixel_difference % 2 == 0:
pixels_array[previous_index] -= math.floor(m)
pixels_array[index] += math.ceil(m)
else:
pixels_array[previous_index] -= math.ceil(m)
pixels_array[index] += math.floor(m)
secret_position += bit_count
first_in_pair = True
if secret_position >= len(secret_binary):
break
if secret_position >= len(secret_binary):
break
pixels_array = np.array(pixels_array)
mat = np.reshape(pixels_array, (height, width))
img = Image.fromarray(np.uint8(mat), 'L')
img.save("embedded.png")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment