Converting from Format24bppRgb to Format8bppIndexed distorts the image

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • arashkk
    New Member
    • Jun 2007
    • 8

    #1

    Converting from Format24bppRgb to Format8bppIndexed distorts the image

    I convert a bitmap from Format24bppRgb to Format8bppIndex ed this way:

    Code:
    Bitmap bmp = new Bitmap("c:\\flower.bmp");
    BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    Bitmap temp = new Bitmap(bmData.Width, bmData.Height, bmData.Stride, PixelFormat.Format8bppIndexed, bmData.Scan0);
    bmp.UnlockBits(bmData);
    pictureBox1.Image = temp; //But then the result is a destroyed image:
    Would you please tell me where is the problem?
    Attached Files
    Last edited by tlhintoq; Dec 26 '09, 03:19 PM. Reason: [CODE] ...Your code goes between code tags [/CODE]
  • tlhintoq
    Recognized Expert Specialist
    • Mar 2008
    • 3532

    #2
    TIP: When you are writing your question, there is a button on the tool bar that wraps the [code] tags around your copy/pasted code. It helps a bunch. Its the button with a '#' on it. More on tags. They're cool. Check'em out.

    Comment

    • tlhintoq
      Recognized Expert Specialist
      • Mar 2008
      • 3532

      #3
      There is a lot more to converting an image than that.
      Below is a "boiled down" sample that comes from another article.


      Code:
      using System;
      using System.Collections;
      using System.Collections.Generic;
      using System.ComponentModel;
      using System.Data;
      using System.Drawing;
      using System.Drawing.Imaging;
      using System.Drawing.Printing;
      using System.Drawing.Drawing2D;
      
      using System.IO;
      using System.Runtime.InteropServices;
      using System.Text;
      using System.Windows.Forms;
      using System.Drawing.Printing;
      
      namespace FormPrinting
      {
          public partial class Form1 : Form
          {
              private Font printFont;
              private StreamReader streamToPrint;
              private Image Orig;
              private Image Converted;
              private Hashtable m_knownColors = new Hashtable((int)Math.Pow(2, 20), 1.0f); 
      
              public Form1()
              {
                  InitializeComponent();
              }
      
      
              private int GetPixelInfoSize(PixelFormat format)
              {
                  switch (format)
                  {
                      case PixelFormat.Format24bppRgb:
                          {
                              return 3;
                          }
                      default:
                          {
                              throw new ApplicationException("Only 24bit colors supported now");
                          }
                  }
      
              }
      
      
              // The PrintPage event is raised for each page to be printed.
              private void pd_PrintPage(object sender, PrintPageEventArgs ev)
              {
                  
                  ev.Graphics.PageScale = 25;// Print at 100% or 1:1 ratio 
                  //ev.PageSettings.PrinterSettings.
                  ev.Graphics.DrawImageUnscaled(System.Drawing.Image.FromFile(browseFile1.Text),0,0);
                  ev.HasMorePages = false;
                  return;
                  float linesPerPage = 0;
                  float yPos = 0;
                  int count = 0;
                  float leftMargin = ev.MarginBounds.Left;
                  float topMargin = ev.MarginBounds.Top;
                  string line = null;
      
                  // Calculate the number of lines per page.
                  linesPerPage = ev.MarginBounds.Height /
                     printFont.GetHeight(ev.Graphics);
      
                  // Print each line of the file.
                  while (count < linesPerPage &&
                     ((line = streamToPrint.ReadLine()) != null))
                  {
                      yPos = topMargin + (count *
                         printFont.GetHeight(ev.Graphics));
                      ev.Graphics.DrawString(line, printFont, Brushes.Black,
                         leftMargin, yPos, new StringFormat());
                      count++;
                  }
      
                  // If more lines exist, print another page.
                  if (line != null)
                      ev.HasMorePages = true;
                  else
                      ev.HasMorePages = false;
              }
      
              private void browseFile1_PathChanged(object sender, Saint.Controls.BrowseFile.TextChangedEventArgs e)
              {
                  Orig = Saint.GDI.Graphics.ImageFromFile(browseFile1.Text);
                  pbOrig.Image = Orig;
                  pbOrig.SizeMode = PictureBoxSizeMode.Zoom;
                  Converted = (Image) ConvertTo8bppFormat((Bitmap) Orig);
                  Converted = pbConverted.Image = Converted;
                  pbConverted.SizeMode = PictureBoxSizeMode.Zoom;
              }
      
              private void btn8bppIndex_Click(object sender, EventArgs e)
              {
      
              }
              public Bitmap ConvertTo8bppFormat(Bitmap bmpSource)
              {
                  int imageWidth = bmpSource.Width;
                  int imageHeight = bmpSource.Height;
      
                  Bitmap bmpDest = null;
                  BitmapData bmpDataDest = null;
                  BitmapData bmpDataSource = null;
      
                  try
                  {
                      // Create new image with 8BPP format
                      bmpDest = new Bitmap(
                          imageWidth,
                          imageHeight,
                          PixelFormat.Format8bppIndexed
                          );
      
                      // Lock bitmap in memory
                      bmpDataDest = bmpDest.LockBits(
                          new Rectangle(0, 0, imageWidth, imageHeight),
                          ImageLockMode.ReadWrite,
                          bmpDest.PixelFormat
                          );
      
                      bmpDataSource = bmpSource.LockBits(
                          new Rectangle(0, 0, imageWidth, imageHeight),
                          ImageLockMode.ReadOnly,
                          bmpSource.PixelFormat
                      );
      
                      int pixelSize = GetPixelInfoSize(bmpDataSource.PixelFormat);
                      byte[] buffer = new byte[imageWidth * imageHeight * pixelSize];
                      byte[] destBuffer = new byte[imageWidth * imageHeight];
      
                      // Read all data to buffer
                      ReadBmpData(bmpDataSource, buffer, pixelSize, imageWidth, imageHeight);
      
                      // Get color indexes
                      MatchColors(buffer, destBuffer, pixelSize, bmpDest.Palette);
      
                      // Copy all colors to destination bitmaps
                      WriteBmpData(bmpDataDest, destBuffer, imageWidth, imageHeight);
      
                      return bmpDest;
                  }
                  finally
                  {
                      if (bmpDest != null) bmpDest.UnlockBits(bmpDataDest);
                      if (bmpSource != null) bmpSource.UnlockBits(bmpDataSource);
                  }
              }
      
              /// <summary>
              /// Reads all bitmap data at once
              /// </summary>
              private void ReadBmpData(
                  BitmapData bmpDataSource,
                  byte[] buffer,
                  int pixelSize,
                  int width,
                  int height)
              {
                  // Get unmanaged data start address
                  int addrStart = bmpDataSource.Scan0.ToInt32();
      
                  for (int i = 0; i < height; i++)
                  {
                      // Get address of next row
                      IntPtr realByteAddr = new IntPtr(addrStart +
                          System.Convert.ToInt32(i * bmpDataSource.Stride)
                          );
      
                      // Perform copy from unmanaged memory
                      // to managed buffer
                      Marshal.Copy(
                          realByteAddr,
                          buffer,
                          (int)(i * width * pixelSize),
                          (int)(width * pixelSize)
                      );
                  }
              }
      
              /// <summary>
              /// Writes bitmap data to unmanaged memory
              /// </summary>
              private void WriteBmpData(
                  BitmapData bmpDataDest,
                  byte[] destBuffer,
                  int imageWidth,
                  int imageHeight)
              {
                  // Get unmanaged data start address
                  int addrStart = bmpDataDest.Scan0.ToInt32();
      
                  for (int i = 0; i < imageHeight; i++)
                  {
                      // Get address of next row
                      IntPtr realByteAddr = new IntPtr(addrStart +
                          System.Convert.ToInt32(i * bmpDataDest.Stride)
                          );
      
                      // Perform copy from managed buffer
                      // to unmanaged memory
                      Marshal.Copy(
                          destBuffer,
                          i * imageWidth,
                          realByteAddr,
                          imageWidth
                      );
                  }
              }
      
              /// <summary>
              /// This method matches indices from pallete ( 256 colors )
              /// for each given 24bit color
              /// </summary>
              /// <param name="buffer">Buffer that contains color for each pixel</param>
              /// <param name="destBuffer">Destination buffer that will contain index 
              /// for each color</param>
              /// <param name="pixelSize">Size of pixel info ( 24bit colors supported )</param>
              /// <param name="pallete">Colors pallete ( 256 colors )</param>
              private void MatchColors(
                  byte[] buffer,
                  byte[] destBuffer,
                  int pixelSize,
                  ColorPalette pallete)
              {
                  int length = destBuffer.Length;
      
                  // Temp storage for color info
                  byte[] temp = new byte[pixelSize];
      
                  int palleteSize = pallete.Entries.Length;
      
                  int mult_1 = 256;
                  int mult_2 = 256 * 256;
      
                  int currentKey = 0;
      
                  // For each color
                  for (int i = 0; i < length; i++)
                  {
                      // Get next color
                      Array.Copy(buffer, i * pixelSize, temp, 0, pixelSize);
      
                      // Build key for hash table
                      currentKey = temp[0] + temp[1] * mult_1 + temp[2] * mult_2;
      
                      // If hash table already contains such color - fetch it
                      // Otherwise perform calculation of similar color and save it to HT
                      if (!m_knownColors.ContainsKey(currentKey))
                      {
                          destBuffer[i] = GetSimilarColor(pallete, temp, palleteSize);
                          m_knownColors.Add(currentKey, destBuffer[i]);
                      }
                      else
                      {
                          destBuffer[i] = (byte)m_knownColors[currentKey];
                      }
                  }// for
              }
      
              /// <summary>
              /// Returns Similar color
              /// </summary>
              /// <param name="palette"></param>
              /// <param name="color"></param>
              /// <returns></returns>
              private byte GetSimilarColor(ColorPalette palette, byte[] color, int palleteSize)
              {
                  byte minDiff = byte.MaxValue;
                  byte index = 0;
      
                  if (color.Length == 3)// Implemented for 24bpp color
                  {
                      // Loop all pallete ( 256 colors )
                      for (int i = 0; i < palleteSize - 1; i++)
                      {
                          // Calculate similar color
                          byte currentDiff = GetMaxDiff(color, palette.Entries[i]);
      
                          if (currentDiff < minDiff)
                          {
                              minDiff = currentDiff;
                              index = (byte)i;
                          }
                      }// for
                  }
                  else// TODO implement it for other color types
                  {
                      throw new ApplicationException("Only 24bit colors supported now");
                  }
      
                  return index;
              }
      
              /// <summary>
              /// Return similar color
              /// </summary>
              /// <param name="a"></param>
              /// <param name="b"></param>
              /// <returns></returns>
              private static byte GetMaxDiff(byte[] a, Color b)
              {
                  // Get difference between components ( red green blue )
                  // of given color and appropriate components of pallete color
                  byte bDiff = a[0] > b.B ? (byte)(a[0] - b.B) : (byte)(b.B - a[0]);
                  byte gDiff = a[1] > b.G ? (byte)(a[1] - b.G) : (byte)(b.G - a[1]);
                  byte rDiff = a[2] > b.R ? (byte)(a[2] - b.R) : (byte)(b.R - a[2]);
      
                  // Get max difference
                  byte max = bDiff > gDiff ? bDiff : gDiff;
                  max = max > rDiff ? max : rDiff;
      
                  return max;
              }
          }
      }

      Comment

      • arashkk
        New Member
        • Jun 2007
        • 8

        #4
        Hello tlhintoq,
        Thank you very much for your asnwer. I used the samlpe to convert from Format24bppRgb to Format8bppIndex ed but yet the resulting image loses some color data. I feel the probmel is in MatchColors function, but I can not find it... I would be thankful if you could help me.

        Comment

        • tlhintoq
          Recognized Expert Specialist
          • Mar 2008
          • 3532

          #5
          I used the samlpe to convert from Format24bppRgb to Format8bppIndex ed but yet the resulting image loses some color data.
          Well... yeah. You went from 24 bits per pixel to 8. Of course it is going to loose some color data.

          At 8 bits you have a pallet of 256 colors.
          At 24 bits you have 16,777,216 colors.

          Comment

          • arashkk
            New Member
            • Jun 2007
            • 8

            #6
            Thank you very much! Actually I am going to write a program for color grading (or color gradient if I am not mistaken). I thought by converting the image to Format8bppIndex ed, I would be able to change the colors by setting palette value instead of changing colors pixel by pixel. That is why I am looking for a way to do this conversion. I need a fast method for changing the color, contrast and resolution of images. Do you think the way I chose is a good one?

            Comment

            • tlhintoq
              Recognized Expert Specialist
              • Mar 2008
              • 3532

              #7
              I'm sorry. I don't really understand what your goal is. "A program for color grading / gradient" ??

              You want to make a gradient? A smooth transition from Color A to Color B ?

              Comment

              • arashkk
                New Member
                • Jun 2007
                • 8

                #8
                I'm sorry that I could not explain. Yes, I mean smooth transition from one to color to another. For example to increase Red in an RGB image to make it more red. I did it by changing every pixel, but my teacher is not satisfied with it and says I have to do it by changing palettes as it takes shorter time.

                Comment

                • tlhintoq
                  Recognized Expert Specialist
                  • Mar 2008
                  • 3532

                  #9
                  Bytes has a policy regarding assisting students with their homework.

                  The short version is that the volunteers here can't help you with schoolwork.
                  A) We don't know what material you have and have not learned in class.
                  B) We don't know the guidelines you must follow.
                  C) In the long run giving you the answers actually short changes your education.

                  Comment

                  • arashkk
                    New Member
                    • Jun 2007
                    • 8

                    #10
                    It is not a school work. take it easy:) my research topic is much more complicated to be discussed here. This program is just a bit of work that I can ask everyone I want and what I have to do it something new that could not be found on the Internet:) So please help me to do this faster. I would appreciate it.

                    Comment

                    • tlhintoq
                      Recognized Expert Specialist
                      • Mar 2008
                      • 3532

                      #11
                      Originally posted by arashkk
                      It is not a school work.
                      Originally posted by arashkk
                      but my teacher is not satisfied with it and says I have to do it by changing palettes as it takes shorter time.
                      Sounds like schoolwork to me. Maybe someone else will interpret that differently.

                      Comment

                      • arashkk
                        New Member
                        • Jun 2007
                        • 8

                        #12
                        I am PhD student. My main work is with mathematic, not manipulating bitmaps.

                        Comment

                        • ThatThatGuy
                          Recognized Expert Contributor
                          • Jul 2009
                          • 453

                          #13
                          obviously .... convertting from a 24 bit bmp image to a 8 bit bitmap image...
                          will losea lot of colors....

                          notice the graphics of your windows XP machine.... when its newly formatted... and no graphic drivers are installed....

                          its 4 bit

                          Comment

                          • arashkk
                            New Member
                            • Jun 2007
                            • 8

                            #14
                            thank you for your answer. I understand the reason of losing data. I am looking for a fast way for changing colors but not pixel by pixel. As Format24bppRgb format doesn't provide palette information, I thought it is good to convert it to another format which does have palette information. I need to have access to palettes but without losing data.

                            Comment

                            Working...