LibTiff.NET Tiff to WPF Image - c#

I'm using LibTiff.NET to read a multipage Tiff file. It's no problem to convert my Tiff into a System.Drawing.Bitmap, since it's shown on their website, but what I want is a BitmapSource or something comparable to use in WPF.
Of course, I can convert the already converted System.Drawing.Bitmap, but since the amount of data is quite big, I am looking for a method to convert directly from the Tiff object.
Any suggestions? Maybe with the ReadRGBAImage method, that returns an int array with the colors?
Edit1:
I tried the following but only get an image consisting of gray stripes:
int[] raster = new int[height * width];
im.ReadRGBAImage(width, height, raster);
byte[] bytes = new byte[raster.Length * sizeof(int)];
Buffer.BlockCopy(raster, 0, bytes, 0, bytes.Length);
int stride = raster.Length / height;
image.Source = BitmapSource.Create(
width, height, dpiX/*ex 96*/, dpiY/*ex 96*/,
PixelFormats.Indexed1, BitmapPalettes.BlackAndWhite, bytes,
/*32/*bytes/pixel * width*/ stride);
Edit2:
Maybe this helps, it is for conversion to System.Drawing.Bitmap.

Ok I've downloaded the lib. The full solution is:
byte[] bytes = new byte[imageSize * sizeof(int)];
int bytesInRow = width * sizeof(int);
//Invert bottom and top
for (int row = 0; row < height; row++)
Buffer.BlockCopy(raster, row * bytesInRow, bytes, (height - row -1) * bytesInRow, bytesInRow);
//Invert R and B bytes
byte tmp;
for (int i = 0; i < bytes.Length; i += 4)
{
tmp = bytes[i];
bytes[i] = bytes[i + 2];
bytes[i + 2] = tmp;
}
int stride = width * 4;
Image = BitmapSource.Create(
width, height, 96, 96,
PixelFormats.Pbgra32, null, bytes, stride);
The solution is a bit more complex. In fact WPF don't support rgba32 format. So to display the image correctly R and B bytes should be swapped. Another tric is that tif image is loaded upside down. This needs some additional manipulation.
Hope this helps.

Related

How can I save an image as Bitmap Image File?

I made an Automatic image thresholding function, and wanna to save it as a bitmap file.
However, when I use the Bitmap.Save function of C# GDI+, although I set the ImageFormat as BMP, it always as the RGB color image file but not bitmap image file.
I must save it as the bitmap image file for the printer only can read the bitmap image file.
Maybe you will ask me what the bitmap image file is. I am not an expert of image processing and sorry about that can hardly explain clearly. But I can quote an example: in Photoshop, there are several color mode, such as RGB mode/CMYK mode/Index mode/Grayscale mode/Bitmap mode, I want to save the image as the Bitmap mode in C#.
Here is what Adobe explain about the Bitmap mode in their website:
Bitmap mode uses one of two color values (black or white) to represent the pixels in an image. Images in Bitmap mode are called bitmapped 1‑bit images because they have a bit depth of 1.
I googled but found nothing about this. How can I do it in C#? Thank you.
Here is my code:
Thread T = new Thread(() => {
Bitmap processedBitmap = new Bitmap(#"G:\\0001.jpg");
BitmapData bitmapData = processedBitmap.LockBits(new Rectangle(0, 0, processedBitmap.Width, processedBitmap.Height), ImageLockMode.ReadWrite, processedBitmap.PixelFormat);
int bytesPerPixel = Bitmap.GetPixelFormatSize(processedBitmap.PixelFormat) / 8;
int byteCount = bitmapData.Stride * processedBitmap.Height;
byte[] pixels = new byte[byteCount];
IntPtr ptrFirstPixel = bitmapData.Scan0;
Marshal.Copy(ptrFirstPixel, pixels, 0, pixels.Length);
int heightInPixels = bitmapData.Height;
int widthInBytes = bitmapData.Width * bytesPerPixel;
for (int y = 0; y < heightInPixels; y++)
{
int currentLine = y * bitmapData.Stride;
for (int x = 0; x < widthInBytes; x = x + bytesPerPixel)
{
int oldBlue = pixels[currentLine + x];
int oldGreen = pixels[currentLine + x + 1];
int oldRed = pixels[currentLine + x + 2];
double averageColor = (oldBlue + oldGreen + oldRed) / 3;
int NewC;
if (averageColor > 200)
{
NewC = 255;
}
else
{
NewC = 0;
}
// calculate new pixel value
pixels[currentLine + x] = (byte)NewC;
pixels[currentLine + x + 1] = (byte)NewC;
pixels[currentLine + x + 2] = (byte)NewC;
}
}
// copy modified bytes back
Marshal.Copy(pixels, 0, ptrFirstPixel, pixels.Length);
processedBitmap.UnlockBits(bitmapData);
processedBitmap.Save("G:\\aaa.bmp", ImageFormat.Bmp);
MessageBox.Show("Sucess!");
});
T.Start();
I believe the OP is referring to the last type of image in this adobe link
Bitmap is merely a container for data, the format of the data that you are storing is defined by the PixelFormat setting. As can be seen "Adobe" Bitmap mode is a 2 color format mode and corresponds to PixelFormat.Format1bppIndexed in C# Bitmap.
You have a couple of constructors for Bitmaps which have the PixelFormat as a parameter.
1.
public Bitmap (int width, int height, System.Drawing.Imaging.PixelFormat format);
2.
public Bitmap (int width, int height, int stride, System.Drawing.Imaging.PixelFormat format, IntPtr scan0);
With your source image you have a 24 bit image.
When you do your colour averaging, you're writing back to the image buffer with the following code:
pixels[currentLine + x] = (byte)NewC;
pixels[currentLine + x + 1] = (byte)NewC;
pixels[currentLine + x + 2] = (byte)NewC;
You're writing back 24 bits again.
So for example if your original values for RGB were (202, 203, 249), then NewC would be 218, and then you threshold it back to 255, so you write back (255,255,255) which is still an RGB value, it's just for white.
Then you save that image using
processedBitmap.Save("G:\\aaa.bmp", ImageFormat.Bmp);
The ImageFormat class just sets the type of image, like jpeg, png, etc.
And as you've discovered, you still have a 24 bit image being output.
So what you want is to save the image as a pure 1 bit per pixel black and white image.
To do this you need to specify the PixelFormat of the image you're saving, and specifically you want the PixelFormat Format1bppIndexed.
If you instead change the relevant bit of your code to:
...
Marshal.Copy(pixels, 0, ptrFirstPixel, pixels.Length);
processedBitmap.UnlockBits(bitmapData);
Bitmap clone = processedBitmap.Clone(new Rectangle(0, 0, processedBitmap.Width, processedBitmap.Height), PixelFormat.Format1bppIndexed);
clone.Save("G:\\aaa.bmp", ImageFormat.Bmp);
MessageBox.Show("Success!");
Now your output clone will be a 1bpp image.
However, you can simplify your code even more, because this clone function can actually do all the work for you, and you can reduce your code to just the following.
Bitmap processedBitmap = new Bitmap(#"G:\0001.jpg");
Bitmap clone = processedBitmap.Clone(new Rectangle(0, 0, processedBitmap.Width, processedBitmap.Height), PixelFormat.Format1bppIndexed);
clone.Save("G:\\aaa.bmp", ImageFormat.Bmp);
MessageBox.Show("Success!");
Be aware though that the output is slightly different.
Here are some test samples of the output.
This is my input image:
Output image with your thresholding code:
And output image using just the clone method:
To save a BMP object to a file all you have to do it this:
bmp.Save("c:\\Path\\To\\File\\image.bmp, ImageFormat.Bmp);
Are you doing anything else?

How to display raw data as an image (Visual Studio c#)

I will be receiving some raw data that will be stored in a byte array, where each 2 bytes is a pixel value (16 bits/px). To start with, the array will contain 100x100*2 bytes (enough for a 100x100 pixel image). I would like to display this data in the Form window. Eventually, I would like to refresh the image with the new data to make it look like a video stream. No strict frame rate is required. How can this be done? Any code examples in C#?
EDIT:
After some suggestions and reviews of tens of similar questions I still can not get this going. Here's the general idea of what I am trying to do, but the image is not displayed in the picture box on the form. What is specifically wrong with my implementation and how to fix it?
// array of data I collected
byte[] dataArray = new byte[100 * 100 * 2];
//create a pointer to the data
IntPtr hglobal = Marshal.AllocHGlobal(100 * 100 * 2);
// copy my array to global
Marshal.Copy(dataArray, 0, hglobal, dataArray.Length);
// create a bitmap: 100x100 pixels, 2bytes/pixel, 16bitgrayscale
Bitmap newBitmap = new Bitmap(100, 100, 2 * 100, PixelFormat.Format16bppGrayScale, hglobal);
// display bitmap
pictureBox1.Image = newBitmap;
// free the memory
Marshal.FreeHGlobal(hglobal);
The main problem is that PixelFormat.Format16bppGrayScale is not supported (at least on my Win 8.1 x64 system). So you have to convert image to rgb before displaying:
private void Form1_Load(object sender, EventArgs e)
{
//Create pixel data to put in image, use 2 since it is 16bpp
Random r = new Random();
int width = 100;
int height = 100;
byte[] pixelValues = new byte[width * height * 2];
for (int i = 0; i < pixelValues.Length; ++i)
{
// Just creating random pixel values for test
pixelValues[i] = (byte)r.Next(0, 256);
}
var rgbData = Convert16BitGrayScaleToRgb48(pixelValues, width, height);
var bmp = CreateBitmapFromBytes(rgbData, width, height);
// display bitmap
pictureBox1.Image = bmp;
}
private static byte[] Convert16BitGrayScaleToRgb48(byte[] inBuffer, int width, int height)
{
int inBytesPerPixel = 2;
int outBytesPerPixel = 6;
byte[] outBuffer = new byte[width * height * outBytesPerPixel];
int inStride = width * inBytesPerPixel;
int outStride = width * outBytesPerPixel;
// Step through the image by row
for (int y = 0; y < height; y++)
{
// Step through the image by column
for (int x = 0; x < width; x++)
{
// Get inbuffer index and outbuffer index
int inIndex = (y * inStride) + (x * inBytesPerPixel);
int outIndex = (y * outStride) + (x * outBytesPerPixel);
byte hibyte = inBuffer[inIndex + 1];
byte lobyte = inBuffer[inIndex];
//R
outBuffer[outIndex] = lobyte;
outBuffer[outIndex + 1] = hibyte;
//G
outBuffer[outIndex + 2] = lobyte;
outBuffer[outIndex + 3] = hibyte;
//B
outBuffer[outIndex + 4] = lobyte;
outBuffer[outIndex + 5] = hibyte;
}
}
return outBuffer;
}
private static Bitmap CreateBitmapFromBytes(byte[] pixelValues, int width, int height)
{
//Create an image that will hold the image data
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format48bppRgb);
//Get a reference to the images pixel data
Rectangle dimension = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData picData = bmp.LockBits(dimension, ImageLockMode.ReadWrite, bmp.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
//Copy the pixel data into the bitmap structure
System.Runtime.InteropServices.Marshal.Copy(pixelValues, 0, pixelStartAddress, pixelValues.Length);
bmp.UnlockBits(picData);
return bmp;
}
Idea was taken from this thread.
Use this Bitmap constructor:
public Bitmap(
int width,
int height,
int stride,
PixelFormat format,
IntPtr scan0
)
You pass it the shape of your bitmap, the stride (how many bytes per line, including padding), pixel format and the pixel data as a void * pointer. You can create the latter with Marshal.AllocHGlobal and fill it in as normal with pointer operations. Don't forget to free this memory after you create your bitmap.
Edit to account for updated question:
Simply call IntPtr.ToPointer() to get back a pointer. If you're familiar with C, the rest should be cake:
var p=(char *)hglobal.ToPointer(); // bad name by the way, it's not a handle, it's a pointer
p[0]=0; // access it like any normal pointer
However, you can use the Marshaller to copy memory for you from managed to unmanaged (getting your hands dirty is usually frowned upon in C#):
Marshal.Copy(dataArray, 0, hglobal, dataArray.Length); // again, terrible name
A Bitmap is an Image (as in, it derives from it), however you're using Graphics.DrawImage() wrong. As the error says, it's not a static method, you draw it to a specific graphic context. Now what that graphic context is, that's up to you:
If you want to paint it in response to WM_PAINT, use the Paint event -- it provides you with a special Graphics object set up with clipping and everything as instructed by the windowing system.
If you want to paint it on a bitmap to be later displayed somehow (the common use, also called double buffering), use Graphics.FromImage() on the source bitmap then draw your bitmap over it.
You can (and should) delete your virtual memory buffer as soon as you get the result back from the Bitmap constructor. Don't leak memory, use a try..finally construct.

C# BitmapSource - result is vertically flipped

I am implementing a software in C#/WPF that is recording images from a camera.
The result is a Bitmap that I am copying into a byte[].
pixelFormat = System.Windows.Media.PixelFormats.Bgr24;
int size = buffer.FrameType.BufferSize;
byte[] img = new byte[size];
Marshal.Copy(buffer.GetImageDataPtr(), img, 0, size);
I am creating a BitmapSource using this byte[] but the result I am getting is vertically flipped.
int stride = width * (pixelFormat.BitsPerPixel / 8);
image = BitmapSource.Create(width,
height,
96,
96,
pixelFormat,
BitmapPalettes.Gray256Transparent,
img,
stride);
The point that can be a problem according to me is the PixelFormat. The camera is using the system.drawing.imaging.pixelformat whereas I am using System.Windows.Media.pixelformat.
The camera is using RGB24 but the documentation says it "uses BGR order for the RGB24 pixel format. The organization of the pixels in the image buffer is from left to right and bottom up. "
What can be the problem ? Am I using the wrong Pixel Format ?
Peter Duniho and Clemens are right.
Here is the solution:
int size = buffer.FrameType.BufferSize;
int height = buffer.FrameType.Height;
byte[] img = new byte[size];
int lineSize = buffer.BytesPerLine;
//for each row of the image
for (int row = 0; row < height; row++)
{
//For each byte on a row
for (int col = 0; col < lineSize; col++)
{
int newIndex = (size - (lineSize * (row + 1))) + col;
img[newIndex] = buffer[col, row];
}
}

C# Creating PixelFormat.Format32bppArgb skewing image

I am trying to combine 3 grayscale bitmaps into one color bitmap. All three grayscale images are the same size (this is based off of data from the Hubble). My logic is:
Load "blue" image and convert to PixelFormat.Format24bppRgb. Based off of that create a new byte array that is 4 times as large as the blue data array length/3 (so it will be one byte for blue, one byte for green, one byte for red, one byte for alpha per pixel since my system is little endian). Populate the "blue" bytes of the array from the "blue" bytes of the blue image (and in this first loop set the alpha byte to 255). I then load the green and red bitmaps, convert them to PixelFormat.Format24bppRgb, and pull the g/r value and add it to the correct place in the data array. The final data array then has the bgra bytes set correctly from what I can tell.
When I have the data array populated, I have used it to:
Create a PixelFormats.Bgra32 BitmapSource then convert that to a Bitmap.
Create a PixelFormat.Format32bppArgb Bitmap using the Bitmap constructor (width, height, stride, PixelForma, IntPtr)
Create a PixelFormat.Format32bppArgb Bitmap using pointers
All three ways of creating a return bitmap result in the image being "skewed" (sorry, I don't know of a better word).
The actual output (of all three ways of generating the final bitmap) is: Actual output
The desired output is something like (this was done in photoshop so it is slightly different): Desired output
The three file names (_blueFileName, _greenFileName, _redFileName) are set in the constructor and I check to make sure the files exist before creating the class. I can post that code if anyone wants it.
Can anyone tell me what I am doing wrong? I am guessing that is is due to the stride or something like that?
Note: I can't post the links to the images I am using as input as I don't have 10 reputation points. Maybe I could send the links via email or something if someone wants them as well.
Here is my code (with some stuff commented out, the comments describe what happens if each commented out block is used instead):
public Bitmap Merge()
{
// Load original "blue" bitmap.
Bitmap tblueBitmap = (Bitmap)Image.FromFile(_blueFileName);
int width = tblueBitmap.Width;
int height = tblueBitmap.Height;
// Convert to 24 bpp rgb (which is bgr on little endian machines)
Bitmap blueBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(blueBitmap))
{
gr.DrawImage(tblueBitmap, 0, 0, width, height);
}
tblueBitmap.Dispose();
// Lock and copy to byte array.
BitmapData blueData = blueBitmap.LockBits(new Rectangle(0, 0, blueBitmap.Width, blueBitmap.Height), ImageLockMode.ReadOnly,
blueBitmap.PixelFormat);
int numbBytes = blueData.Stride*blueBitmap.Height;
byte[] blueBytes = new byte[numbBytes];
Marshal.Copy(blueData.Scan0, blueBytes, 0, numbBytes);
blueBitmap.UnlockBits(blueData);
blueData = null;
blueBitmap.Dispose();
int mult = 4;
byte[] data = new byte[(numbBytes/3)*mult];
int count = 0;
// Copy every third byte starting at 0 to the final data array (data).
for (int i = 0; i < data.Length / mult; i++)
{
// Check for overflow
if (blueBytes.Length <= count*3 + 2)
{
continue;
}
// First pass, set Alpha channel.
data[i * mult + 3] = 255;
// Set blue byte.
data[i*mult] = blueBytes[count*3];
count++;
}
// Cleanup.
blueBytes = null;
int generation = GC.GetGeneration(this);
GC.Collect(generation);
Bitmap tgreenBitmap = (Bitmap)Image.FromFile(_greenFileName);
Bitmap greenBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(greenBitmap))
{
gr.DrawImage(tgreenBitmap, 0, 0, width, height);
}
tgreenBitmap.Dispose();
BitmapData greenData = greenBitmap.LockBits(new Rectangle(0, 0, greenBitmap.Width, greenBitmap.Height), ImageLockMode.ReadOnly,
greenBitmap.PixelFormat);
numbBytes = greenData.Stride * greenBitmap.Height;
byte[] greenBytes = new byte[numbBytes];
Marshal.Copy(greenData.Scan0, greenBytes, 0, numbBytes);
greenBitmap.UnlockBits(greenData);
greenData = null;
greenBitmap.Dispose();
count = 0;
for (int i = 0; i < data.Length / mult; i++)
{
if (greenBytes.Length <= count * 3 + 1)
{
continue;
}
// Set green byte
data[i * mult + 1] = greenBytes[count * 3 + 1];
count++;
}
greenBytes = null;
generation = GC.GetGeneration(this);
GC.Collect(generation);
Bitmap tredBitmap = (Bitmap)Image.FromFile(_redFileName);
Bitmap redBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(redBitmap))
{
gr.DrawImage(tredBitmap, 0, 0, width, height);
}
tredBitmap.Dispose();
BitmapData redData = redBitmap.LockBits(new Rectangle(0, 0, redBitmap.Width, redBitmap.Height), ImageLockMode.ReadOnly,
redBitmap.PixelFormat);
numbBytes = redData.Stride * redBitmap.Height;
byte[] redBytes = new byte[numbBytes];
Marshal.Copy(redData.Scan0, redBytes, 0, numbBytes);
redBitmap.UnlockBits(redData);
redData = null;
redBitmap.Dispose();
count = 0;
for (int i = 0; i < data.Length / mult; i++)
{
if (redBytes.Length <= count * 3+2)
{
count++;
continue;
}
// set red byte
data[i * mult + 2] = redBytes[count * 3 + 2];
count++;
}
redBytes = null;
generation = GC.GetGeneration(this);
GC.Collect(generation);
int stride = (width*32 + 7)/8;
var bi = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgra32, null, data, stride);
// uncomment out below to see what a bitmap source to bitmap does. So far, it is exactly the same as
// the uncommented out lines below.
// ---------------------------------------------------------------------------------------------------
//return BitmapImage2Bitmap(bi);
unsafe
{
fixed (byte* p = data)
{
IntPtr ptr = (IntPtr)p;
// Trying the commented out lines returns the same bitmap as the uncommented out lines.
// ------------------------------------------------------------------------------------
byte* p2 = (byte*)ptr;
Bitmap retBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
BitmapData fData = retBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb);
unsafe
{
for (int i = 0; i < fData.Height; i++)
{
byte* imgPtr = (byte*)(fData.Scan0 + (fData.Stride * i));
for (int x = 0; x < fData.Width; x++)
{
for (int ii = 0; ii < 4; ii++)
{
*imgPtr++ = *p2++;
}
//*imgPtr++ = 255;
}
}
}
retBitmap.UnlockBits(fData);
//Bitmap retBitmap = new Bitmap(width, height, GetStride(width, PixelFormat.Format32bppArgb),
// PixelFormat.Format32bppArgb, ptr);
return retBitmap;
}
}
}
private Bitmap BitmapImage2Bitmap(BitmapSource bitmapSrc)
{
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapSrc));
enc.Save(outStream);
Bitmap bitmap = new Bitmap(outStream);
return new Bitmap(bitmap);
}
}
private int GetStride(int width, PixelFormat pxFormat)
{
int bitsPerPixel = ((int)pxFormat >> 8) & 0xFF;
int validBitsPerLine = width * bitsPerPixel;
int stride = ((validBitsPerLine + 31) / 32) * 4;
return stride;
}
You are missing the gap between the lines. The Stride value is not the amount of data in a line, it's the distance between the start of one line to the next. There may be a gap at the end of each line to align the next line on an even address boundary.
The Stride value can even be negative, then the image is stored upside down in memory. To get the data without the gaps and to handle all cases you need to copy one line at a time:
BitmapData blueData = blueBitmap.LockBits(new Rectangle(0, 0, blueBitmap.Width, blueBitmap.Height), ImageLockMode.ReadOnly, blueBitmap.PixelFormat);
int lineBytes = blueBitmap.Width * 3;
int numbBytes = lineBytes * blueBitmap.Height;
byte[] blueBytes = new byte[numbBytes];
for (int y = 0; y < blueBitmap.Height; y++) {
Marshal.Copy(blueData.Scan0 + y * blueData.Stride, blueBytes, y * lineBytes, lineBytes);
}
blueBitmap.UnlockBits(blueData);
blueBitmap.Dispose();

Creating 16-bit+ grayscale images in WPF

I want to create a 16-bit grayscale image from data values in my WPF program. Currently I have been looking at using a WriteableBitmap with PixelFormats.Gray16 set.
However I can't get this to work, and a Microsoft page (http://msdn.microsoft.com/en-us/magazine/cc534995.aspx) lists the Gray16 format as not writeable via the WriteableBitmap, but does not suggest how else to make one in this way.
Currently my code operates within a loop, where i represents the image height and j the width, and looks something like this:
short dataValue = GetDataSamplePixelValue(myDataValue);
//the pixel to fill with this data value:
int pixelOffset = ((i * imageWidth) + j) * bytesPerPixel;
//set the pixel colour values:
pixels[pixelOffset] = dataValue;
I do get an image with this but it is just a bunch of vertical black and white lines. I don't have a problem if using just 8-bit grayscale data (in which case in the above example short is changed to byte).
Does anyone know how to create a 16-bit per pixel or higher grayscale image using WPF? This image will ultimately need to be saved as well.
Any advice is much appreciated.
EDIT
Further to this I have done some editing and am now getting a sensible image using the Gray16 PixelFormat. It's very difficult for me to tell if it is actually 16-bit though, as a colour count by an image program gives 256, and I am not sure if this is because the image is being constrained by WPF, or perhaps the image program does not support it as apparently many image programs ignore the lower 8-bits. For now I will stick with what I have.
For information the code is like this:
myBitmap = new WriteableBitmap((int)visualRect.Width, (int)visualRect.Height, 96, 96, PixelFormats.Gray16, null);
int bytesPerPixel = myBitmap.Format.BitsPerPixel / 8;
ushort[] pixels = new ushort[(int)myBitmap.PixelWidth * (int)myBitmap.PixelHeight];
//if there is a shift factor, set the background colour to white:
if (shiftFactor > 0)
{
for (int i = 0; i < pixels.Length; i++)
{
pixels[i] = 255;
}
}
//the area to be drawn to:
Int32Rect drawRegionRect = new Int32Rect(0, 0, (int)myBitmap.PixelWidth, (int)myBitmap.PixelHeight);
//the number of samples available at this line (reduced by one so that the picked sample can't lie beyond the array):
double availableSamples = myDataFile.DataSamples.Length - 1;
for (int i = 0; i < numDataLinesOnDisplay; i++)
{
//the current line to use:
int currentLine = ((numDataLinesOnDisplay - 1) - i) + startLine < 0 ? 0 : ((numDataLinesOnDisplay- 1) - i) + startLine;
for (int j = 0; j < myBitmap.PixelWidth; j++)
{
//data sample to use:
int sampleToUse = (int)(Math.Floor((availableSamples / myBitmap.PixelWidth) * j));
//get the data value:
ushort dataValue = GetDataSamplePixelValue(sampleToUse);
//the pixel to fill with this data value:
int pixelOffset = (((i + shiftFactor) * (int)myBitmap.PixelWidth) + j);
//set the pixel colour values:
pixels[pixelOffset] = dataValue;
}
}
//copy the byte array into the image:
int stride = myBitmap.PixelWidth * bytesPerPixel;
myBitmap.WritePixels(drawRegionRect, pixels, stride, 0);
In this example startLine and shiftFactor are already set, and depend on from which point in the data file the user is viewing, with shiftFactor only non-zero in the cases of a data file smaller than the screen, in which case I am centering the image vertically using this value.
find bug in your code or display your full code
next example with gray16 image work normal
var width = 300;
var height = 300;
var bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Gray16, null);
var pixels = new ushort[width * height];
for (var y = 0; y < height; ++y)
for (var x = 0; x < width; ++x)
{
var v = (0x10000*2 * x/width + 0x10000 * 3 * y / height);
var isMirror = (v / 0x10000) % 2 == 1;
v = v % 0xFFFF;
if (isMirror)
v = 0xFFFF - v;
pixels[y * width + x] = (ushort)v;
}
bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, width *2, 0);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (var stream = System.IO.File.Create("gray16.png"))
encoder.Save(stream);
For reference, it is unlikely that a screen can display a 16-bit grayscale image, and also, this format is not well supported by Windows. For example, Windows XP cannot even display a 16-bit grayscale image in Photo viewer, though Windows 7+ can (I'm not sure about Vista, I don't have it).
On top of that, the .NET open TIF method will not load a 16-bit grayscale image.
The solution to loading and saving of 16-bit grayscale image, and I would recommend for TIFs in general is LibTIFF. You then have the option of loading the whole TIF, or loading it line by line, among other methods. I recommend loading it line by line, as then you can keep just the data that will be visible on screen, as some TIFs these days get very large, and cannot be held by a single array.
So ultimately, do not worry about displaying 16-bit grayscale on screen, it may be limited by the capabilities of the system / monitor, and the human eye cannot tell the difference between this and 8-bit anyway. If however you need to load or save 16-bit, use LibTIFF.

Categories

Resources