How can I watermark an image?

74 views (last 30 days)
Mithra
Mithra on 15 Mar 2013
Commented: cui,xingxing on 23 Jan 2024
I'm trying to watermark a RGB image into another RGB image by replacing the 1st bit of the host image (hst) with the 8th bit of the massage image (msg). Also there's no error, MATLAB give me no result. I use the codes below:
cman=imread('2.png');
logo=imread('10.png');
graycman=rgb2gray(cman);
graylog=rgb2gray(logo);
amsg=uint8(graylog);
[r,c]=size(amsg);
msg=zeros(r,c);
for i=1:r
for j=1:c
bmsg=dec2bin(amsg(i,j),8);
cmsg=str2double(bmsg);
msg(i,j)=bitget(cmsg,8);
end
end
ahst=uint8(graycman);
[R,C]=size(ahst);
for k=1:R
for L=1:C
bhst=dec2bin(ahst(k,L),8);
chst=str2double(bhst);
end
end
for x=1:r
for y=1:c
hst=bitset(chst,1,msg(x,y));
end
end
imshow(hst)
  2 Comments
Walter Roberson
Walter Roberson on 15 Mar 2013
No result? It did not even show the hst afterwards?
Mithra
Mithra on 16 Mar 2013
Edited: Mithra on 16 Mar 2013
it actually does open a window which is supposed to show something, but in fact nothing is shown but a 1*1 pixel with value 1 , I mean just a white spot!!! I need to know what's wrong with this code!?!

Sign in to comment.

Answers (4)

Image Analyst
Image Analyst on 10 Oct 2014
Waseem: see my attached demo to do a visible watermark.
  1 Comment
Image Analyst
Image Analyst on 11 Oct 2014
No books, but over a hundred tutorials/demos that I've written. A small handful are in my File Exchange: http://www.mathworks.com/matlabcentral/fileexchange/?term=authorid%3A31862. Keep watching my posts here. I post old and new demos all the time.

Sign in to comment.


Image Analyst
Image Analyst on 15 Mar 2013
That is done by my demo:
% Demo to watermark an image by hiding another image in a certain bit
% plane. Sometimes called "LSB Watermarking" or something similar.
% User is asked which bit plane they want to hide the image in.
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
imtool close all; % Close all imtool figures.
clear; % Erase all existing variables.
workspace; % Make sure the workspace panel is showing.
fontSize = 12;
% Read in the image what will have another image hidden into it.
baseFileName='moon.tif';
% baseFileName='cameraman.tif';
folder = fullfile(matlabroot, '\toolbox\images\imdemos');
% Get the full filename, with path prepended.
fullFileName = fullfile(folder, baseFileName);
if ~exist(fullFileName, 'file')
% Didn't find it there. Check the search path for it.
fullFileName = baseFileName; % No path this time.
if ~exist(fullFileName, 'file')
% Still didn't find it. Alert user.
errorMessage = sprintf('Error: %s does not exist.', fullFileName);
uiwait(warndlg(errorMessage));
return;
end
end
originalImage = imread(fullFileName);
% Get the number of rows and columns in the original image.
[visibleRows visibleColumns numberOfColorChannels] = size(originalImage);
if numberOfColorChannels > 1
% If it's color, extract the red channel.
originalImage = originalImage(:,:,1);
end
% Display the original gray scale image.
subplot(3, 3, 4);
imshow(originalImage, []);
title('Original Grayscale Starting Image', 'FontSize', fontSize);
% Enlarge figure to full screen.
set(gcf, 'units','normalized','outerposition',[0 0 1 1]);
set(gcf,'name','Demo by ImageAnalyst','numbertitle','off')
% read the message image you want to hide in the cover image
baseFileName='cameraman.tif';
% baseFileName='moon.tif';
% Get the full filename, with path prepended.
fullFileName = fullfile(folder, baseFileName);
if ~exist(fullFileName, 'file')
% Didn't find it there. Check the search path for it.
fullFileName = baseFileName; % No path this time.
if ~exist(fullFileName, 'file')
% Still didn't find it. Alert user.
errorMessage = sprintf('Error: %s does not exist.', fullFileName);
uiwait(warndlg(errorMessage));
return;
end
end
hiddenImage = imread(fullFileName);
% Get the number of rows and columns in the hidden image.
[hiddenRows hiddenColumns numberOfColorChannels] = size(hiddenImage);
if numberOfColorChannels > 1
% If it's color, extract the red channel.
hiddenImage = hiddenImage(:,:,1);
end
% Display the image.
subplot(3, 3, 1);
imshow(hiddenImage, []);
title('Image to be Hidden', 'FontSize', fontSize);
% Let's compute and display the histogram.
[pixelCount grayLevels] = imhist(hiddenImage);
subplot(3, 3, 2);
bar(pixelCount);
title('Histogram of image to be hidden', 'FontSize', fontSize);
xlim([0 grayLevels(end)]); % Scale x axis manually.
grid on;
thresholdValue = 70;
binaryImage = hiddenImage < thresholdValue;
% Display the image.
subplot(3, 3, 3);
imshow(binaryImage, []);
caption = sprintf('Hidden Image Thresholded at %d', thresholdValue);
title(caption, 'FontSize', fontSize);
% Get the bit plane to hide the image in.
prompt = 'Enter the bit plane you want to hide the image in (1 - 8) ';
dialogTitle = 'Enter Bit Plane to Replace';
numberOfLines = 1;
defaultResponse = {'6'};
bitToSet = str2double(cell2mat(inputdlg(prompt, dialogTitle, numberOfLines, defaultResponse)));
% If image to be hidden is bigger than the original image, scale it down.
if hiddenRows > visibleRows || hiddenColumns > visibleColumns
amountToShrink = min([visibleRows / hiddenRows, visibleColumns / hiddenColumns]);
binaryImage = imresize(binaryImage, amountToShrink);
% Need to update the number of rows and columns.
[hiddenRows hiddenColumns] = size(binaryImage);
end
% Tile the hiddenImage, if it's smaller, so that it will cover the original image.
if hiddenRows < visibleRows || hiddenColumns < visibleColumns
watermark = zeros(size(originalImage), 'uint8');
for column = 1:visibleColumns
for row = 1:visibleRows
watermark(row, column) = binaryImage(mod(row,hiddenRows)+1, mod(column,hiddenColumns)+1);
end
end
% Crop it to the same size as the original image.
watermark = watermark(1:visibleRows, 1:visibleColumns);
else
% Watermark is the same size as the original image.
watermark = binaryImage;
end
% Display the thresholded binary image - the watermark alone.
subplot(3, 3, 5);
imshow(watermark, []);
caption = sprintf('Hidden Image\nto be Inserted into Bit Plane %d', bitToSet);
title(caption, 'FontSize', fontSize);
% Set the bit of originalImage(a copy, actually) to the value of the watermark.
watermarkedImage = originalImage; % Initialize
for column = 1 : visibleColumns
for row = 1 : visibleRows
watermarkedImage(row, column) = bitset(originalImage(row, column), bitToSet, watermark(row, column));
end
end
% Display the image.
subplot(3, 3, 6);
imshow(watermarkedImage, []);
caption = sprintf('Final Watermarked Image\nwithout added Noise');
title(caption, 'FontSize', fontSize);
% add noise to watermarked image
noisyWatermarkedImage = imnoise(watermarkedImage,'gaussian', 0, 0.0005);
% Display the image.
subplot(3, 3, 7);
imshow(noisyWatermarkedImage, []);
caption = sprintf('Watermarked Image\nwith added Noise');
title(caption, 'FontSize', fontSize);
%====================================================================================
% Now let's pretend we are starting with the watermarked noisy corrupted image.
% We want to recover the watermark.
% Use the known bitplane of watermarked image to recover the watermark.
recoveredWatermark = zeros(size(noisyWatermarkedImage));
recoveredNoisyWatermark = zeros(size(noisyWatermarkedImage));
for column = 1:visibleColumns
for row = 1:visibleRows
recoveredWatermark(row, column) = bitget(watermarkedImage(row, column), bitToSet);
recoveredNoisyWatermark(row, column) = bitget(noisyWatermarkedImage(row, column), bitToSet);
end
end
% Scale the recovered watermark to 0=255
recoveredWatermark = uint8(255 * recoveredWatermark);
recoveredNoisyWatermark = uint8(255 * recoveredNoisyWatermark);
% Display the images.
subplot(3, 3, 8);
imshow(recoveredWatermark, []);
caption = sprintf('Watermark Recovered\nfrom Bit Plane %d of\nNoise-Free Watermarked Image', bitToSet);
title(caption, 'FontSize', fontSize);
% Display the images.
subplot(3, 3, 9);
imshow(recoveredNoisyWatermark, []);
caption = sprintf('Watermark Recovered\nfrom Bit Plane %d of\nNoisy Watermarked Image', bitToSet);
title(caption, 'FontSize', fontSize);
msgbox('Done with demo!');
  9 Comments
Image Analyst
Image Analyst on 18 Mar 2013
I'm finding it difficult to find time to work on your project. Do you have anything yet? Have you been able to adapt my or your code yet? I won't be able to get to it for a while.
Mithra
Mithra on 18 Mar 2013
Edited: Mithra on 18 Mar 2013
oh please don't take it wrong sir!!! I never ask anyone to do my home works for me!I just wanted some guidance with my own codes, to understand WHAT is exactly causing the malfunction! I already have some ready codes for watermarking, which work great, including the one you gave me. I also thank you A LOT for the time you put on this... but I still have one question: do we always HAVE to tile the massage image, and make it cover the original image, or that's optional( not to cover the entire original image)?!!!

Sign in to comment.


Shivaputra Narke
Shivaputra Narke on 16 Mar 2013
Edited: Walter Roberson on 17 Mar 2013
% It may work..
% edit ur code as
for x=1:r
for y=1:c
hst(x,y)=bitset(chst,1,msg(x,y));
end
end
imshow(hst);
  1 Comment
Mithra
Mithra on 16 Mar 2013
Edited: Mithra on 18 Mar 2013
thanks Shivaputra Narke for the point, but this time it shows a 133*81 completely white rectangle!!! any idea?

Sign in to comment.


Waseem AL Aqqad
Waseem AL Aqqad on 10 Oct 2014
Guys, what if I don't wanna hide the message in the cover image (visible watermarked), what should I do? any idea?
  4 Comments
DGM
DGM on 11 Jan 2024
Edited: DGM on 11 Jan 2024
Depends how your watermark image is designed and what you want the result to look like. I would generally suggest that a weighted average would not be the preferred choice.
Let's say you have the following images
Considering that these images are of dissimilar geometry and that nothing in base MATLAB or IPT caters to image blending or composition, I'm going to continue using MIMT tools.
Unidirectional blend modes
Let's start with something other than composition-only approaches. The first thing we need to do is make the images have a common geometry. That can be done however you choose. I'm going to use MIMT imstacker() because I'm lazy. Once we do that, both FG and BG are arranged as layers in a 4D stack. We can blend these two images using any blending mode we want. Name one from GIMP, Krita, or Photoshop, and imblend()/mergedown() can do it. For a watermark like the one we have, any mode with a neutral response at FG=0 will work.
% flat wm with lighten mode
% two images of any size, depth, or class
BG = imread('peppers.png');
FG = imread('watermark.png');
% arrange them such that the FG image is centered
% and scaled to fit the background image
% the output is a 4D image stack
outstack = imstacker({FG BG},'padding',0,'size','last','fit','inscribe');
% the stack can be disassembled and fed to replacepixels()/imblend()
% or used as is with mergedown().
% imcompose() GUI will accept it either way.
outpict = mergedown(outstack,0.25,'screen');
Okay, that works, but say you want to generate a text watermark like @cui,xingxing's example. That can be done. It's all basically the same, the only thing that's different is where FG comes from.
% the background image
BG = imread('peppers.png');
% generate the FG image
%FG = textim('pineapple','DSM48'); % convenient for single-line text
FG = textblock('pineapple banana raspberry',270,'font','DSM48','halign','center');
% stack the two images
outstack = imstacker({FG BG},'padding',0,'size','last','fit','inscribe');
% blend the stack
outpict = mergedown(outstack,0.25,'screen');
Bidirectional blend modes
Typically, a blend mode with a NRL at FG=0 can only lighten the image as in the examples above. If you know your way around blend modes, you know that some modes can do both. Typically, symmetric contrast modes will have a neutral response at FG=0.5. Say we had this as our watermark instead:
Everything is the same as before, but we can just pick an appropriate blend mode.
% two images of any size, depth, or class
BG = imread('peppers.png');
FG = imread('wmgray.png');
% stack the two images
outstack = imstacker({FG BG},'padding',0.5,'size','last','fit','inscribe');
% blend the stack
outpict = mergedown(outstack,1,'grainmerge');
Scalar alpha composition
Now let's back up and consider the composition-only suggestion. What happens if we just do a simple average between the two images?
We can do this a number of ways. We could use IPT imfuse(), but since the image is already stacked, we could just directly average it.
% two images of any size, depth, or class
BG = imread('peppers.png');
FG = imread('watermark.png');
% stack the two images
outstack = imstacker({FG BG},'padding',0,'size','last','fit','inscribe');
% blend the stack
outpict = mean(outstack,4);
Considering MIMT-specific tools, we can do it as before, using mergedown() or imblend():
% ...
% blend the stack
fgalpha = 0.5; % pick overlay opacity
outpict = mergedown(outstack,fgalpha,'normal');
... or we can use replacepixels() with a scalar alpha parameter. Since we're working with a stack, it's a little less verbose to use mergedown() in this instance, though it's probably not faster.
% ...
% the stack can be disassembled and fed to replacepixels()/imblend()
fgalpha = 0.5; % pick overlay opacity
outpict = replacepixels(outstack(:,:,:,1),outstack(:,:,:,2),fgalpha);
Either way, the result is pretty terrible, as the watermark affects the entire image. There is no FG color which is transparent.
Self-masking composition
Well, what happens if we were to use the foreground image (the watermark) as its own alpha? That way only the white parts of the watermark will influence the result. Again, we can do this with either mergedown()/imblend(), or we could just unstack everything and feed it to replacepixels().
% two images of any size, depth, or class
BG = imread('peppers.png');
FG = imread('watermark.png');
% stack the two images
outstack = imstacker({FG BG},'padding',0,'size','last','fit','inscribe');
% blend the stack
fgalpha = 0.5; % pick overlay opacity
outpict = mergedown(outstack,fgalpha,'foglighten');
... or with replacepixels()
% ...
% the stack can be disassembled and fed to replacepixels()/imblend()
fgalpha = 0.5; % pick overlay opacity
outpict = replacepixels(outstack(:,:,:,1),outstack(:,:,:,2),fgalpha*outstack(:,:,:,1));
Well that looks pretty good at first glance, but if you look close, it's not really. There's plenty of soft edges in the FG, so there are plenty of edge regions which are actually black. While it might help with contrast in some cases, it generally just looks like a bad masking job.
Masked composition with a uniform color
Instead of using the FG as both a mask and as FG color content, use it strictly as a mask. Fill the FG with some uniform color field. For the sake of illustration, I'm not going to use white here, otherwise it will look nearly identical to the 'screen' blend, when the two are not similar for other colors. As before, it can be done with imblend()
% ...
% the stack can be disassembled and fed to replacepixels()/imblend()
fgcolor = [1 0.5 1]; % or any color
fgalpha = 0.5; % pick overlay opacity
FG = colorpict(imsize(outstack,2),fgcolor); % a solid color field
FG = joinalpha(FG,outstack(:,:,:,1)); % attach alpha
outpict = imblend(FG,outstack(:,:,:,2),fgalpha,'normal');
outpict = splitalpha(outpict); % strip any extraneous alpha
... though this is simpler done with replacepixels():
% ...
% the stack can be disassembled and fed to replacepixels()/imblend()
fgcolor = [1 0.5 1]; % or any color
fgalpha = 0.5; % pick overlay opacity
outpict = replacepixels([fgcolor fgalpha],outstack(:,:,:,2),outstack(:,:,:,1));
Again, these examples are based on MIMT, so if you get errors about missing functions, then that's why. There are examples of all these things elsewhere on the forum, so if you want to stick to base MATLAB/IPT tools, you can find those.
cui,xingxing
cui,xingxing on 23 Jan 2024
@DGM ,Thanks for the detailed examples!

Sign in to comment.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!