Search code examples
c#.netfftfrequencysteganography

Trying to hide information in JPEG picture using frequency domain FFT steganography but can't get correct output picture after encryption


I'm studying frequency domain steganography. My task is to hide text information in JPEG picture using FFT. I prepared some code in C# which takes a picture, prepares it for FFT algorithm and computes its frequency domain using FFT. Input picture is here, result of FFT algorithm is here. Then this code gets text message, takes complex value from picture and changes LeastSignificantBit (LSB) of this complex value. After this we have modified frequency domain in this pucture, and as you can see, there is a message from center to right encrypted. But when I try to use IFFT in modified picture, the output of this is just a white picture. So obviously I somehow damage the picture during changing frequency domain. What may be wrong with this code?

The code with detailed comments is below:

        // Uploading image
        var image = AForge.Imaging.Image.FromFile("input.jpg");

        // Find the nearest power of 2 dimensions
        int width = (int)Math.Pow(2, Math.Ceiling(Math.Log(image.Width, 2)));
        int height = (int)Math.Pow(2, Math.Ceiling(Math.Log(image.Height, 2)));
        // Resize the image to the nearest power of 2 dimensions
        Bitmap resizedImage = new Bitmap(image, width, height);

        // Converting image to grayscale
        Grayscale filter = new Grayscale(0.2125, 0.7154, 0.0721);
        Bitmap grayImage = filter.Apply(resizedImage);

        // Converting image to the ComplexImage
        ComplexImage complexImage = ComplexImage.FromBitmap(grayImage);

        // Applying FFT
        complexImage.ForwardFourierTransform();

        // Saving result of FFT
        complexImage.ToBitmap().Save("fourier.jpg");

        // -------------------------------------------------------------------------

        // Converting text message to binary
        string textMessage = "Hello world";
        byte[] messageBytes = Encoding.Unicode.GetBytes(textMessage);
        BitArray messageBits = new BitArray(messageBytes);

        // Embedding text bits into the frequency domain
        int bitIndex = 0;
        for (int y = complexImage.Height / 2; y < complexImage.Height; y++)
        {
            for (int x = complexImage.Width / 2; x < complexImage.Width; x++)
            {
                if (bitIndex >= messageBits.Length)
                {
                    break;
                }

                // Getting complex value of frequency
                Complex complexValue = complexImage.Data[y, x];

                // Replacing the LSB of the real and imaginary parts with a bit from the message
                complexValue.Re = SetLeastSignificantBit(complexValue.Re, messageBits[bitIndex++]);
                complexValue.Im = SetLeastSignificantBit(complexValue.Im, messageBits[bitIndex++]);

                // Applying new value
                complexImage.Data[y, x] = complexValue;
            }
        }

        // Setting LSB
        static float SetLeastSignificantBit(double value, bool bit)
        {
            int intValue = BitConverter.ToInt32(BitConverter.GetBytes(value), 0);
            intValue = bit ? (intValue | 1) : (intValue & ~1);
            return BitConverter.ToSingle(BitConverter.GetBytes(intValue), 0);
        }

        // -------------------------------------------------------------------------

        complexImage.ToBitmap().Save("fourier222.jpg");

        // Applying IFFT
        complexImage.BackwardFourierTransform();

        complexImage.ToBitmap().Save("output.jpg");

Solution

  • After some research I got to know that it is better not to use LSB approach but to change "null" values of frequency domain and adding or substracting real part of frequency data. "Null" value is real value of ComplexImage[x, y] taken from frequency data which is equal zero. In this code I use top left corner to find "null" values. Also while moving through the frequency domain it's better to avoid the middle of frequency domain because it can cause troubles and incorrect encoded data. Some code example for encryption:

    public static ComplexImage EmbedData(ComplexImage freqDomain, byte[] data)
        {
            ComplexImage modifiedFreqDomain = freqDomain;
            int xCoordinate = 0;
            int yCoordinate = 0;
    
            if (data.Length * 8 > modifiedFreqDomain.Data.Length)
            {
                throw new Exception("Data too large to embed into the frequency domain");
            }
    
            int bitIndex = 0;
            for (int k = 0; k < data.Length; k++)
            {
                for (int i = 0; i < 8; i++)
                {
                    int bit = (data[k] >> i) & 1;
                    double realPart = modifiedFreqDomain.Data[xCoordinate, yCoordinate].Re;
    
                    if (bit == 1)
                    {
                        realPart += 0.00025;
                    }
                    else
                    {
                        realPart -= 0.00025;
                    }
    
                    modifiedFreqDomain.Data[xCoordinate, yCoordinate] = new Complex(realPart, modifiedFreqDomain.Data[xCoordinate, yCoordinate].Im);
    
                    yCoordinate += 1;
    
                    if (yCoordinate >= freqDomain.Width / 2.5)
                    {
                        yCoordinate = 0;
                        xCoordinate += 1;
                    }
                    bitIndex++;
                }
            }
    
            return modifiedFreqDomain;
        }
    

    When I use this code for encryption, image may be slightly changed but it is not cricical. Also this algorithm should check for "not-null" values because we can't take data from it. So there is some way to improve this method but basically it works.