152 lines
6.6 KiB
Matlab
152 lines
6.6 KiB
Matlab
function stego = S_UNIWARD1(coverPath, payload)
|
|
% -------------------------------------------------------------------------
|
|
% Copyright (c) 2013 DDE Lab, Binghamton University, NY.
|
|
% All Rights Reserved.
|
|
% -------------------------------------------------------------------------
|
|
% Permission to use, copy, modify, and distribute this software for
|
|
% educational, research and non-profit purposes, without fee, and without a
|
|
% written agreement is hereby granted, provided that this copyright notice
|
|
% appears in all copies. The program is supplied "as is," without any
|
|
% accompanying services from DDE Lab. DDE Lab does not warrant the
|
|
% operation of the program will be uninterrupted or error-free. The
|
|
% end-user understands that the program was developed for research purposes
|
|
% and is advised not to rely exclusively on the program for any reason. In
|
|
% no event shall Binghamton University or DDE Lab be liable to any party
|
|
% for direct, indirect, special, incidental, or consequential damages,
|
|
% including lost profits, arising out of the use of this software. DDE Lab
|
|
% disclaims any warranties, and has no obligations to provide maintenance,
|
|
% support, updates, enhancements or modifications.
|
|
% -------------------------------------------------------------------------
|
|
% Contact: vojtech_holub@yahoo.com | fridrich@binghamton.edu | October 2012
|
|
% http://dde.binghamton.edu/download/steganography
|
|
% -------------------------------------------------------------------------
|
|
% This function simulates embedding using S-UNIWARD steganographic
|
|
% algorithm. For more deatils about the individual submodels, please see
|
|
% the publication [1].
|
|
% -------------------------------------------------------------------------
|
|
% Input: coverPath ... path to the image
|
|
% payload ..... payload in bits per pixel
|
|
% Output: stego ....... resulting image with embedded payload
|
|
% -------------------------------------------------------------------------
|
|
% PAPER
|
|
% -------------------------------------------------------------------------
|
|
|
|
sgm = 1;
|
|
|
|
%% Get 2D wavelet filters - Daubechies 8
|
|
% 1D high pass decomposition filter
|
|
hpdf = [-0.0544158422, 0.3128715909, -0.6756307363, 0.5853546837, 0.0158291053, -0.2840155430, -0.0004724846, 0.1287474266, 0.0173693010, -0.0440882539, ...
|
|
-0.0139810279, 0.0087460940, 0.0048703530, -0.0003917404, -0.0006754494, -0.0001174768];
|
|
% 1D low pass decomposition filter
|
|
lpdf = (-1).^(0:numel(hpdf)-1).*fliplr(hpdf);
|
|
% construction of 2D wavelet filters
|
|
F{1} = lpdf'*hpdf;
|
|
F{2} = hpdf'*lpdf;
|
|
F{3} = hpdf'*hpdf;
|
|
|
|
%% Get embedding costs
|
|
% inicialization
|
|
cover = double(imread(coverPath));
|
|
wetCost = 10^8;
|
|
[k,l] = size(cover);
|
|
|
|
% add padding
|
|
padSize = max([size(F{1})'; size(F{2})'; size(F{3})']);
|
|
coverPadded = padarray(cover, [padSize padSize], 'symmetric');
|
|
|
|
xi = cell(3, 1);
|
|
for fIndex = 1:3
|
|
% compute residual
|
|
R = conv2(coverPadded, F{fIndex}, 'same');
|
|
% compute suitability
|
|
xi{fIndex} = conv2(1./(abs(R)+sgm), rot90(abs(F{fIndex}), 2), 'same');
|
|
% correct the suitability shift if filter size is even
|
|
if mod(size(F{fIndex}, 1), 2) == 0, xi{fIndex} = circshift(xi{fIndex}, [1, 0]); end;
|
|
if mod(size(F{fIndex}, 2), 2) == 0, xi{fIndex} = circshift(xi{fIndex}, [0, 1]); end;
|
|
% remove padding
|
|
xi{fIndex} = xi{fIndex}(((size(xi{fIndex}, 1)-k)/2)+1:end-((size(xi{fIndex}, 1)-k)/2), ((size(xi{fIndex}, 2)-l)/2)+1:end-((size(xi{fIndex}, 2)-l)/2));
|
|
end
|
|
|
|
% compute embedding costs \rho
|
|
rho = xi{1} + xi{2} + xi{3};
|
|
|
|
% adjust embedding costs
|
|
rho(rho > wetCost) = wetCost; % threshold on the costs
|
|
rho(isnan(rho)) = wetCost; % if all xi{} are zero threshold the cost
|
|
rhoP1 = rho;
|
|
rhoM1 = rho;
|
|
rhoP1(cover==255) = wetCost; % do not embed +1 if the pixel has max value
|
|
rhoM1(cover==0) = wetCost; % do not embed -1 if the pixel has min value
|
|
|
|
%% Embedding simulator
|
|
stego = EmbeddingSimulator(cover, rhoP1, rhoM1, payload*numel(cover), false);
|
|
|
|
|
|
%% --------------------------------------------------------------------------------------------------------------------------
|
|
% Embedding simulator simulates the embedding made by the best possible ternary coding method (it embeds on the entropy bound).
|
|
% This can be achieved in practice using "Multi-layered syndrome-trellis codes" (ML STC) that are asymptotically aproaching the bound.
|
|
function [y] = EmbeddingSimulator(x, rhoP1, rhoM1, m, fixEmbeddingChanges)
|
|
|
|
n = numel(x);
|
|
lambda = calc_lambda(rhoP1, rhoM1, m, n);
|
|
pChangeP1 = (exp(-lambda .* rhoP1))./(1 + exp(-lambda .* rhoP1) + exp(-lambda .* rhoM1));
|
|
pChangeM1 = (exp(-lambda .* rhoM1))./(1 + exp(-lambda .* rhoP1) + exp(-lambda .* rhoM1));
|
|
if fixEmbeddingChanges == 1
|
|
RandStream.setGlobalStream(RandStream('mt19937ar','seed',139187));
|
|
else
|
|
RandStream.setGlobalStream(RandStream('mt19937ar','Seed',sum(100*clock)));
|
|
end
|
|
randChange = rand(size(x));
|
|
y = x;
|
|
y(randChange < pChangeP1) = y(randChange < pChangeP1) + 1;
|
|
y(randChange >= pChangeP1 & randChange < pChangeP1+pChangeM1) = y(randChange >= pChangeP1 & randChange < pChangeP1+pChangeM1) - 1;
|
|
|
|
function lambda = calc_lambda(rhoP1, rhoM1, message_length, n)
|
|
|
|
l3 = 1e+3;
|
|
m3 = double(message_length + 1);
|
|
iterations = 0;
|
|
while m3 > message_length
|
|
l3 = l3 * 2;
|
|
pP1 = (exp(-l3 .* rhoP1))./(1 + exp(-l3 .* rhoP1) + exp(-l3 .* rhoM1));
|
|
pM1 = (exp(-l3 .* rhoM1))./(1 + exp(-l3 .* rhoP1) + exp(-l3 .* rhoM1));
|
|
m3 = ternary_entropyf(pP1, pM1);
|
|
iterations = iterations + 1;
|
|
if (iterations > 10)
|
|
lambda = l3;
|
|
return;
|
|
end
|
|
end
|
|
|
|
l1 = 0;
|
|
m1 = double(n);
|
|
lambda = 0;
|
|
|
|
alpha = double(message_length)/n;
|
|
% limit search to 30 iterations
|
|
% and require that relative payload embedded is roughly within 1/1000 of the required relative payload
|
|
while (double(m1-m3)/n > alpha/1000.0 ) && (iterations<30)
|
|
lambda = l1+(l3-l1)/2;
|
|
pP1 = (exp(-lambda .* rhoP1))./(1 + exp(-lambda .* rhoP1) + exp(-lambda .* rhoM1));
|
|
pM1 = (exp(-lambda .* rhoM1))./(1 + exp(-lambda .* rhoP1) + exp(-lambda .* rhoM1));
|
|
m2 = ternary_entropyf(pP1, pM1);
|
|
if m2 < message_length
|
|
l3 = lambda;
|
|
m3 = m2;
|
|
else
|
|
l1 = lambda;
|
|
m1 = m2;
|
|
end
|
|
iterations = iterations + 1;
|
|
end
|
|
end
|
|
|
|
function Ht = ternary_entropyf(pP1, pM1)
|
|
p0 = 1-pP1-pM1;
|
|
P = [p0(:); pP1(:); pM1(:)];
|
|
H = -((P).*log2(P));
|
|
H((P<eps) | (P > 1-eps)) = 0;
|
|
Ht = sum(H);
|
|
end
|
|
end
|
|
end |