matlabimage-processingsteganographyhaar-waveletdwt

Using DWT Haar transform method to hide text inside coefficients


I have written the code below which takes the Haar transform of an image and embeds a secret message, bit by bit on in the least significant bits of the coefficients. To use the bitset function I convert the double coefficients to uint64 and I change them back after the embedding.

function DwtSteg(address,message)

      coverImage=imread(address);
      ascii=uint8(message);

      [LL LH HL HH]=dwt2(coverImage,'haar');

      LH=round(LH);
      HL=round(HL);

      subplot(1,2,1)
      imshow(LH)

      [r c]=size(LL);

      wc=1;
      bc=1;
      done=false;
      for i=1:r        
          if(done)
              break
          end
          for j=1:c                      

              if(bc==8)
                 bc=1;
                 wc=wc+1;

              end

              if(wc==length(message))
                  done=true;
                  break;
              end

              xb = typecast(LH(i,j), 'uint64' );
              xb=bitset(xb,1,bitget(ascii(wc),bc));
              xb%***
              LH(i,j)=typecast(xb, 'double');  
              bc=bc+1;                    
          end    

      end

      subplot(1,2,2)
      imshow(LH)

      stegoImage=idwt2(LL ,HL,LH, HH,'haar');

      figure(2)
      imshow(uint8(stegoImage));

      imwrite(uint8(stegoImage),'stegoImage.tiff');

end

But when I run below code to extract my message from Image.The coefficients aren't same as the converted ones.(consider '***' on both function) :

function [ str ] = DwtDesteg( address)
    str='';
    image=imread(address);
    [LL LH HL HH]=dwt2(image,'haar');

      [r c]=size(LL);
      LH=round(LH);
      bc=1;
      ch=0;
      for i=1:r                
          for j=1:c                                                          
              if(bc==8)
                 bc=1;
                 str=strcat(str,ch);
                 char(ch)

              end
              xb = typecast(LH(i,j), 'uint64');
              xb%***
              ch=bitset(ch,bc,bitget(xb,1));
              bitget(xb,1)                        

              bc=bc+1;

          end    

      end

end

Solution

  • Since you're manipulating the DWT coefficients as integers, you may as well work with the integer transform, IWT. So, instead of doing

    [LL LH HL HH]=dwt2(coverImage,'haar');
    

    use a lifting scheme with lwt2().

    % set up a Haar integer lifting scheme
    els = {'p',[-0.125 0.125],0};
    lshaarInt = liftwave('haar','int2int');
    lsnewInt = addlift(lshaarInt,els);
    % transform away!
    [LL,LH,HL,HH] = lwt2(double(image),lsnewInt);
    

    At this point, you don't have to do any rounding, e.g., LH = round(LH). Just proceed to embedding straight away.

    imread() loads the image as a uint8 type. When you pass that to dwt2(), it will convert everything to double. However, if you try to pass a uint8 matrix to lwt2(), it will complain, so you have to manually convert it to double. For the inverse transform simply do

    image = ilwt2(LH,LH,HL,HH,lsnewInt);
    image = uint8(image);
    

    Now, your code has some other bugs, such as typecasting the LH coefficient to uint64 messes up the bitsetting. It's a bit troubling to chase everything, so I rewrote your functions.

    embed.m

    function embed(filein,fileout,message)
    image = imread(filein);
    message = double(message);
    
    els = {'p',[-0.125 0.125],0};
    lshaarInt = liftwave('haar','int2int');
    lsnewInt = addlift(lshaarInt,els);
    [LL,LH,HL,HH] = lwt2(double(image),lsnewInt);
    
    col = size(LH,2);
    r = 1;
    c = 1;
    
    for i = 1:length(message);
        letter = message(i);
        for b = 8:-1:1;
            LH(r,c) = bitset(LH(r,c),1,bitget(letter,b));
            c = c + 1;
            if (c > col)
                r = r + 1;
                c = 0;
            end
        end
    end
    
    stego = uint8(ilwt2(LL,LH,HL,HH,lsnewInt));
    
    imshow(stego);
    imwrite(stego,fileout);
    end
    

    extract.m

    function extract(filename,messageLength)
    image = imread(filename);
    
    els = {'p',[-0.125 0.125],0};
    lshaarInt = liftwave('haar','int2int');
    lsnewInt = addlift(lshaarInt,els);
    [LL,LH,HL,HH] = lwt2(double(image),lsnewInt);
    
    col = size(LH,2);
    r = 1;
    c = 1;
    
    secret = zeros(messageLength,1);
    for i = 1:messageLength;
        letter = 0;
        for b = 8:-1:1;
            letter = bitset(letter,b,bitget(LH(r,c),1));
            c = c + 1;
            if (c > col)
                r = r + 1;
                c = 0;
            end
        end
        secret(i) = char(letter);
    end
    
    disp(char(secret'));
    end
    

    You'll notice that the extraction function requires the message length as a parameter. This is because your embedding method does not encode in any way when to stop extracting bits. If you don't want the receiver to be dependent on the knowledge of that parameter, as he shouldn't be, you have a couple of options. However, I'll leave that implementation to you because it's outside the scope of this question.

    1. During embedding put some bits in your pixels that translate to the message length, e.g., the first 16 bits/wavelet coefficients. So when the extraction begins, it reads the first 16 bits, converts them to an integer and then carries on with the message extraction.
    2. Along with your message, embed some extra bits, e.g., 8 zeros, so that when you're extracting the secret, you know to stop once you encounter them.