-1

I am trying to make threshold process on an image. The image of type "tif" and i am getting this error says

SetPixel is not supported for images with indexed pixel formats

This is my code and p11 is the image name

for (int r = 0; r < p11.Width; r++)
{
    //  whiteColor = 0;
    //  blackColor = 0;
    for (int c = 0; c < p11.Height; c++)
    {
        num1 = int.Parse(p11.GetPixel(r, c).A.ToString()); // gets the alpha component value of this colout
        num2 = int.Parse(p11.GetPixel(r, c).B.ToString()); // gets the Blue component value of this colout
        num3 = int.Parse(p11.GetPixel(r, c).R.ToString()); // gets the Red component value of this colout
        num4 = int.Parse(p11.GetPixel(r, c).G.ToString()); // gets the green component value of this colout

        if( T <= num1 && T <= num2 && T <= num3 && T <= num4)
        {

        }
        else
        {
            p11.SetPixel(r, c, Color.Black);
        }
    }
}
GSerg
  • 76,472
  • 17
  • 159
  • 346
  • 1
    `p11` cannot be an “image name” because you cannot call `SetPixel` on names. All we know is that `p11` has an indexed pixel format, so it contains indexes into a color table, not RGB colors. That is why you cannot read nor set a pixel to a color. You should convert your TIFF file to an RGB format so you can read and set colors on it. – Dour High Arch Dec 30 '18 at 18:02
  • 3
    The error really tells you all you need to know. The moment you saw that you should have consulted the SetPixel documentation, which says: "This function works only on RGBA32, ARGB32, RGB24 and Alpha8 texture formats. For other formats SetPixel is ignored." So, get the pixel format of your Texture right, and also respect the other requirements mentioned there, then it'll work just fine. – Thomas Hilbert Dec 30 '18 at 18:06
  • 3
    Not your problem but... `GetPixel`/`SetPixel` are notoriously slow. What's even worse is calling `GetPixel` repeatedly for the same pixel instead of caching it into a variable. What's worse than that, is extracting an integer from the ARGB components by calling `ToString` and then using `int.Parse` on the results. Those components are `byte`s already...just cast them directly to `int` or rely on the implicit conversion: `var color = p11.GetPixel(r,c); num1=color.A;` – pinkfloydx33 Dec 30 '18 at 18:10

1 Answers1

2

Indexed pixel format is when the image data contains no direct colors but entries of a palette, which indirectly references colors. 8 and less bit-per-pixel images are typically indexed ones. To access the list of actual colors see the Palette property of the Bitmap.

SetPixel cannot be used for these images as it expects a color as parameter. To manipulate the image content you need to obtain the BitmapData by the LockBits method. In the example below the TransformColor gets a palette index and returns another one.

private unsafe static void ManipulateIndexedBitmap(Bitmap bitmap)
{
    int bpp = Image.GetPixelFormatSize(bitmap.PixelFormat);

    BitmapData bitmapData = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadWrite, bitmap.PixelFormat);
    try
    {
        byte* line = (byte*)bitmapData.Scan0;

        // scanning through the lines
        for (int y = 0; y < bitmapData.Height; y++)
        {
            // scanning through the pixels within the line
            for (int x = 0; x < bitmapData.Width; x++)
            {
                switch (bpp)
                {
                    // a pixel is 1 byte - there are up to 256 palette entries 
                    case 8:
                        line[x] = (byte)TransformColor(line[x]);
                        break;
                    // a pixel is 4 bits - there are up to 16 palette entries
                    case 4:
                        // First pixel is the high nibble
                        int pos = x >> 1;
                        byte nibbles = line[pos];
                        if ((x & 1) == 0)
                        {
                            nibbles &= 0x0F;
                            nibbles |= (byte)(TransformColor(nibbles) << 4);
                        }
                        else
                        {
                            nibbles &= 0xF0;
                            nibbles |= (byte)TransformColor(nibbles >> 4);
                        }

                        line[pos] = nibbles;
                        break;
                    // a pixel is 1 bit - there are exactly two palette entries
                    case 1:
                        // First pixel is MSB.
                        pos = x >> 3;
                        byte mask = (byte)(128 >> (x & 7));
                        if (TransformColor(((line[pos] & mask) == 0) ? 0 : 1) == 0)
                            line[pos] &= (byte)~mask;
                        else
                            line[pos] |= mask;
                        break;
                }
            }

            line += bitmapData.Stride;
        }
    }
    finally
    {
        bitmap.UnlockBits(bitmapData);
    }
}
György Kőszeg
  • 17,093
  • 6
  • 37
  • 65
  • I try to integrate this code, but I have no clue about the `TransformColor` function. Is this an existing function? – Dabblernl Oct 08 '19 at 22:03
  • As I mentioned `TransformColor` gets and returns a palette index. It is upon you how you implement it. For example, if you want to replace the background color, which is represented by index 0 (`bitmap.Palette.Entries[0]`), then you should return the input value except for 0. Or, if you have a contiguous palette and you want to [cycle the colors](https://en.wikipedia.org/wiki/Color_cycling#/media/File:-PLASMA-ColorCycling.Gif), then you can return `index + 1`. – György Kőszeg Oct 09 '19 at 07:37