2

My problem is as follows :

  • My problem is that even after doing LSB replacement after the quantization step I still get errors and changes on the detection side. for strings, letters get changed but for bitmaps the image isn't readable as deduced from getting "Parameters no valid". I've tried a lot of debugging and I just can't figure it out.

  • My goal is pretty simple, insert a set of bits (before string or Bitmap) into a JPEG image, save it and be able to detect and extract said set of bits to its original form. I've been successful with BMP and PNG as there is no compression there, but JPEG is another story. Btw I'm doing LSB replacement.

I understand what I need to do, apply the LSB replacement after the DCT coefficients have been quantized. For that purpose I have been using a JPEG Encoder and modified what I needed in the appropriate spot.

I modified the method EncodeImageBufferToJpg to convert a string or bitmap into a bit array (int[]) and then do LSB replacement to one Coefficient per block for each channel Y, Cb, Cr.

This here is my modified method for EncodeImageBufferToJpg, plus the Detection+Process method I use to reconstruct the message: Link Here.

For the Y channel for example :

  • In encoding :

                Int16[] DCT_Quant_Y = Do_FDCT_Quantization_And_ZigZag(Y_Data, Tables.FDCT_Y_Quantization_Table);
                if (!StegoEncodeDone)
                {
                    // We clear the LSB to 0
                    DCT_Quant_Y[DCIndex] -= Convert.ToInt16(DCT_Quant_Y[DCIndex] % 2);
                    // We add the bit to the LSB
                    DCT_Quant_Y[DCIndex] += Convert.ToInt16(MsgBits[MsgIndx]);
                    // Ys for debug print
                    Ys.Add(DCT_Quant_Y[DCIndex]);
                    MsgIndx++;
                    if (MsgIndx >= MsgBits.Length) StegoEncodeDone = true;
                }
                DoHuffmanEncoding(DCT_Quant_Y, ref prev_DC_Y, Tables.Y_DC_Huffman_Table, Tables.Y_AC_Huffman_Table, OutputStream);
    
  • and in detection :

                Int16[] DCT_Quant_Y = Do_FDCT_Quantization_And_ZigZag(Y_Data, Tables.FDCT_Y_Quantization_Table);
                // SteganoDecode *********************************************
                if (!StegoDecodeDone)
                {
                    int Dtt = Math.Abs(DCT_Quant_Y[DCIndex] % 2);
                    int DYY = Y_Data[DCIndex];
                    int DDCTYYB = DCT_Quant_Y[DCIndex];
                    Ys.Add(DCT_Quant_Y[DCIndex]);
                    // Si le DCT Coefficient est negatif le % retournais un -1 mais binaire => 0,1 => positif
                    charValue = charValue * 2 + Math.Abs(DCT_Quant_Y[DCIndex] % 2);
                    ProcessStaganoDecode();
                }
                // End *********************************************************
                DCT_Quant_Y.CopyTo(Y, index);
    
    public void ProcessStaganoDecode()
    {
        Counter++;
        cc++;
    
        if (IDFound) MsgBits.Add(charValue % 2);
        else IDBits.Add(charValue % 2);
    
        if (Counter == 8)
        {
            // If we find a '-' we inc, else we set to 0. because they have to be 3 consecutive "---"
            char ccs = (char)reverseBits(charValue);
            if (((char)reverseBits(charValue)) == '-')
            {
                SepCounter++;
            }
            else SepCounter = 0;
    
            if (SepCounter >= 3)
            {
                if (IDFound)
                {
                    MsgBits.RemoveRange(MsgBits.Count - 3 * 8, 3 * 8);
                    StegoDecodeDone = MarqueFound = true;
                }
                else
                {
                    IDFound = true;
                    IDBits.RemoveRange(IDBits.Count - 3 * 8, 3 * 8);
                    string ID = BitToString(IDBits);
                    IDNum = Convert.ToInt16(BitToString(IDBits));
                    Console.WriteLine("ID Found : " + IDNum);
                }
                SepCounter = 0;
            }
            charValue = 0;
            Counter = 0;
        }
    }
    

All the code is in the class: BaseJPEGEncoder.

Here's the VS 2015 C# project for you to check the rest of the classes etc. I can only put 2 links, so sorry couldn't put the original: Here. I got the original encoder from "A simple JPEG encoder in C#" at CodeProject

I've read some answers to other questions from these two people, and I would love to get their attention to give me some help if they can: Sneftel and Reti43. Couldn't find a way to contact them.

halfer
  • 19,824
  • 17
  • 99
  • 186
LatFoued
  • 21
  • 5
  • JPEG compression provides an approximation of the original image. The decode approximates what was encoded. One-bit changes resulting from rounding errors and floating point to integer conversion are a natural part of JPEG. – user3344003 May 03 '16 at 17:11
  • You're not explicit what kind of error you get. From what I understand, the extraction works properly when you hide text, i.e., with ID 1, 2 or 4, but not when you have an image. In that case, your embedding/extraction should be correct and you can verify that with your debugging of what bits you embed and extract. Then, the error should be either in the `BitmapToByteArr()` or `BitToBitmap()` functions. If that's the case, please [update your question](https://stackoverflow.com/help/mcve) with only those two functions as they should be sufficient to replicate the error. – Reti43 May 06 '16 at 09:52
  • @Reti43, what was happening is simple, i embed an array of bits in the LSB of DCT Coefficients after quantization, so i should expect those Coefficients to not change value when i load the encoded/saved JPEG, Which is not the case. 80% of the time, when i try the extraction on the saved JPEG, there are some bits that get altered. And that's what has been bugging me, i'm embeding after the lossy part of the JPEG compression. – LatFoued May 07 '16 at 21:43
  • I understand that. If the coefficients **themselves** change between encoding and decoding, the encoder you got is simply flawed. You can test that. However, it's more likely that you don't extract what you embed. In that case, there is a flaw in your embedding/extraction logic. But, my understanding from your first bullet point is that the extraction works for text and not for images. In this case, your embedding/extraction are correct and there is a flaw with the above mentioned functions. Which of these three cases are we dealing with? – Reti43 May 08 '16 at 07:30
  • The first case i think, might be the second. When the coefficients change, extraction for text becomes faulty too, some letters get changed which is logical in that case. Images though throw an exception as the code can't recreate a proper Bitmap. – LatFoued May 08 '16 at 11:42
  • Can you clarify exactly what you mean by "letters get changed"? Can you show us an example of a bit sequence you embed and what you extract? Obviously, if you extract the wrong bitstream, reconstructing an image from a byte array will not work. – Reti43 May 09 '16 at 14:45
  • I'm sorry for not resonding sooner and not being able to give you a concrete example, i'm very busy :(. But it's similar to what you said for images. For text embedding, the changes are visible because when retrieved bits aren't the same as te embedded ones and it's generally a rate of 1-2 bits change per 5 bytes. Retrieving the bits->byte->char will retrieve then different chars, so letters that were embedded aren't the same one that are retrieved. I hope that wasn't confusing :). – LatFoued May 12 '16 at 11:15

0 Answers0