No BSD License  

Highlights from
Geometric Warping-Based Watermarking

from Geometric Warping-Based Watermarking by Dima Pröfrock
This functions can be used to embed and extract a watermark bit using the geometric structure.

EmbedWatermark(block,bitvalue,quant)
%   EmbedWatermark embeds a watermark bit into a 16x16 uint8 block of an
%   image
%	EmbedWatermark(block,bitvalue,quant) embeds into a block a bit with value
%	'0' or '1' (bitvalue) and an embedding strength quant
%	
%	block    --> 16x16 uint8 block of an image
%   bitvalue --> bitvalue (int) 0 or 1
%   quant    --> embedding strength, only even values > 2, commend is 16,
%                a higher value quant yields lower quality degradations, a
%                lower value quant yields a more robust watermark bit
%
%   watermarkedblock --> watermarked block
%   wx               --> resulting warping  strength in x direction
%   wy               --> resulting warping  strength in y direction
%
%   [watermarkedblock, wx, wy] = EmbedWatermark(block,1,16)

function [newblock, wx, wy] =  EmbedWatermark(block,bitvalue,quant)

    bsize = 16; %Block size 

    vek = block_vek(block); %Compute the vectors for the normed centre of gravity

%STEP ONE: CRUDE GLOBAL SEARCH OF SUITABLE WARPING DIRECTIONS

    steps = 10; %resoultion 

    zm_max = [];

    % NCG-coordinates of the original block
    x = 8*vek2wink(vek(1,1),vek(1,2))/pi;
    y = 8*vek2wink(vek(2,1),vek(2,2))/pi;                

    %global search for  warping
    maxcnt = 1;
    for cnt3 = 1:2*steps+1
        for cnt4 = 1:2*steps+1
            %warp original blocks in different directions
            zm_vek = block_vek(warp_block(block,bsize,x,y,(cnt3-steps-1)/steps,(cnt4-steps-1)/steps));
            %compute svalue (zm)
            zm_x = 8*vek2wink(zm_vek(1,1),zm_vek(1,2))/pi;
            zm_y = 8*vek2wink(zm_vek(2,1),zm_vek(2,2))/pi;
            zm_lx = (zm_vek(1,1)^2+zm_vek(1,2)^2)^0.5;
            zm_ly = (zm_vek(2,1)^2+zm_vek(2,2)^2)^0.5;
            [zm_l1,zm_l2] = compf(zm_lx,zm_ly,1.8);
            zm = ((dreieck(zm_x,quant)*zm_l1)+(dreieck(zm_y,quant)*zm_l2));
            %adapt zm to the wanted bitvalue
            zm = abs(zm -(1-bitvalue));
            merker(cnt3,cnt4) = zm;
            %remember warping directions with good (zm near 1) results 
             if (zm > 0.9)
                 zm_max(maxcnt) = zm; % remember zm-value
                 zm_wx(maxcnt) = (cnt3-steps-1)/steps; % remember warping in x direction
                 zm_wy(maxcnt) = (cnt4-steps-1)/steps; % remember warping in y direction
                 maxcnt = maxcnt+1;
             end
        end
    end
    %reducing some of the selected warping directions which are close
    %together to reduce the computational complexity for the next steps
    if length(zm_max ) >= 1
        posvek = ones(length(zm_max),1);
        for cnt1 = 1:1:length(zm_max)
            if posvek(cnt1) == 1
                for cnt2 = cnt1+1:1:length(zm_max)
                    if ((zm_wx(cnt1)-zm_wx(cnt2))^2+(zm_wy(cnt1)-zm_wy(cnt2))^2)^0.5 < 0.25
                        if zm_max(cnt2) <= zm_max(cnt1)
                            posvek(cnt2) = 0;
                        else
                            posvek(cnt1) = 0;
                        end
                    end
                end
            end
        end
        zm_max2 = zm_max(find(posvek==1));                
        zm_wy2 = zm_wy(find(posvek==1));
        zm_wx2 = zm_wx(find(posvek==1));
    else
        pos = find(merker(:) == max(merker(:)));
        pos = pos(1);
        [xp, yp] = ind2sub([steps*2+1,steps*2+1],pos);
        zm_max2 = merker(11,11);
        zm_wy2 = (yp-steps-1)/steps;
        zm_wx2 = (xp-steps-1)/steps;
    end
    
%STEP TWO: FINE SEARCH, THE SELECTED CRUDE WARPING DIRECTIONS ARE USED AS STARTING POINTS 

    for cnt1 = 1:1:length(zm_max2)
      increment = 1/(2*steps);
        zm_max = zm_max2(cnt1);                
        zm_wy = zm_wy2(cnt1);
        zm_wx = zm_wx2(cnt1);

         % neue Schrittweite

        i = 0;

        % Verschiebung des Blockes in 8 Richtungen und Test auf
        % Maximum
        [zmax, zmax_wx, zmax_wy] = warp_spider(block,bsize,quant,x,y,zm_max,zm_wx,zm_wy,increment,bitvalue);                
        merker2(i+1,cnt1) = zmax;
        while (zmax ~= 0.5) && (i < 15)

            i = i + 1;

            % Sichern des aktuellen BESTEN Ergebnisses
            zm_max = zmax;
            zm_wx = zmax_wx;
            zm_wy = zmax_wy;

            % Halbieren der zu verwendenden Schrittweite
            increment = increment/2;                    

            % Verschieben um den aktuellen BESTEN Z-Wert
            [zmax, zmax_wx, zmax_wy] = warp_spider(block,bsize,quant,x,y,zm_max,zm_wx,zm_wy,increment,bitvalue);
            merker2(i+1,cnt1) = zmax;
        end
        zm_maxe(cnt1) = zm_max;                
        zm_wye(cnt1) = zmax_wy;
        zm_wxe(cnt1) = zmax_wx;
    end
%STEP THREE: CHOSE THE BEST RESULT OUT OF THE LOCATED MAXIMA
    Dist = (zm_wye.^2+zm_wxe.^2).^0.5;%compute the warping strength
    Qual = 0.99*(zm_maxe/0.5) +  0.01*(1-(Dist/(2^0.5)));%compute the quality of the result
    pos = find(Qual == max(Qual));%find the best quality
    pos = pos(1);
    %compute the watermakred block
    newblock = warp_block(block,bsize,x,y,zm_wxe(pos),zm_wye(pos));
    wx = zm_wxe(pos);
    wy = zm_wye(pos);
    


%function delivers the vector for the Normed Centre of Gravity
function [vek] = block_vek(block)

    blocksize      = size(block);
    winkelvek_x = (pi/blocksize(1):2*pi/blocksize(1):(2*pi)-(pi/blocksize(1)));
    winkelvek_y = (pi/blocksize(1):2*pi/blocksize(1):(2*pi)-(pi/blocksize(1)));

    vert_m = mean(double(block)); 
    vek(1,1) = sum(cos(winkelvek_x).*vert_m); 
    vek(1,2) = sum(sin(winkelvek_x).*vert_m); 

    hor_m = mean(double(block),2); 
    vek(2,1) = sum(cos(winkelvek_y).*hor_m'); 
    vek(2,2) = sum(sin(winkelvek_y).*hor_m'); 

%sub-function the compute the s value
function [erg1, erg2] = compf(l1,l2,ef)

    if l1 < l2
        erg1 = x(l1,l2,ef)*l1/(l1+l2);
        erg2 = 1-erg1;
    else
        erg2 = x(l2,l1,ef)*l2/(l1+l2);
        erg1 = 1-erg2;
    end

%sub-function the compute the s value
function erg = x(l1,l2,ef)

    if ef ~= 0
        erg1 = (exp(ef*l1/1200))/(exp(ef));
        erg = erg1/(1+(1-abs((l1-l2)/1200))*(erg1-1));
    else
        erg = 1;
    end

%sub-function the compute the s value
function erg = dreieck(x,n)

    Breite = 16/n; 
    Breite_h = Breite/2;
    pos = mod(x,Breite);
    if pos <= Breite_h
        erg = pos*n/16; 
    else
        erg = (pos*(-n/8)+2)/2;
    end
    erg = 2*erg; 


%sub-function the compute the NCG
function ergw = vek2wink(x,y)

    if x ~= 0
        ergw = atan(abs(y/x));
    else
        ergw = pi/2;
    end

    if x < 0 & y > 0
        ergw = pi - ergw;
    elseif x < 0 & y < 0
        ergw = ergw + pi;
    elseif x > 0 & y < 0
        ergw = 2*pi - ergw;
    end


%sub-function which warps a block in 8 different directions to find the
%best warping direction
function [zmax, zmax_wx, zmax_wy] = warp_spider(block,bsize,quant,x,y,z,wx,wy,increment,bitvalue)

    zmax = z;
    zmax_wx = wx;
    zmax_wy = wy;

    % warping up
    zm_vek = block_vek(warp_block(block,bsize,x,y,wx+increment,wy));

    zm_x = 8*vek2wink(zm_vek(1,1),zm_vek(1,2))/pi;
    zm_y = 8*vek2wink(zm_vek(2,1),zm_vek(2,2))/pi;
    zm_lx = (zm_vek(1,1)^2+zm_vek(1,2)^2)^0.5;
    zm_ly = (zm_vek(2,1)^2+zm_vek(2,2)^2)^0.5;
    [zm_l1,zm_l2] = compf(zm_lx,zm_ly,1.8);
    zm = ((dreieck(zm_x,quant)*zm_l1)+(dreieck(zm_y,quant)*zm_l2));
    zm = abs(zm -(1-bitvalue));
    
    if (zm > zmax)
        zmaxmax = zm;
        zmax_wx = wx+increment;
        zmax_wy = wy;
    end

    % warping up rigth
    zm_vek = block_vek(warp_block(block,bsize,x,y,wx+increment,wy+increment));

    zm_x = 8*vek2wink(zm_vek(1,1),zm_vek(1,2))/pi;
    zm_y = 8*vek2wink(zm_vek(2,1),zm_vek(2,2))/pi;
    zm_lx = (zm_vek(1,1)^2+zm_vek(1,2)^2)^0.5;
    zm_ly = (zm_vek(2,1)^2+zm_vek(2,2)^2)^0.5;
    [zm_l1,zm_l2] = compf(zm_lx,zm_ly,1.8);
    zm = ((dreieck(zm_x,quant)*zm_l1)+(dreieck(zm_y,quant)*zm_l2));
    zm = abs(zm -(1-bitvalue));

    if (zm > zmax)
        zmax = zm;
        zmax_wx = wx+increment;
        zmax_wy = wy+increment;
    end

    % warping rigth
    zm_vek = block_vek(warp_block(block,bsize,x,y,wx+increment,wy));

    zm_x = 8*vek2wink(zm_vek(1,1),zm_vek(1,2))/pi;
    zm_y = 8*vek2wink(zm_vek(2,1),zm_vek(2,2))/pi;
    zm_lx = (zm_vek(1,1)^2+zm_vek(1,2)^2)^0.5;
    zm_ly = (zm_vek(2,1)^2+zm_vek(2,2)^2)^0.5;
    [zm_l1,zm_l2] = compf(zm_lx,zm_ly,1.8);
    zm = ((dreieck(zm_x,quant)*zm_l1)+(dreieck(zm_y,quant)*zm_l2));
    zm = abs(zm -(1-bitvalue));

    if (zm > zmax)
        zmax = zm;
        zmax_wx = wx+increment;
        zmax_wy = wy;
    end

    % warping down rigth
    zm_vek = block_vek(warp_block(block,bsize,x,y,wx+increment,wy-increment));

    zm_x = 8*vek2wink(zm_vek(1,1),zm_vek(1,2))/pi;
    zm_y = 8*vek2wink(zm_vek(2,1),zm_vek(2,2))/pi;
    zm_lx = (zm_vek(1,1)^2+zm_vek(1,2)^2)^0.5;
    zm_ly = (zm_vek(2,1)^2+zm_vek(2,2)^2)^0.5;
    [zm_l1,zm_l2] = compf(zm_lx,zm_ly,1.8);
    zm = ((dreieck(zm_x,quant)*zm_l1)+(dreieck(zm_y,quant)*zm_l2));
    zm = abs(zm -(1-bitvalue));

    if (zm > zmax)
        zmax = zm;
        zmax_wx = wx+increment;
        zmax_wy = wy-increment;
    end

    % warping down
    zm_vek = block_vek(warp_block(block,bsize,x,y,wx,wy-increment));

    zm_x = 8*vek2wink(zm_vek(1,1),zm_vek(1,2))/pi;
    zm_y = 8*vek2wink(zm_vek(2,1),zm_vek(2,2))/pi;
    zm_lx = (zm_vek(1,1)^2+zm_vek(1,2)^2)^0.5;
    zm_ly = (zm_vek(2,1)^2+zm_vek(2,2)^2)^0.5;
    [zm_l1,zm_l2] = compf(zm_lx,zm_ly,1.8);
    zm = ((dreieck(zm_x,quant)*zm_l1)+(dreieck(zm_y,quant)*zm_l2));
    zm = abs(zm -(1-bitvalue));

    if (zm > zmax)
        zmax = zm;
        zmax_wx = wx;
        zmax_wy = wy-increment;
    end

    % warping down left
    zm_vek = block_vek(warp_block(block,bsize,x,y,wx-increment,wy-increment));

    zm_x = 8*vek2wink(zm_vek(1,1),zm_vek(1,2))/pi;
    zm_y = 8*vek2wink(zm_vek(2,1),zm_vek(2,2))/pi;
    zm_lx = (zm_vek(1,1)^2+zm_vek(1,2)^2)^0.5;
    zm_ly = (zm_vek(2,1)^2+zm_vek(2,2)^2)^0.5;
    [zm_l1,zm_l2] = compf(zm_lx,zm_ly,1.8);
    zm = ((dreieck(zm_x,quant)*zm_l1)+(dreieck(zm_y,quant)*zm_l2));
    zm = abs(zm -(1-bitvalue));

    if (zm > zmax)
        zmax = zm;
        zmax_wx = wx-increment;
        zmax_wy = wy-increment;
    end

    % warping left
    zm_vek = block_vek(warp_block(block,bsize,x,y,wx-increment,wy));

    zm_x = 8*vek2wink(zm_vek(1,1),zm_vek(1,2))/pi;
    zm_y = 8*vek2wink(zm_vek(2,1),zm_vek(2,2))/pi;
    zm_lx = (zm_vek(1,1)^2+zm_vek(1,2)^2)^0.5;
    zm_ly = (zm_vek(2,1)^2+zm_vek(2,2)^2)^0.5;
    [zm_l1,zm_l2] = compf(zm_lx,zm_ly,1.8);
    zm = ((dreieck(zm_x,quant)*zm_l1)+(dreieck(zm_y,quant)*zm_l2));
    zm = abs(zm -(1-bitvalue));

    if (zm > zmax)
        zmax = zm;
        zmax_wx = wx-increment;
        zmax_wy = wy;
    end

    % warping up left
    zm_vek = block_vek(warp_block(block,bsize,x,y,wx-increment,wy+increment));

    zm_x = 8*vek2wink(zm_vek(1,1),zm_vek(1,2))/pi;
    zm_y = 8*vek2wink(zm_vek(2,1),zm_vek(2,2))/pi;
    zm_lx = (zm_vek(1,1)^2+zm_vek(1,2)^2)^0.5;
    zm_ly = (zm_vek(2,1)^2+zm_vek(2,2)^2)^0.5;
    [zm_l1,zm_l2] = compf(zm_lx,zm_ly,1.8);
    zm = ((dreieck(zm_x,quant)*zm_l1)+(dreieck(zm_y,quant)*zm_l2));
    zm = abs(zm -(1-bitvalue));

    if (zm > zmax)
        zmax = zm;
        zmax_wx = wx-increment;
        zmax_wy = wy+increment;
    end

%sub-function to warp a block 
function output = warp_block(input,bsize,x,y,wx,wy)
   
    morphingfield = ones(16,16,2);
    morphingfield(:,:,1) = morphingfield(:,:,1)*-wx;
    morphingfield(:,:,2) = morphingfield(:,:,2)*wy;
    output = uint8(morphimage(uint8(input),morphingfield));

Contact us at files@mathworks.com