Image Class Problem
-
I have a problem with the Image Class - or in particular the way it stores data. I need to process some images (JPG and TIFFs mainly), but have come into a problem with TIFF images. The images are stored on disc as 16-bit TIFF images - so pixel values are in the range 0-65535. Now, when I used Image.FromFile() - it reads OK and says the pixelformat is 48bpp. BUT, the data (R,G and B values) still appear to be stored as bytes. So a pixel value of (1,1,1) in the image is actually (0,0,0) in memory. I am guessing it normalises the image as r = (r / 65535) * 255; g = (b / 65535) * 255; b = (b / 65535) * 255; I do not want this and need the actual 16 bit unsigned integer values - the values close to zero (which are now truncated to zero) are VERY important. Does anyone know of a way to get this data? Thanks Barry
-
I have a problem with the Image Class - or in particular the way it stores data. I need to process some images (JPG and TIFFs mainly), but have come into a problem with TIFF images. The images are stored on disc as 16-bit TIFF images - so pixel values are in the range 0-65535. Now, when I used Image.FromFile() - it reads OK and says the pixelformat is 48bpp. BUT, the data (R,G and B values) still appear to be stored as bytes. So a pixel value of (1,1,1) in the image is actually (0,0,0) in memory. I am guessing it normalises the image as r = (r / 65535) * 255; g = (b / 65535) * 255; b = (b / 65535) * 255; I do not want this and need the actual 16 bit unsigned integer values - the values close to zero (which are now truncated to zero) are VERY important. Does anyone know of a way to get this data? Thanks Barry
You should be able to do it by looking at the raw data bytes. This is more complicated than GetPixel(), but the System.Color structure definitely only supports 8bpp. The upside is that this method can be a lot faster than calling GetPixel thousands of times. To get at the raw data for a System.Drawing.Bitmap you first have to lock the bits (so the array doesn't move around in memory while you're doing unsafe pointer stuff), then you can get a pointer to the start of a particular scan line, which will be a pointer to the raw array of bytes for that scanline. All of this requires the "unsafe" keyword, which requires a special compiler flag. Inside the unsafe { ... } block, first call LockBits() on the Bitmap to get a BitmapData structure. Then you can use the Scan0 property of the BitmapData to get a pointer to the first scanline. In your case you would cast this to a (ushort*) type, since each 16bit value is a ushort. Incrementing the pointer will point to subsequent ushort's on the same scanline. To get to the next row (scan line) of the image, add the value of the Stride property of the BitmapData to the Scan0 value to get the pointer to the start of the second scan line. I don't have a 48bpp image to try this on, but here's some code for a 32bpp image which you can use as a starting point:
using (Bitmap bmp = new Bitmap("image.tif"))
{
int[,] raw = new int[bmp.Width, bmp.Height];
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
unsafe
{
BitmapData data = bmp.LockBits(rect,
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
int max = int.MinValue;
byte *p = (byte *) data.Scan0;
for (int y = 0; y < bmp.Height; y++)
{
byte *q = p;
for (int x = 0; x < bmp.Width; x++)
{
byte red = *(q++);
byte grn = *(q++);
byte blu = *(q++);
raw[x, y] = blu + 256 * grn + 65536 * red;
}
p += data.Stride;
}
bmp.UnlockBits(data);
}
}In your case you'll want ushort instead of byte everywhere, and you wouldn't be saving the pixels as 32-bit ints of course, but the logic is the same. It's also possible that I have the red/blu values backwards here, but you can figure that out.