Oxaric’s Blog

A compendium of amazing things…

Posts Tagged ‘combine’

Hidden Image Watermarking

Posted by oxaric on December 9, 2008




I’ve written some code in Matlab to take an image of a forest, shown in figure 1, and an image of the X-Men character Wolverine, shown in figure 3, and hide the image of Wolverine inside of the image of the tree, figure 2. The code then retrieves the hidden image of Wolverine, shown in figure 4. I chose these two images at random from Google Image Search.


The main impetus for writing this code is for the purposes of watermarking. Watermarking is useful if you have an image or video you want to distribute professionally or personally and you want to be able to determine if someone else is using the same image you distributed. Hidden watermarking is nice when you want to be able to track your images while not visibly changing the original image. This code can also be used for Steganography purposes.


The code can easily be reused for any images and is heavily commented to be clear and easy to understand. It also gives the option to combine two images in any ratio so it can also be used for neat effects like blending two images, creating visible watermarks, or neat fade effects. The code also demonstrates how the hidden watermark can still be partially retrieved after resizing the combined image, figure 5, heavily compressing the combined image as a JPEG, figure 6, and after adding random noise to the combined image, figure 7. These tests show how hard it would be for someone to completely eradicate hidden watermarks from an image as simple editing of an image will not remove the watermark.






Figure 1 – Original forest image
Forest


Figure 2 – Forest image with hidden Wolverine watermark
Forest and Wolverine Combined


Figure 3 – Original Wolverine Image
Wolverine


Figure 4 – Wolverine watermark retrieved from the watermarked forest image
Restored Wolverine


Figure 5 – Wolverine watermark retrieved after the watermarked forest image was resized
Retrieved Wolverine After Resizing Combined


Figure 6 – Wolverine watermark retrieved after the watermarked forest image was saved as a heavily compressed .jpg
Retrieved Wolverine After Heavily Compressing Combined


Figure 7 – Wolverine watermark retrieved after the watermarked forest image had random noise added
Retreived Wolverine After Adding Random Noise to Combined






Currently the code is hard coded to load ‘tree_path.jpg’ and ‘wolverine.jpg’ so I have zipped those two images with the code. And possibly of note is that I am using Matlab 7.6.0.

Click to directly download stegocombine.zip




% file name 'stegocombine.m'

% Run through Matlab


% By: Louis Casillas, oxaric@gmail.com


% Input:

% tree_path.jpg (RGB)

% wolverine.jpg (RGB)

% Output:

% displays these two images combined and the restored wolverine 

% image pulled from the combined image

% How:

% resizes wolverine.jpg to the size of tree_path.jpg,

% adds the resized wolverine image to tree_path.jpg by weighted values

% specified by WEIGHTED_COMBINE_VALUE:

% combined_image = tree_path + (wolverine_resized * WEIGHTED_COMBINE_VALUE)

% then tries to restore the wolverine image by subtracting the original

% tree_path.jpg values from the combined_image values and dividing by

% WEIGHTED_COMBINE_VALUE:

% restored_wolverine_image = (tree_path - combined_image) / WEIGHTED_COMBINE_VALUE

% the percent used to multiply the wolverine.jpg color values by before

% adding them to tree_path.jpg

% 1.0 = 100%

WEIGHTED_COMBINE_VALUE = 0.01;

% false => only display combined image and restored image

% true => display all images

DISPLAY_INTERMEDIATE_IMAGES = false;

% set DISPLAY_TESTING_IMAGES to a number below to run those tests

% extra tests will show how the wolverine image is restored from the 

% combined image for these 3 cases:

% 0 => no extra tests

% 1 => if the combined image is resized

% 2 => if the combined image is stored as a heavily compressed .jpg

% 3 => if the combined image has added noise

% 4 => show all tests

DISPLAY_TESTING_IMAGES = 4;

% load the images

original_tree_path_image = imread( 'tree_path.jpg' );

original_wolverine_image = imread( 'wolverine.jpg' );

% grab the x and y resolution for tree_path.jpg

tree_path_x_size = size( original_tree_path_image, 2 );

tree_path_y_size = size( original_tree_path_image, 1 );

% grab the x and y resolution for wolverine.jpg

original_wolverine_x_size = size( original_wolverine_image, 2 );

original_wolverine_y_size = size( original_wolverine_image, 1 );

if DISPLAY_INTERMEDIATE_IMAGES


    % display the original tree_path.jpg

    figure, imshow( original_tree_path_image ); title( 'Original - tree_path.jpg' );

    % display the original wolverine.jpg

    figure, imshow( original_wolverine_image ); title( 'Original - wolverine.jpg' );

end

% resize wolverine.jpg to be the same same as tree_path.jpg

resized_wolverine_image = imresize( original_wolverine_image, [tree_path_y_size, tree_path_x_size] );

if DISPLAY_INTERMEDIATE_IMAGES

    

    % display the resized wolverine.jpg

    figure, imshow( resized_wolverine_image ); title( 'Resized Wolverine' );

end

% create a blank image the same size as tree_path.jpg

% this will store the new image that is a combination of tree_path.jpg and

% wolverine.jpg

combined_image = zeros( tree_path_y_size, tree_path_x_size, 3 );

% add tree_path.jpg and wolverine.jpg but only give the pixels in

% the resized wolverine.jpg a weighted value determined by

% WEIGHTED_COMBINE_VALUE

for x_value = 1:tree_path_x_size

    for y_value = 1:tree_path_y_size

        combined_image( y_value, x_value, 1 ) = original_tree_path_image( y_value, x_value, 1 ) + (resized_wolverine_image( y_value, x_value, 1 ) * WEIGHTED_COMBINE_VALUE);

        combined_image( y_value, x_value, 2 ) = original_tree_path_image( y_value, x_value, 2 ) + (resized_wolverine_image( y_value, x_value, 2 ) * WEIGHTED_COMBINE_VALUE);

        combined_image( y_value, x_value, 3 ) = original_tree_path_image( y_value, x_value, 3 ) + (resized_wolverine_image( y_value, x_value, 3 ) * WEIGHTED_COMBINE_VALUE);

    end

end

% force the combined image color values to be between 0 and 255

combined_image = uint8( combined_image );

% display the combined tree_path.jpg and wolverine.jpg

figure, imshow( combined_image ); title( 'Weight Combined Image' );

% create a blank image the same size as tree_path.jpg

wolverine_restored_big_image = zeros( tree_path_y_size, tree_path_x_size, 3 );

% take the values in the combined image and subtract them from the original

% tree_path.jpg values, this will give the values that were added to

% tree_path.jpg above, then divide these values by WEIGHTED_COMBINE_VALUE in order to try 

% to get the original values in wolverine.jpg

for x_value = 1:tree_path_x_size

    for y_value = 1:tree_path_y_size

        wolverine_restored_big_image( y_value, x_value, 1 ) = ( combined_image( y_value, x_value, 1 ) - original_tree_path_image( y_value, x_value, 1 ) ) / WEIGHTED_COMBINE_VALUE;

        wolverine_restored_big_image( y_value, x_value, 2 ) = ( combined_image( y_value, x_value, 2 ) - original_tree_path_image( y_value, x_value, 2 ) ) / WEIGHTED_COMBINE_VALUE;

        wolverine_restored_big_image( y_value, x_value, 3 ) = ( combined_image( y_value, x_value, 3 ) - original_tree_path_image( y_value, x_value, 3 ) ) / WEIGHTED_COMBINE_VALUE;

    end

end

% force wolverine restored image color values to be between 0 and 255

wolverine_restored_big_image = uint8( wolverine_restored_big_image );

if DISPLAY_INTERMEDIATE_IMAGES

    

    % display the restored wolverine.jpg that is still the size of tree_path.jpg

    figure, imshow( wolverine_restored_big_image ); title( 'Restored Wolverine - Tree Path Size' );

end

% resize wolverine_restored_big_image to the size of the original wolverine.jpg

wolverine_restored_image = imresize( wolverine_restored_big_image, [ original_wolverine_y_size , original_wolverine_x_size ] );

% display the restored wolverine image that is the original wolverine.jpg size

figure, imshow( wolverine_restored_image ); title( 'Restored Wolverine - Original Size' );

% compute the Peak Signal-to-Noise Ratio (PSNR) between the original

% wolverine.jpg and the wolverine image restored from the combined image

% The bigger the PSNR the closer the two images are to being exactly the

% same and if the PSNR is infinity the images are exactly the same

% If not infinity values of 30 to 50 are very good and values above 50 are

% excellent/amazing

% Displayed in the Matlab command window

mean_square_error = sum( sum( sum( ( original_wolverine_image - wolverine_restored_image ).^2 ) ) ) / double( original_wolverine_x_size * original_wolverine_y_size * 3 );

disp(' ')

disp('The PSNR between the original wolverine.jpg and the restored wolverine taken from the combined image:')

PSNR = 10 * log10( ( 255 )^2 / mean_square_error )

% DISPLAY_TESTING_IMAGES =

% 0 => no extra tests

% 1 => if the combined image is resized

% 2 => if the combined image is stored as a heavily compressed .jpg

% 3 => if the combined image has added noise

% 4 => show all tests

if (DISPLAY_TESTING_IMAGES ~= 0) && (DISPLAY_TESTING_IMAGES < 5)

    % try to restore the wolverine image from the combined image when the 

    % combined image is resized

    if (DISPLAY_TESTING_IMAGES == 1) || (DISPLAY_TESTING_IMAGES == 4)

    

        % the percent to resize the combined image

        PERCENT_TO_RESIZE = 0.4;

        % resize the combined image

        resized_combined_image = imresize( combined_image, [ tree_path_y_size * PERCENT_TO_RESIZE, tree_path_x_size * PERCENT_TO_RESIZE ] );

        

        if DISPLAY_INTERMEDIATE_IMAGES

            

            % display the resized combined image

            figure, imshow( resized_combined_image ); title( 'Combine Image - Resized' );

        end

        

        % restore the resized combined image to its original size

        restored_combined_image = imresize( resized_combined_image, [tree_path_y_size, tree_path_x_size ] );

        if DISPLAY_INTERMEDIATE_IMAGES


            % display the combined image that has been returned to its

            % original size

            figure, imshow( restored_combined_image ); title( 'Combined Image - Resized to Original Size' );

        end

        

        % try to get the original values in wolverine.jpg

        for x_value = 1:tree_path_x_size

            for y_value = 1:tree_path_y_size

                wolverine_restored_big_image( y_value, x_value, 1 ) = ( restored_combined_image( y_value, x_value, 1 ) - original_tree_path_image( y_value, x_value, 1 ) ) / WEIGHTED_COMBINE_VALUE;

                wolverine_restored_big_image( y_value, x_value, 2 ) = ( restored_combined_image( y_value, x_value, 2 ) - original_tree_path_image( y_value, x_value, 2 ) ) / WEIGHTED_COMBINE_VALUE;

                wolverine_restored_big_image( y_value, x_value, 3 ) = ( restored_combined_image( y_value, x_value, 3 ) - original_tree_path_image( y_value, x_value, 3 ) ) / WEIGHTED_COMBINE_VALUE;

            end

        end

        % force wolverine restored image color values to be between 0 and 255

        wolverine_restored_big_image = uint8( wolverine_restored_big_image );

        if DISPLAY_INTERMEDIATE_IMAGES


            % display the restored wolverine.jpg that is still the size of tree_path.jpg

            figure, imshow( wolverine_restored_big_image ); title( 'Restored Wolverine - Tree Path Size - Combined Image Resized' );

        end

        % resize wolverine_restored_big_image to the size of the original wolverine.jpg

        wolverine_restored_image = imresize( wolverine_restored_big_image, [ original_wolverine_y_size , original_wolverine_x_size ] );

        % display the restored wolverine image that is the original wolverine.jpg size

        figure, imshow( wolverine_restored_image ); title( 'Restored Wolverine - Original Size - Combined Image Resized' );

                

        % compute the Peak Signal-to-Noise Ratio (PSNR)

        % Displayed in the Matlab command window

        mean_square_error = sum( sum( sum( ( original_wolverine_image - wolverine_restored_image ).^2 ) ) ) / double( original_wolverine_x_size * original_wolverine_y_size * 3 );

        disp(' ')

        disp('The PSNR after resizing the combined image:')

        PSNR = 10 * log10( ( 255 )^2 / mean_square_error )

    end

    

    % try to restore the wolverine image from the combined image when the

    % combined image is made a heavily compressed .jpg

    if (DISPLAY_TESTING_IMAGES == 2) || (DISPLAY_TESTING_IMAGES == 4)

    

        % accepts values 0 to 100

        % even if you specify 100 Matlab will compress the image to a minor

        % degree, if you want absolutely no loss set PERCENT_OF_COMPRESSION

        % to 100 and add this line to the inside of the imwrite function:

        % , 'Mode', 'lossless'

        PERCENT_OF_COMPRESSION = 50;

        

        % uses Matlab to save the combined image as the compressed .jpg

        % image 'compressed_combined.jpg'

        imwrite( combined_image, 'combined_compressed.jpg', 'Quality', PERCENT_OF_COMPRESSION );

        

        % reads in the compressed combined .jpg image

        restored_combined_image = uint8( imread( 'combined_compressed.jpg' ) );

        if DISPLAY_INTERMEDIATE_IMAGES


            % display the compressed combined image .jpg image

            figure, imshow( restored_combined_image ); title( 'Combined Image - After Saving As a Compressed .jpg' );

        end

        % try to get the original values in wolverine.jpg

        for x_value = 1:tree_path_x_size

            for y_value = 1:tree_path_y_size

                wolverine_restored_big_image( y_value, x_value, 1 ) = ( restored_combined_image( y_value, x_value, 1 ) - original_tree_path_image( y_value, x_value, 1 ) ) / WEIGHTED_COMBINE_VALUE;

                wolverine_restored_big_image( y_value, x_value, 2 ) = ( restored_combined_image( y_value, x_value, 2 ) - original_tree_path_image( y_value, x_value, 2 ) ) / WEIGHTED_COMBINE_VALUE;

                wolverine_restored_big_image( y_value, x_value, 3 ) = ( restored_combined_image( y_value, x_value, 3 ) - original_tree_path_image( y_value, x_value, 3 ) ) / WEIGHTED_COMBINE_VALUE;

            end

        end

        % force wolverine restored image color values to be between 0 and 255

        wolverine_restored_big_image = uint8( wolverine_restored_big_image );

        if DISPLAY_INTERMEDIATE_IMAGES


            % display the restored wolverine.jpg that is still the size of tree_path.jpg

            figure, imshow( wolverine_restored_big_image ); title( 'Restored Wolverine - Tree Path Size - Combined Image Saved As a Compressed .jpg' );

        end

        % resize wolverine_restored_big_image to the size of the original wolverine.jpg

        wolverine_restored_image = imresize( wolverine_restored_big_image, [ original_wolverine_y_size , original_wolverine_x_size ] );

        % display the restored wolverine image that is the original wolverine.jpg size

        figure, imshow( wolverine_restored_image ); title( 'Restored Wolverine - Original Size - Combined Image Saved As a Compressed .jpg' );

        

        % compute the Peak Signal-to-Noise Ratio (PSNR)

        % Displayed in the Matlab command window

        mean_square_error = sum( sum( sum( ( original_wolverine_image - wolverine_restored_image ).^2 ) ) ) / double( original_wolverine_x_size * original_wolverine_y_size * 3 );

        disp(' ')

        disp('The PSNR after compressing the combined image to a .jpg:')

        PSNR = 10 * log10( ( 255 )^2 / mean_square_error )

    end


    % try to restore the wolverine image from the combined image when the

    % combined image has random added noise

    if (DISPLAY_TESTING_IMAGES == 3) || (DISPLAY_TESTING_IMAGES == 4)

    

        % specifies the maximum noise value

        MAX_NOISE_VALUE = 20;

        % specifies the approximate amount of noise

        % amount of noise = (1 / AMOUNT_OF_NOISE)%

        % 0 means every pixel of the combined image will have added noise

        % 1 means ~50% will have added noise, 2 means ~33%, 3 means ~25%, ....

        AMOUNT_OF_NOISE = 3;

        

        noise_image = uint8( zeros( tree_path_y_size, tree_path_x_size, 3 ) );

        

        % create a random noise image

        for x_value = 1:tree_path_x_size

            for y_value = 1:tree_path_y_size

                if round( rand() * AMOUNT_OF_NOISE ) == 0

                    noise_image( y_value, x_value, 1 ) = round( rand() * (MAX_NOISE_VALUE + 1) );

                    noise_image( y_value, x_value, 2 ) = round( rand() * (MAX_NOISE_VALUE + 1) );

                    noise_image( y_value, x_value, 3 ) = round( rand() * (MAX_NOISE_VALUE + 1) );

                end

                

            end

        end

                       

        % add the noise image to the combined image

        noise_added_combined_image = combined_image + noise_image;

        

        if DISPLAY_INTERMEDIATE_IMAGES

            noise_display_image = ones( tree_path_y_size, tree_path_x_size, 3 ) * 255;


            % create a random noise image

            for x_value = 1:tree_path_x_size

                for y_value = 1:tree_path_y_size

                    if noise_image( y_value, x_value, 1 ) ~= 0

                        noise_display_image( y_value, x_value, 1 ) = noise_image( y_value, x_value, 1 );

                    end

                    

                    if noise_image( y_value, x_value, 2 ) ~= 0

                        noise_display_image( y_value, x_value, 2 ) = noise_image( y_value, x_value, 2 );

                    end

                    

                    if noise_image( y_value, x_value, 3 ) ~= 0

                        noise_display_image( y_value, x_value, 3 ) = noise_image( y_value, x_value, 3 );

                    end

                end

            end

            

            % display the noise image

            figure, imshow( uint8(noise_display_image) ); title( 'Noise to Add to Combined Image' );

            

            % display the combined image with added noise

            figure, imshow( restored_combined_image ); title( 'Combined Image - After Adding Noise' );

        end

        % try to get the original values in wolverine.jpg

        for x_value = 1:tree_path_x_size

            for y_value = 1:tree_path_y_size

                                

                    wolverine_restored_big_image( y_value, x_value, 1 ) = ( noise_added_combined_image( y_value, x_value, 1 ) - original_tree_path_image( y_value, x_value, 1 ) ) / WEIGHTED_COMBINE_VALUE;

                    wolverine_restored_big_image( y_value, x_value, 2 ) = ( noise_added_combined_image( y_value, x_value, 2 ) - original_tree_path_image( y_value, x_value, 2 ) ) / WEIGHTED_COMBINE_VALUE;

                    wolverine_restored_big_image( y_value, x_value, 3 ) = ( noise_added_combined_image( y_value, x_value, 3 ) - original_tree_path_image( y_value, x_value, 3 ) ) / WEIGHTED_COMBINE_VALUE;                

            end

        end

        % force wolverine restored image color values to be between 0 and 255

        wolverine_restored_big_image = uint8( wolverine_restored_big_image );

        if DISPLAY_INTERMEDIATE_IMAGES


            % display the restored wolverine.jpg that is still the size of tree_path.jpg

            figure, imshow( wolverine_restored_big_image ); title( 'Restored Wolverine - Tree Path Size - Combined Image With Added Noise' );

        end

        % resize wolverine_restored_big_image to the size of the original wolverine.jpg

        wolverine_restored_image = imresize( wolverine_restored_big_image, [ original_wolverine_y_size , original_wolverine_x_size ] );

        % display the restored wolverine image that is the original wolverine.jpg size

        figure, imshow( wolverine_restored_image ); title( 'Restored Wolverine - Original Size - Combined Image With Added Noise' );

        

        % compute the Peak Signal-to-Noise Ratio (PSNR)

        % Displayed in the Matlab command window

        mean_square_error = sum( sum( sum( ( original_wolverine_image - wolverine_restored_image ).^2 ) ) ) / double( original_wolverine_x_size * original_wolverine_y_size * 3 );

        disp(' ')

        disp('The PSNR after adding random noise to the combined image:')

        PSNR = 10 * log10( ( 255 )^2 / mean_square_error )

    end

end

Posted in Imaging, Programming, Steganography | Tagged: , , , , , , , , , , , | 1 Comment »