C# - Bitmap - Edited png image was saved incorrectly

172 views Asked by At

I'm implementing image steganography, using the LSB of each pixel. If size of secret text is low (<10) it works fine, but it fails for longer texts.

My code:

private void processButtonLSB_Click(object sender, EventArgs e)
{
    String dest = destLSBText.Text;
    Bitmap img = (Bitmap)Bitmap.FromFile(dest); // bitmap of the given image
    byte[] temp = GetBytes(this.secretText.Text);
    byte[] secretText = new byte[temp.Length + 1];
    temp.CopyTo(secretText, 0);
    secretText[temp.Length] = 0x0; // adding last 0 byte
    BitArray bits = new BitArray(secretText); // parsing bytes to bits of secret text
    StringBuilder sb = new StringBuilder(); // debug feature
    for (int i = 0; i < bits.Length; i++)
    {
        bool bit = bits.Get(i); // getting i bit of secret text
        int x = i / img.Width;
        int y = i % img.Height;

        int argb = img.GetPixel(x, y).ToArgb(); // getting argb of the original image
        bool argbBit = argb % 2 != 0; // getting last bit of the pixel
        if (bit && !argbBit || !bit && argbBit) // ensure if we need to make changes
        {
            setBit(ref argb, 0, !argbBit); // changing last bit
            img.SetPixel(x, y, Color.FromArgb(argb)); // changing bitmap
        }

        if (sb.Length > 0)
            sb.Append(",");
        sb.Append("0x" + argb.ToString("X4")); // adding modified pixel to debug #2
    }
    MessageBox.Show(sb.ToString()); // showing all pixels with payload
    saveImage(img, ImageFormat.Png);
}

private void retrieveButtonLSB_Click(object sender, EventArgs e)
{
    String dest = destLSBText.Text;
    Bitmap img = (Bitmap) Bitmap.FromFile(dest); // bitmap with payload
    List<bool> bits = new List<bool>();
    byte[] foundBytes = null;
    StringBuilder sb = new StringBuilder(); // debug feature
    for (int i = 0; i < img.Width * img.Height; i++) // iterating thru all pixels until zero byte is met
    {
        int x = i / img.Width;
        int y = i % img.Height;
        int argb = img.GetPixel(x, y).ToArgb(); // getting argb
        bool bit = argb % 2 != 0; // last bit
        bits.Add(bit);

        if (sb.Length > 0)
            sb.Append(",");
        sb.Append("0x" + argb.ToString("X4")); // adding argb to debug #3

        if (bits.Count % 8 == 0) // checking if last byte is 0x0
        {
            foundBytes = new byte[bits.Count / 8];
            BitArray bitArray = new BitArray(bits.ToArray());
            bitArray.CopyTo(foundBytes, 0);
            if (foundBytes[foundBytes.Length - 1] == 0x0)
                break;
        }
    }
    MessageBox.Show(sb.ToString()); // showing all pixels with payload

    if (foundBytes != null)
        secretText.Text = GetString(foundBytes);
}

processButtonLSB_Click creates an image with the given hidden text, and retrieveButtonLSB_Click retrieves hidden text from an image.

I added message boxes to detect the pixels on each step, and got the following results

Original image (first pixels)

0xFF0C6128,0xFF0C6128,0xFF0B6027,0xFF0B6027,0xFF0A5F26,0xFF0A5F26,0xFF0A5D25,0xFF0A5D25,...

Changed image (from #1)

0xFF0C6128,0xFF0C6128,0xFF0B6026,0xFF0B6027,0xFF0A5F26,0xFF0A5F27,0xFF0A5D25,0xFF0A5D24,...

Saved image (from #2)

0xFF0C6128,0xFF0C6129,0xFF0B6027,0xFF0B6027,0xFF0A5F27,0xFF0A5F26,0xFF0A5D24,0xFF0A5D24,...

But why? Maybe I have made a mistake? I checked everything twice and did not find anything.

The code that saves the image:

private String saveImage(Image img, ImageFormat format)
{
    String path = getSavePath(format);
    if(path != null)
    {
        ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
        ImageCodecInfo ici = null;

        foreach (ImageCodecInfo codec in codecs)
        {
            if (codec.MimeType == "image/png")
                ici = codec;
        }

        EncoderParameters ep = new EncoderParameters();
        ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
        img.Save(path, ici, ep);
        return path;
    }
    return null;
}

Other dependencies (I'm sure it's correct)

private bool getBit(int b, int bitNumber)
{
    return (b & (1 << bitNumber)) != 0;
}

private void setBit(ref int b, int bitNumber, bool value)
{
    int mask = 1 << bitNumber;
    if(value)
        b |= mask;
    else
        b &= ~mask;
}

private byte[] GetBytes(string str)
{
    return Encoding.UTF8.GetBytes(str);
}

private string GetString(byte[] bytes)
{
    return Encoding.UTF8.GetString(bytes);
}
0

There are 0 answers