0

Good day to everyone. I need to read images pixel data using bitmap. Than make something with this data and write back to image. Doing this using GetPixels & SetPixels functions

string ImageFilePath = "image.jpg";
Bitmap image = Bitmap(ImageFilePath);
.....
for (int i = 0; i < image.Height; i++)
        {
            for (int j = 0; j < image.Width; j++)
            {
                Color pixelColor = image.GetPixel(j, i);
                RedColor  [i, j] = pixelColor.R;
                GreenColor[i, j] = pixelColor.G;
                BlueColor [i, j] = pixelColor.B;
            }
        }
.......


for (int y = 0; y < image.Height; y++)
        {
            for (int x = 0; x < image.Width; x++)
            {
               ...............
               image.SetPixel(x, y, Color.FromArgb(red, green, blue));
            }
         }    

With 24bbp images it's work fine. But with Format8bppIndexed have some troubles, using method below (GetPixels) obtain wrong pixels values. Not to mention the record back to the file after some counting. Can some one give code example how can I read\write 8bpp images using bitmap correctly (by obtaining pixel data)? Searching some examples before, but my "
crooked hands" didn`t allow to use what I have found.

K. Alex
  • 33
  • 1
  • 1
  • 5
  • What do you need to do with the pixels, though? Manipulating 8-bit data works quite differently from manipulating colour data, and `SetPixel()` plain doesn't work in 8-bit mode, but for manipulating colours alone, there's no need to do any of that. – Nyerguds Jan 22 '18 at 09:58
  • Various plans. For example: obtain pixel data of 8bit black & white image. Based on it's values count look up table, and obtain new pixel data values to change contrast. Or convolution with some filter to sharp\smooth image. Already know, that `SetPixel()` is not suitable for this. Trying with using `LockBits` as in comments under first answer. But still obtain strange pixel data. If I save back to file - there appear some 'artifacts'. I will be grateful for another variant to obtain pixel data on C#, may be with out using `Bitmap`. – K. Alex Jan 22 '18 at 10:13
  • You can't just set pixels to a colour in 8-bit mode. The byte values you get are _references_ to the colours on the palette. You are, in _all_ your actions, limited to _only_ these 256 colours on the image's colour palette. So yes, you can change them... but only to a different colour reference from 0 to 255. – Nyerguds Jan 22 '18 at 10:17
  • I know, and using `palette`, and I am not trying to set pixels to a color in 8-bit mode. I have 0..255, recount values (look up table works seems 'palette') and write back. The problem, that I can't obtain 'correct' pixel data for 8-bit image using `Bitmap` functionality. For example: read 8-bit image in Matlab, and using this library (no matter image from stream or from file) - obtain different pixel data values. And if i write back them to file, image from `Bitmap` have some 'artifacts' (look like added some noise). May be I didn't understand hove some thing works. – K. Alex Jan 22 '18 at 10:40
  • Does it have to remain in 8-bit? If not, just paint the 8-bit image on a new 32bpp bitmap object. – Nyerguds Jan 22 '18 at 15:35
  • Not necessary. But already tried. Interesting thing. All time, I have used non windows default programs to view images. 8-bit images where .jpg format. Today opened in paint and windows photo viewer. And WTF - I could see the same 'artifacts' on image! Using ACDSee program converted into .png (not by programs indicated above, because obtain 'artifacts' too). Create `Bitmap` object - Miracle! Pixel data are adequate. No 'artifacts' before and after some changes and saving back! Seems there some problems in windows to view 8-bit .jpg format images, and `Bitmap` library have the same. – K. Alex Jan 22 '18 at 16:18
  • Um. jpeg always gives compression artifacts... it is a lossy compression format. And the .Net framework doesn't support grayscale jpeg as separate type; it just converts it to 24bpp on import. – Nyerguds Jan 22 '18 at 17:33
  • I know, that jpeg gives compression artifacts, but didn't knew about .Net framework and grayscale jpeg feature. Even, I didn't expect such image behavior, when started work with `Bitmap`. May be here my fault and vacancy in knowledge. Thanks for your time. – K. Alex Jan 22 '18 at 17:45
  • Grayscale jpeg isn't actually 8-bit in the traditional sense; it's not a paletted format; it's just monochrome. And, re-saving a jpeg image as jpeg will always decrease the quality further. – Nyerguds Jan 22 '18 at 17:52

1 Answers1

1

In case of 8 bpp (and less) images the bitmap data contains palette entries instead of direct color information. For such images the Bitmap.Palette property is non-empty and it contains the colors the image can use.

8bpp means 256 possible different colors, and one pixel is exactly one byte. But a 0 value here is not a color but the 0th entry in the palette.

To convert a high-color image to an indexed one see my answer here: Saving Transparent PNG as Transparent GIF


Update

This is quite an old answer. Since then I made my drawing libraries public, which supports setting pixels even for an indexed bitmap. See some examples with images here.

György Kőszeg
  • 17,093
  • 6
  • 37
  • 65
  • Thanks for you answer. Convert a high-color image to an indexed one may be useful in future. According your words, i can obtain pixel data information from Bitmap.Palette or misunderstood something? Bth, it`s a point to look more about bitmap functionality. – K. Alex Jan 15 '18 at 21:14
  • Pixel data information is still in `BitmapData` (you can obtain it by `LockBits`) but it contains palette entries instead of colors. That's why `SetPixel` does not work for indexed images. You can set a new palette with a replaced color instead but that will recolor every pixel that refers the replaced color. – György Kőszeg Jan 16 '18 at 09:07
  • Thanks for the explanation. Now I understand why i received wrong data by using `SetPixel`. I'll pay attention on `Bitmap` functionality such as `LockBits` and `Palette`. This was a mistake thinking, that all so easy without knowledge about Indexed format images. – K. Alex Jan 16 '18 at 09:24
  • 1
    Trying with `LockBits` to obtain pixel data using following code: `var data = b.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);` `byte* bmpPtr = (byte*)data.Scan0;` `var d = (bmpPtr[x + y * data.Stride]);` `b.UnlockBits(data);` But obtain result such as using `GetPixels`. If it's no bother, can you say what i am doing wrong or show little example? I am still trying to obtain correct pixel data values from 8bpp image. – K. Alex Jan 16 '18 at 16:40
  • Both solutions are correct, the only difference that `GetPixel` is very slow because it does the locking for each pixel and translates any pixel format into colors. In your original code the problem is at `SetPixel` because that does not work for indexed images. To set a pixel of an indexed image (and remember: pixel is a color index from a palette) you must set `BitmapData` manually. – György Kőszeg Jan 17 '18 at 15:27
  • If all you're doing is manipulating colours, though, you can simply do that on the palette. Changing a colour entry on the palette affects all pixels on the image using that entry. – Nyerguds Jan 22 '18 at 10:00