Bases: BaseCodec
LSBHideStrInNNModel - steganography algorithm that hides PNG image in neural
network models.
Originally implemented in
gaborvecsei/Neural-Network-Steganography
Source code in stegobox/codec/hide_png_in_nn_model/lsb_hide_png_in_nnmodel.py
| class LSBHidePNGInNNModel(BaseCodec):
"""LSBHideStrInNNModel - steganography algorithm that hides PNG image in neural
network models.
Originally implemented in
[gaborvecsei/Neural-Network-Steganography](https://github.com/gaborvecsei/Neural-Network-Steganography)
"""
def __init__(self, num_bits: int = 8) -> None:
"""To initialize the class and specify the number of bits to use.
Args:
num_bits: The number of bits to hide secret information.
"""
super().__init__()
self.bit_use = num_bits
def encode(
self, carrier: collections.OrderedDict, payload: np.ndarray
) -> tuple[collections.OrderedDict, tuple[int, ...]]:
"""Encodes payload string into an NN model by LSB.
Args:
carrier: The carrier NN model, read with `io.nnmodel.read()`.
payload: The secret image in format PNG. read with `io.image.read_cv2()`.
Returns:
The encoded NN model, write with `io.nnmodel.write()`.
size of secret image, you will need this when decoding.
"""
image_size = payload.shape
secret_bits = payload.reshape(-1)
secret_bits = np.unpackbits(secret_bits)
length = len(secret_bits)
# This dict holds the original weights for the selected layers
original_weights_dict: dict = {}
for te in carrier:
# if re.match(".*conv.*", te):
original_weights_dict[te] = deepcopy(
carrier[te].clone().detach().cpu().numpy()
)
layer_name = list(original_weights_dict.keys())
layer_name_num = {}
pre_layer_name = {}
layer_shape = {}
for name in layer_name:
layer_name_num[name] = np.prod(original_weights_dict[name].shape)
for i in range(len(layer_name)):
name = layer_name[i]
if i == 0:
pre_layer_name[name] = 0
else:
pre_layer_name[name] = (
layer_name_num[layer_name[i - 1]]
+ pre_layer_name[layer_name[i - 1]]
)
carrier_data = []
for name in layer_name:
layer_shape[name] = original_weights_dict[name].shape
carrier_data.extend(original_weights_dict[name].reshape(-1))
for i in range(0, length, self.bit_use):
_from_index = i
_to_index = _from_index + self.bit_use
bits_to_hide = secret_bits[_from_index:_to_index]
bits_to_hide = list(map(bool, bits_to_hide)) # type: ignore
j = math.ceil(i / self.bit_use)
x = FloatBinary(carrier_data[j])
fraction_modified = list(x.fraction)
if len(bits_to_hide) > 0:
fraction_modified[-self.bit_use :] = bits_to_hide
x_modified = x.modify_clone(fraction=fraction_modified)
carrier_data[j] = x_modified.v
outdata = {}
for name in layer_name:
temp = np.array(
carrier_data[
int(pre_layer_name[name]) : int(pre_layer_name[name])
+ int(layer_name_num[name])
]
)
outdata[name] = temp.reshape(layer_shape[name])
for name in layer_name:
carrier.pop(name)
carrier[name] = torch.from_numpy(outdata[name])
# carrier[name] = torch.nn.parameter.Parameter(
# torch.from_numpy(outdata[name]), requires_grad= False
# )
return carrier, image_size
def decode(self, _):
raise NotImplementedError(
"This codec does not support decoding without image size"
)
def decodewithsize(
self, carrier: collections.OrderedDict, image_size: tuple[int, ...]
) -> np.ndarray:
"""Try to decode secret image from an NN model by LSB.
Args:
carrier: the carrier NN model,read with `io.nnmodel.read()`.
image_size: the size of secret image.
Returns:
np.ndarray: The decode secret image.
"""
hidden_data: List[bool] = []
original_weights_dict: dict = {}
for te in carrier:
# if re.match(".*conv.*", te):
original_weights_dict[te] = deepcopy(
carrier[te].clone().detach().cpu().numpy()
)
layer_name = list(original_weights_dict.keys())
carrier_data = []
for name in layer_name:
carrier_data.extend(original_weights_dict[name].reshape(-1))
length = image_size[0] * image_size[1] * image_size[2] * 8
for i in range(0, length, self.bit_use):
j = math.ceil(i / self.bit_use)
x = FloatBinary(carrier_data[j])
hidden_bits = x.fraction[-self.bit_use :]
hidden_data.extend(hidden_bits)
hidden_data = np.array(hidden_data).astype(int) # type: ignore
recovered_image = np.array(np.packbits(hidden_data)).reshape(image_size)
return recovered_image
|
__init__(num_bits=8)
To initialize the class and specify the number of bits to use.
Parameters:
Name |
Type |
Description |
Default |
num_bits |
int
|
The number of bits to hide secret information.
|
8
|
Source code in stegobox/codec/hide_png_in_nn_model/lsb_hide_png_in_nnmodel.py
| def __init__(self, num_bits: int = 8) -> None:
"""To initialize the class and specify the number of bits to use.
Args:
num_bits: The number of bits to hide secret information.
"""
super().__init__()
self.bit_use = num_bits
|
encode(carrier, payload)
Encodes payload string into an NN model by LSB.
Parameters:
Name |
Type |
Description |
Default |
carrier |
OrderedDict
|
The carrier NN model, read with io.nnmodel.read() .
|
required
|
payload |
ndarray
|
The secret image in format PNG. read with io.image.read_cv2() .
|
required
|
Returns:
Type |
Description |
OrderedDict
|
The encoded NN model, write with io.nnmodel.write() .
|
tuple[int, ...]
|
size of secret image, you will need this when decoding.
|
Source code in stegobox/codec/hide_png_in_nn_model/lsb_hide_png_in_nnmodel.py
| def encode(
self, carrier: collections.OrderedDict, payload: np.ndarray
) -> tuple[collections.OrderedDict, tuple[int, ...]]:
"""Encodes payload string into an NN model by LSB.
Args:
carrier: The carrier NN model, read with `io.nnmodel.read()`.
payload: The secret image in format PNG. read with `io.image.read_cv2()`.
Returns:
The encoded NN model, write with `io.nnmodel.write()`.
size of secret image, you will need this when decoding.
"""
image_size = payload.shape
secret_bits = payload.reshape(-1)
secret_bits = np.unpackbits(secret_bits)
length = len(secret_bits)
# This dict holds the original weights for the selected layers
original_weights_dict: dict = {}
for te in carrier:
# if re.match(".*conv.*", te):
original_weights_dict[te] = deepcopy(
carrier[te].clone().detach().cpu().numpy()
)
layer_name = list(original_weights_dict.keys())
layer_name_num = {}
pre_layer_name = {}
layer_shape = {}
for name in layer_name:
layer_name_num[name] = np.prod(original_weights_dict[name].shape)
for i in range(len(layer_name)):
name = layer_name[i]
if i == 0:
pre_layer_name[name] = 0
else:
pre_layer_name[name] = (
layer_name_num[layer_name[i - 1]]
+ pre_layer_name[layer_name[i - 1]]
)
carrier_data = []
for name in layer_name:
layer_shape[name] = original_weights_dict[name].shape
carrier_data.extend(original_weights_dict[name].reshape(-1))
for i in range(0, length, self.bit_use):
_from_index = i
_to_index = _from_index + self.bit_use
bits_to_hide = secret_bits[_from_index:_to_index]
bits_to_hide = list(map(bool, bits_to_hide)) # type: ignore
j = math.ceil(i / self.bit_use)
x = FloatBinary(carrier_data[j])
fraction_modified = list(x.fraction)
if len(bits_to_hide) > 0:
fraction_modified[-self.bit_use :] = bits_to_hide
x_modified = x.modify_clone(fraction=fraction_modified)
carrier_data[j] = x_modified.v
outdata = {}
for name in layer_name:
temp = np.array(
carrier_data[
int(pre_layer_name[name]) : int(pre_layer_name[name])
+ int(layer_name_num[name])
]
)
outdata[name] = temp.reshape(layer_shape[name])
for name in layer_name:
carrier.pop(name)
carrier[name] = torch.from_numpy(outdata[name])
# carrier[name] = torch.nn.parameter.Parameter(
# torch.from_numpy(outdata[name]), requires_grad= False
# )
return carrier, image_size
|
decodewithsize(carrier, image_size)
Try to decode secret image from an NN model by LSB.
Parameters:
Name |
Type |
Description |
Default |
carrier |
OrderedDict
|
the carrier NN model,read with io.nnmodel.read() .
|
required
|
image_size |
tuple[int, ...]
|
the size of secret image.
|
required
|
Returns:
Type |
Description |
ndarray
|
np.ndarray: The decode secret image.
|
Source code in stegobox/codec/hide_png_in_nn_model/lsb_hide_png_in_nnmodel.py
| def decodewithsize(
self, carrier: collections.OrderedDict, image_size: tuple[int, ...]
) -> np.ndarray:
"""Try to decode secret image from an NN model by LSB.
Args:
carrier: the carrier NN model,read with `io.nnmodel.read()`.
image_size: the size of secret image.
Returns:
np.ndarray: The decode secret image.
"""
hidden_data: List[bool] = []
original_weights_dict: dict = {}
for te in carrier:
# if re.match(".*conv.*", te):
original_weights_dict[te] = deepcopy(
carrier[te].clone().detach().cpu().numpy()
)
layer_name = list(original_weights_dict.keys())
carrier_data = []
for name in layer_name:
carrier_data.extend(original_weights_dict[name].reshape(-1))
length = image_size[0] * image_size[1] * image_size[2] * 8
for i in range(0, length, self.bit_use):
j = math.ceil(i / self.bit_use)
x = FloatBinary(carrier_data[j])
hidden_bits = x.fraction[-self.bit_use :]
hidden_data.extend(hidden_bits)
hidden_data = np.array(hidden_data).astype(int) # type: ignore
recovered_image = np.array(np.packbits(hidden_data)).reshape(image_size)
return recovered_image
|