Load 32-bit greyscale TIFF image in C# - c#

I'm developing a small C# tool that must be able to load a TIFF image, crop the image to a certain size, and save it as a PNG file.
I have large greyscale TIFF images of about 28000x256 pixels with 32-bit bit depth. When I try to process the images with my tool, it just outputs a blank white image.
Also, when I try to open the original TIFF images (not the ones processed with my tool) with the Windows Photo Viewer, it also shows a blank white image. Some other applications, e.g. ImageJ, display the image correctly. What is the problem here?
My code to load the images looks as follows:
Image image = Bitmap.FromFile(path.LocalPath);
int width = image.Width;
int height = image.Height;
Bitmap bmp = new Bitmap(width, height);
Graphics g = Graphics.FromImage(bmp);

The problem is that C# (or better said the underlying API) can't handle Greyscale images with a Colordepth greater than 8bit.
I'd suggest using LibTiff.NET for Handling TIFF images.
When i faced such an problem, i loaded the TIFF image raw Data into an array
using (var inputImage = Tiff.Open(image, "r"))
{
width = inputImage.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
height = inputImage.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
inputImageData = new byte[width * height * bytePerPixel];
var offset = 0;
for (int i = 0; i < inputImage.NumberOfStrips(); i++)
{
offset += inputImage.ReadRawStrip(i, inputImageData, offset, (int)inputImage.RawStripSize(i));
}
}
The bytes then have to be converted into an array of uint (in my case, imagedata was only 16 bit, so i used ushort) Remember to take care of Endianness of the data!
// has to be done by hand to ensure endiannes is kept correctly.
var outputImageData = new ushort[inputImageData.Length / 2];
for (var i = 0; i < outputImageData.Length; i++)
{
outputImageData[i] = (ushort)((inputImageData[i * 2 + 1]) + (ushort)(inputImageData[i * 2] << 8));
}
You can then manipulate the image using normal Array Operations. I'd suggest you to use normal Array operations and not Lambda-Expressions, as they are much faster. (in My Scenario 100s vs 2s Runtime)
Finally you can save the image using LibTiff again
using (var output = Tiff.Open(imageout, "w"))
{
output.SetField(TiffTag.IMAGEWIDTH, width);
output.SetField(TiffTag.IMAGELENGTH, height);
output.SetField(TiffTag.SAMPLESPERPIXEL, 1);
output.SetField(TiffTag.BITSPERSAMPLE, 16);
output.SetField(TiffTag.ROWSPERSTRIP, height);
output.SetField(TiffTag.PHOTOMETRIC, Photometric.MINISBLACK);
output.SetField(TiffTag.FILLORDER, FillOrder.MSB2LSB);
// Transform to Byte-Array
var buffer = new byte[outputImageData.Length * sizeof(ushort)];
Buffer.BlockCopy(outputImageData, 0, buffer, 0, buffer.Length);
// Write it to Image
output.WriteRawStrip(0, buffer, buffer.Length);
}

Related

Concatenate a bitmap (rgb) with a TIFF (cmyk) without converting cmyk to rgb

I'm developing an application to concatenate a bitmap image in RGB with a TIFF in CMYK.
I've tried with System.Drawing and System.Windows.Media namespaces.
The problem is both the libraries try to convert my TIFF image into RGB before merging, which causes a loss in image quality.
As far as I understand, the reason they always convert images into RGB before processing because the two libraries do that with a rendering intent.
I don't need to render anything, just merge the two photos and save to disk, that's all.
What should I do to achieve my goal? Clearly, I don't want to lose the quality of the TIFF so I think it's best to not do any conversion, just keep it raw and merge. Anyway, that's just a guess, other option could be considered as well. Could anybody shed some light on my case please?
See a comparison of the tiff image before and after converted from cmyk to rgb below.
I’m not aware of any capacity in the TIFF format to have two different color spaces at the same time. Since you are dealing in CMYK, I assume that is the one you want to preserve.
If so, the steps to do so would be:
Load CMYK image A (using BitmapDecoder)
Load RGB image B (using BitmapDecoder)
Convert image B to CMYK with the desired color profile (using FormatConvertedBitmap)
If required, ensure the pixel format for image B matches A (using FormatConvertedBitmap)
Composite the two in memory as a byte array (using CopyPixels, then memory manipulation, then new bitmap from the memory)
Save the composite to a new CMYK TIFF file (using TiffBitmapEncoder)
That should be possible with WIC (System.Media).
An example doing so (github) could be written as:
BitmapFrame LoadTiff(string filename)
{
using (var rs = File.OpenRead(filename))
{
return BitmapDecoder.Create(rs, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad).Frames[0];
}
}
// Load, validate A
var imageA = LoadTiff("CMYK.tif");
if (imageA.Format != PixelFormats.Cmyk32)
{
throw new InvalidOperationException("imageA is not CMYK");
}
// Load, validate, convert B
var imageB = LoadTiff("RGB.tif");
if (imageB.PixelHeight != imageA.PixelHeight)
{
throw new InvalidOperationException("Image B is not the same height as image A");
}
var imageBCmyk = new FormatConvertedBitmap(imageB, imageA.Format, null, 0d);
// Merge
int width = imageA.PixelWidth + imageB.PixelWidth,
height = imageA.PixelHeight,
bytesPerPixel = imageA.Format.BitsPerPixel / 8,
stride = width * bytesPerPixel;
var buffer = new byte[stride * height];
imageA.CopyPixels(buffer, stride, 0);
imageBCmyk.CopyPixels(buffer, stride, imageA.PixelWidth * bytesPerPixel);
var result = BitmapSource.Create(width, height, imageA.DpiX, imageA.DpiY, imageA.Format, null, buffer, stride);
// save to new file
using (var ws = File.Create("out.tif"))
{
var tiffEncoder = new TiffBitmapEncoder();
tiffEncoder.Frames.Add(BitmapFrame.Create(result));
tiffEncoder.Save(ws);
}
Which maintains color accuracy of the CMYK image, and converts the RGB using the system color profile. This can be verified in Photoshop which shows that the each letter, and rich black, have maintained their original values. (note that imgur does convert to png with dubious color handling - check github for originals.)
Image A (CMYK):
Image B (RGB):
Result (CMYK):
To have the two images overlayed, one image would have to have some notion of transparency. A mask would be one example thereof, where you pick a particular color value to mean "transparent". The downside of a mask is that masks do not play well with aliased source images. For that, you would want to do an alpha channel - but blending across color spaces would be challenging. (Github)
// Load, validate A
var imageA = LoadTiff("CMYK.tif");
if (imageA.Format != PixelFormats.Cmyk32)
{
throw new InvalidOperationException("imageA is not CMYK");
}
// Load, validate, convert B
var imageB = LoadTiff("RGBOverlay.tif");
if (imageB.PixelHeight != imageA.PixelHeight
|| imageB.PixelWidth != imageA.PixelWidth)
{
throw new InvalidOperationException("Image B is not the same size as image A");
}
var imageBBGRA = new FormatConvertedBitmap(imageB, PixelFormats.Bgra32, null, 0d);
var imageBCmyk = new FormatConvertedBitmap(imageB, imageA.Format, null, 0d);
// Merge
int width = imageA.PixelWidth, height = imageA.PixelHeight;
var stride = width * (imageA.Format.BitsPerPixel / 8);
var bufferA = new uint[width * height];
var bufferB = new uint[width * height];
var maskBuffer = new uint[width * height];
imageA.CopyPixels(bufferA, stride, 0);
imageBBGRA.CopyPixels(maskBuffer, stride, 0);
imageBCmyk.CopyPixels(bufferB, stride, 0);
for (int i = 0; i < bufferA.Length; i++)
{
// set pixel in bufferA to the value from bufferB if mask is not white
if (maskBuffer[i] != 0xffffffff)
{
bufferA[i] = bufferB[i];
}
}
var result = BitmapSource.Create(width, height, imageA.DpiX, imageA.DpiY, imageA.Format, null, bufferA, stride);
// save to new file
using (var ws = File.Create("out_overlay.tif"))
{
var tiffEncoder = new TiffBitmapEncoder();
tiffEncoder.Frames.Add(BitmapFrame.Create(result));
tiffEncoder.Save(ws);
}
Example image B:
Example output:

Image from byte array creating bad image when using Marshalling [duplicate]

I searched all question about byte array but i always failed. I have never coded c# i am new in this side. Could you help me how to make image file from byte array.
Here is my function which stores byte in array named imageData
public void imageReady( byte[] imageData, int fWidth, int fHeight))
You'll need to get those bytes into a MemoryStream:
Bitmap bmp;
using (var ms = new MemoryStream(imageData))
{
bmp = new Bitmap(ms);
}
That uses the Bitmap(Stream stream) constructor overload.
UPDATE: keep in mind that according to the documentation, and the source code I've been reading through, an ArgumentException will be thrown on these conditions:
stream does not contain image data or is null.
-or-
stream contains a PNG image file with a single dimension greater than 65,535 pixels.
Guys thank you for your help. I think all of this answers works. However i think my byte array contains raw bytes. That's why all of those solutions didnt work for my code.
However i found a solution. Maybe this solution helps other coders who have problem like mine.
static byte[] PadLines(byte[] bytes, int rows, int columns) {
int currentStride = columns; // 3
int newStride = columns; // 4
byte[] newBytes = new byte[newStride * rows];
for (int i = 0; i < rows; i++)
Buffer.BlockCopy(bytes, currentStride * i, newBytes, newStride * i, currentStride);
return newBytes;
}
int columns = imageWidth;
int rows = imageHeight;
int stride = columns;
byte[] newbytes = PadLines(imageData, rows, columns);
Bitmap im = new Bitmap(columns, rows, stride,
PixelFormat.Format8bppIndexed,
Marshal.UnsafeAddrOfPinnedArrayElement(newbytes, 0));
im.Save("C:\\Users\\musa\\Documents\\Hobby\\image21.bmp");
This solutions works for 8bit 256 bpp (Format8bppIndexed). If your image has another format you should change PixelFormat .
And there is a problem with colors right now. As soon as i solved this one i will edit my answer for other users.
*PS = I am not sure about stride value but for 8bit it should be equal to columns.
And also this function Works for me.. This function copies 8 bit greyscale image into a 32bit layout.
public void SaveBitmap(string fileName, int width, int height, byte[] imageData)
{
byte[] data = new byte[width * height * 4];
int o = 0;
for (int i = 0; i < width * height; i++)
{
byte value = imageData[i];
data[o++] = value;
data[o++] = value;
data[o++] = value;
data[o++] = 0;
}
unsafe
{
fixed (byte* ptr = data)
{
using (Bitmap image = new Bitmap(width, height, width * 4,
PixelFormat.Format32bppRgb, new IntPtr(ptr)))
{
image.Save(Path.ChangeExtension(fileName, ".jpg"));
}
}
}
}
Can be as easy as:
var ms = new MemoryStream(imageData);
System.Drawing.Image image = Image.FromStream(ms);
image.Save("c:\\image.jpg");
Testing it out:
byte[] imageData;
// Create the byte array.
var originalImage = Image.FromFile(#"C:\original.jpg");
using (var ms = new MemoryStream())
{
originalImage.Save(ms, ImageFormat.Jpeg);
imageData = ms.ToArray();
}
// Convert back to image.
using (var ms = new MemoryStream(imageData))
{
Image image = Image.FromStream(ms);
image.Save(#"C:\newImage.jpg");
}
In addition, you can simply convert byte array to Bitmap.
var bmp = new Bitmap(new MemoryStream(imgByte));
You can also get Bitmap from file Path directly.
Bitmap bmp = new Bitmap(Image.FromFile(filePath));
This was helpful to me: https://www.tek-tips.com/viewthread.cfm?qid=1264492 (Reference answer)
I understand the question as follows:
I have a byte array that contains pixel data e.g. in RGB format (24bit/pixel)
From this raw pixel data I want to create a Bitmap
This code worked for me:
int width = ...;
int height = ...;
byte[] pixelArray = new byte[] {
// Creation of the actual data is not in the scope of this answer
};
Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
// Create a BitmapData and lock all pixels to be written
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
// Copy the data from the byte array into BitmapData.Scan0
Marshal.Copy(pixelArray, 0, bmpData.Scan0, pixelArray.Length);
// Unlock the pixels
bmp.UnlockBits(bmpData);
// Do something with your image, e.g. save it to disc
bmp.Save("c:\\temp\\mybmp.bmp", ImageFormat.Bmp);
Based on the accepted answer the OP wanted to interpret imageData byte array as the pixel buffer, rather than an already encoded bitmap stream as the most upvoted answer suggests. And though it works, it contains a lot of copies, as well as palette issues ("And there is a problem with colors right now").
I actually happen to have a drawing library exactly for this purpose (among others). The platform-independent core library allows you to interpret any array of primitive types as a bitmap data:
// Unlike in the accepted answer, no extra buffer allocation or
// array copy happens in the background. Note that we can specify
// a palette for the indexed format so the colors will be interpreted correctly
using var myBitmap = BitmapDataFactory.CreateBitmapData(imageData, new Size(fWidth, fHeight),
stride: fWidth, // stride is same as width because of the 8bpp pixel format
pixelFormat: KnownPixelFormat.Format8bppIndexed,
palette: Palette.Grayscale256());
myBitmap is now an IReadWriteBitmapData instance, allowing a lot of operations (just see the available extension methods). It also offers a pretty fast SetPixel method, which respects the palette so in this particular case it turns any color to grayscale. But if you know the actual pixel format you can also can use the WriteRaw<T> method to access the pixels directly.
And if you use the technology-specific packages such as the one for GDI+ or WPF, then you can simply convert your buffer into known bitmap types such as System.Drawing.Bitmap or System.Windows.Media.WriteableBitmap:
// the accepted answer creates two bitmaps due to the color problems where
// the 2nd one is a 32 bpp image. This solution is much faster, simpler, it avoids
// unnecessary allocations and uses parallel processing internally if possible
var systemBitmap = myBitmap.ToBitmap(); // or ToBitmapAsync, ToWriteableBitmap, etc.

What byte[] must have in order to be "savable" into bitmap? [duplicate]

I searched all question about byte array but i always failed. I have never coded c# i am new in this side. Could you help me how to make image file from byte array.
Here is my function which stores byte in array named imageData
public void imageReady( byte[] imageData, int fWidth, int fHeight))
You'll need to get those bytes into a MemoryStream:
Bitmap bmp;
using (var ms = new MemoryStream(imageData))
{
bmp = new Bitmap(ms);
}
That uses the Bitmap(Stream stream) constructor overload.
UPDATE: keep in mind that according to the documentation, and the source code I've been reading through, an ArgumentException will be thrown on these conditions:
stream does not contain image data or is null.
-or-
stream contains a PNG image file with a single dimension greater than 65,535 pixels.
Guys thank you for your help. I think all of this answers works. However i think my byte array contains raw bytes. That's why all of those solutions didnt work for my code.
However i found a solution. Maybe this solution helps other coders who have problem like mine.
static byte[] PadLines(byte[] bytes, int rows, int columns) {
int currentStride = columns; // 3
int newStride = columns; // 4
byte[] newBytes = new byte[newStride * rows];
for (int i = 0; i < rows; i++)
Buffer.BlockCopy(bytes, currentStride * i, newBytes, newStride * i, currentStride);
return newBytes;
}
int columns = imageWidth;
int rows = imageHeight;
int stride = columns;
byte[] newbytes = PadLines(imageData, rows, columns);
Bitmap im = new Bitmap(columns, rows, stride,
PixelFormat.Format8bppIndexed,
Marshal.UnsafeAddrOfPinnedArrayElement(newbytes, 0));
im.Save("C:\\Users\\musa\\Documents\\Hobby\\image21.bmp");
This solutions works for 8bit 256 bpp (Format8bppIndexed). If your image has another format you should change PixelFormat .
And there is a problem with colors right now. As soon as i solved this one i will edit my answer for other users.
*PS = I am not sure about stride value but for 8bit it should be equal to columns.
And also this function Works for me.. This function copies 8 bit greyscale image into a 32bit layout.
public void SaveBitmap(string fileName, int width, int height, byte[] imageData)
{
byte[] data = new byte[width * height * 4];
int o = 0;
for (int i = 0; i < width * height; i++)
{
byte value = imageData[i];
data[o++] = value;
data[o++] = value;
data[o++] = value;
data[o++] = 0;
}
unsafe
{
fixed (byte* ptr = data)
{
using (Bitmap image = new Bitmap(width, height, width * 4,
PixelFormat.Format32bppRgb, new IntPtr(ptr)))
{
image.Save(Path.ChangeExtension(fileName, ".jpg"));
}
}
}
}
Can be as easy as:
var ms = new MemoryStream(imageData);
System.Drawing.Image image = Image.FromStream(ms);
image.Save("c:\\image.jpg");
Testing it out:
byte[] imageData;
// Create the byte array.
var originalImage = Image.FromFile(#"C:\original.jpg");
using (var ms = new MemoryStream())
{
originalImage.Save(ms, ImageFormat.Jpeg);
imageData = ms.ToArray();
}
// Convert back to image.
using (var ms = new MemoryStream(imageData))
{
Image image = Image.FromStream(ms);
image.Save(#"C:\newImage.jpg");
}
In addition, you can simply convert byte array to Bitmap.
var bmp = new Bitmap(new MemoryStream(imgByte));
You can also get Bitmap from file Path directly.
Bitmap bmp = new Bitmap(Image.FromFile(filePath));
This was helpful to me: https://www.tek-tips.com/viewthread.cfm?qid=1264492 (Reference answer)
I understand the question as follows:
I have a byte array that contains pixel data e.g. in RGB format (24bit/pixel)
From this raw pixel data I want to create a Bitmap
This code worked for me:
int width = ...;
int height = ...;
byte[] pixelArray = new byte[] {
// Creation of the actual data is not in the scope of this answer
};
Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
// Create a BitmapData and lock all pixels to be written
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
// Copy the data from the byte array into BitmapData.Scan0
Marshal.Copy(pixelArray, 0, bmpData.Scan0, pixelArray.Length);
// Unlock the pixels
bmp.UnlockBits(bmpData);
// Do something with your image, e.g. save it to disc
bmp.Save("c:\\temp\\mybmp.bmp", ImageFormat.Bmp);
Based on the accepted answer the OP wanted to interpret imageData byte array as the pixel buffer, rather than an already encoded bitmap stream as the most upvoted answer suggests. And though it works, it contains a lot of copies, as well as palette issues ("And there is a problem with colors right now").
I actually happen to have a drawing library exactly for this purpose (among others). The platform-independent core library allows you to interpret any array of primitive types as a bitmap data:
// Unlike in the accepted answer, no extra buffer allocation or
// array copy happens in the background. Note that we can specify
// a palette for the indexed format so the colors will be interpreted correctly
using var myBitmap = BitmapDataFactory.CreateBitmapData(imageData, new Size(fWidth, fHeight),
stride: fWidth, // stride is same as width because of the 8bpp pixel format
pixelFormat: KnownPixelFormat.Format8bppIndexed,
palette: Palette.Grayscale256());
myBitmap is now an IReadWriteBitmapData instance, allowing a lot of operations (just see the available extension methods). It also offers a pretty fast SetPixel method, which respects the palette so in this particular case it turns any color to grayscale. But if you know the actual pixel format you can also can use the WriteRaw<T> method to access the pixels directly.
And if you use the technology-specific packages such as the one for GDI+ or WPF, then you can simply convert your buffer into known bitmap types such as System.Drawing.Bitmap or System.Windows.Media.WriteableBitmap:
// the accepted answer creates two bitmaps due to the color problems where
// the 2nd one is a 32 bpp image. This solution is much faster, simpler, it avoids
// unnecessary allocations and uses parallel processing internally if possible
var systemBitmap = myBitmap.ToBitmap(); // or ToBitmapAsync, ToWriteableBitmap, etc.

Create a Bigtiff (>4GB) File with Bitmiracle Libtiff.net

First I want to thank Bitmiracle for this great lib. Even while creating very big files, the memory footprint is very low.
A few days ago I ran into a problem where I wanted to create a tiff file bigger than 4GB. I created the tiled tiff file successfully, but it seems that the color of the tiles created beyond 4GB are somehow inverted.
Here the code relevant code:
Usage:
WriteTiledTiff("bigtiff.tiff",BitmapSourceFromBrush(new RadialGradientBrush(Colors.Aqua,Colors.Red), 256));
Methods:
public static BitmapSource BitmapSourceFromBrush(Brush drawingBrush, int size = 32, int dpi = 96)
{
// RenderTargetBitmap = builds a bitmap rendering of a visual
var pixelFormat = PixelFormats.Pbgra32;
RenderTargetBitmap rtb = new RenderTargetBitmap(size, size, dpi, dpi, pixelFormat);
// Drawing visual allows us to compose graphic drawing parts into a visual to render
var drawingVisual = new DrawingVisual();
using (DrawingContext context = drawingVisual.RenderOpen())
{
// Declaring drawing a rectangle using the input brush to fill up the visual
context.DrawRectangle(drawingBrush, null, new Rect(0, 0, size, size));
}
// Actually rendering the bitmap
rtb.Render(drawingVisual);
return rtb;
}
public static void WriteTiledTiff(string fileName, BitmapSource tile)
{
const int PIXEL_WIDTH = 48000;
const int PIXEL_HEIGHT = 48000;
int iTile_Width = tile.PixelWidth;
int iTile_Height = tile.PixelHeight;
using (Tiff tiff = Tiff.Open(fileName, "w"))
{
tiff.SetField(TiffTag.IMAGEWIDTH, PIXEL_WIDTH);
tiff.SetField(TiffTag.IMAGELENGTH, PIXEL_HEIGHT);
tiff.SetField(TiffTag.COMPRESSION, Compression.NONE);
tiff.SetField(TiffTag.PHOTOMETRIC, Photometric.RGB);
tiff.SetField(TiffTag.ROWSPERSTRIP, PIXEL_HEIGHT);
tiff.SetField(TiffTag.XRESOLUTION, 96);
tiff.SetField(TiffTag.YRESOLUTION, 96);
tiff.SetField(TiffTag.BITSPERSAMPLE, 8);
tiff.SetField(TiffTag.SAMPLESPERPIXEL, 3);
tiff.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG);
tiff.SetField(TiffTag.TILEWIDTH, iTile_Width);
tiff.SetField(TiffTag.TILELENGTH, iTile_Height);
int tileC = 0;
for (int row = 0; row < PIXEL_HEIGHT; row += iTile_Height)
{
for (int col = 0; col < PIXEL_WIDTH; col += iTile_Width)
{
if (tile.Format != PixelFormats.Rgb24) tile = new FormatConvertedBitmap(tile, PixelFormats.Rgb24, null, 0);
int stride = tile.PixelWidth * ((tile.Format.BitsPerPixel + 7) / 8);
byte[] pixels = new byte[tile.PixelHeight * stride];
tile.CopyPixels(pixels, stride, 0);
tiff.WriteEncodedTile(tileC++, pixels, pixels.Length);
}
}
tiff.WriteDirectory();
}
}
The resulted file will be 6,47GB in size. I viewed it with a small tool called "vliv" vilv download
All LibTiff.Net versions including 2.4.500.0 are based on 3.x branch of the original libtiff.
Support for BigTIFF was introduced in 4.x branch of the original libtiff. Thus, at this time there are no LibTiff.Net versions designed to handle BigTiff files / files over 4GB on disk.
EDIT:
LibTiff.Net 2.4.508 adds support for BigTiff.

Using WritePixels when using a writeable bitmap from a intptr to create a bitmap.

Im currently trying to use writeablebitmap to take a IntPtr of a scan of images and turn each one into a Bitmap. Im wanting to use writeablebitmap because im having an issue with standard gdi
GDI+ System.Drawing.Bitmap gives error Parameter is not valid intermittently
There is a method on a WriteableBitmap that called WritePixels
http://msdn.microsoft.com/en-us/library/aa346817.aspx
Im not sure what I set for the buffer and the stride every example I find it shows the stride as 0 although that throws an error. When I set the stride to 5 the image appear black. I know this may not be the most efficient code but any help would be appreciated.
//create bitmap header
bmi = new BITMAPINFOHEADER();
//create initial rectangle
Int32Rect rect = new Int32Rect(0, 0, 0, 0);
//create duplicate intptr to use while in global lock
dibhand = dibhandp;
bmpptr = GlobalLock(dibhand);
//get the pixel sizes
pixptr = GetPixelInfo(bmpptr);
//create writeable bitmap
var wbitm = new WriteableBitmap(bmprect.Width, bmprect.Height, 96.0, 96.0, System.Windows.Media.PixelFormats.Bgr32, null);
//draw the image
wbitm.WritePixels(rect, dibhandp, 10, 0);
//convert the writeable bitmap to bitmap
var stream = new MemoryStream();
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbitm));
encoder.Save(stream);
byte[] buffer = stream.GetBuffer();
var bitmap = new System.Drawing.Bitmap(new MemoryStream(buffer));
GlobalUnlock(dibhand);
GlobalFree(dibhand);
GlobalFree(dibhandp);
GlobalFree(bmpptr);
dibhand = IntPtr.Zero;
return bitmap;
An efficient way to work on Bitmaps in C# is to pass temporarily in unsafe mode (I know I don't answer the question exactly but I think the OP did not manage to use Bitmap, so this could be a solution anyway). You just have to lock bits and you're done:
unsafe private void GaussianFilter()
{
// Working images
using (Bitmap newImage = new Bitmap(width, height))
{
// Lock bits for performance reason
BitmapData newImageData = newImage.LockBits(new Rectangle(0, 0, newImage.Width,
newImage.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte* pointer = (byte*)newImageData.Scan0;
int offset = newImageData.Stride - newImageData.Width * 4;
// Compute gaussian filter on temp image
for (int j = 0; j < InputData.Height - 1; ++j)
{
for (int 0 = 1; i < InputData.Width - 1; ++i)
{
// You browse 4 bytes per 4 bytes
// The 4 bytes are: B G R A
byte blue = pointer[0];
byte green = pointer[1];
byte red = pointer[2];
byte alpha = pointer[3];
// Your business here by setting pointer[i] = ...
// If you don't use alpha don't forget to set it to 255 else your whole image will be black !!
// Go to next pixels
pointer += 4;
}
// Go to next line: do not forget pixel at last and first column
pointer += offset;
}
// Unlock image
newImage.UnlockBits(newImageData);
newImage.Save("D:\temp\OCR_gray_gaussian.tif");
}
}
This is really much more efficient than SetPixel(i, j), you just have to be careful about pointer limits (and not forget to unlock data when you're done).
Now to answer your question about stride: the stride is the length in bytes of a line, it is a multiple of 4. In my exemple I use the format Format32bppArgb which uses 4 bytes per pixel (R, G, B and alpha), so newImageData.Stride and newImageData.Width * 4 are always the same. I use the offset in my loops only to show where it would be necessary.
But if you use another format, for instance Format24bppRgb which uses 3 bytes per pixel (R, G and B only), then there may be an offset between stride and width. For an image 10 * 10 pixels in this format, you will have a stride of 10 * 3 = 30, + 2 to reach nearest multiple of 4, i.e. 32.

Categories

Resources