Generate 16-Bit Grayscale Bitmapdata and Save to File

Generate 16-bit grayscale BitmapData and save to file

This works for System.Drawing.Imaging.PixelFormat.Format16bppGrayScale:

    private static void SaveBmp(Bitmap bmp, string path)
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);

BitmapData bitmapData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);

var pixelFormats = ConvertBmpPixelFormat(bmp.PixelFormat);

BitmapSource source = BitmapSource.Create(bmp.Width,
bmp.Height,
bmp.HorizontalResolution,
bmp.VerticalResolution,
pixelFormats,
null,
bitmapData.Scan0,
bitmapData.Stride * bmp.Height,
bitmapData.Stride);

bmp.UnlockBits(bitmapData);

FileStream stream = new FileStream(path, FileMode.Create);

TiffBitmapEncoder encoder = new TiffBitmapEncoder();

encoder.Compression = TiffCompressOption.Zip;
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Save(stream);

stream.Close();
}

private static System.Windows.Media.PixelFormat ConvertBmpPixelFormat(System.Drawing.Imaging.PixelFormat pixelformat)
{
System.Windows.Media.PixelFormat pixelFormats = System.Windows.Media.PixelFormats.Default;

switch (pixelformat)
{
case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
pixelFormats = PixelFormats.Bgr32;
break;

case System.Drawing.Imaging.PixelFormat.Format8bppIndexed:
pixelFormats = PixelFormats.Gray8;
break;

case System.Drawing.Imaging.PixelFormat.Format16bppGrayScale:
pixelFormats = PixelFormats.Gray16;
break;
}

return pixelFormats;
}

How can I create a 16-bit grayscale PNG in C#?

Update: Magick.NET-Q16-AnyCPU has moved to https://github.com/dlemstra/Magick.NET.

The NuGet package for Magick.NET-Q16-AnyCPU https://magick.codeplex.com/ worked perfectly. Here's my code:

namespace heightmap_generator {
internal class Program {
private static int GRID_SIZE = 1081;

private static void Main(string[] args) {
using (MagickImage image = new MagickImage(@"C:\Users\Brent\AppData\Local\Colossal Order\Cities_Skylines\Addons\MapEditor\Heightmaps\benchmark.png")) {
image.Draw(
new DrawableFillColor(new MagickColor(ushort.MaxValue / 2, ushort.MaxValue / 2, ushort.MaxValue / 2)),
new DrawableRectangle(0, 0, GRID_SIZE, GRID_SIZE)
);
var drawables = new List<IDrawable>();
for (var y = 0; y < GRID_SIZE/50; ++y) {
float t = y / (GRID_SIZE / 50.0f);
t = t*t*(3 - 2*t); //cubic hermite spline h01
ushort v = (ushort)(ushort.MaxValue * (5 + (t-1)*0.3) / 10);
if (y == GRID_SIZE/50 - 1) {
v = (ushort) (ushort.MaxValue*0.501);
}
drawables.Add(new DrawableFillColor(new MagickColor(v, v, v)));
for (var x = 0; x < GRID_SIZE; ++x) {
if (x == GRID_SIZE/2) {
var v2 = (ushort) (v + ushort.MaxValue/1024);
drawables.Add(new DrawableFillColor(new MagickColor(v2, v2, v2)));
drawables.Add(new DrawableColor(x, GRID_SIZE/2 - y, PaintMethod.Point));
drawables.Add(new DrawableColor(x, GRID_SIZE/2 + y, PaintMethod.Point));
drawables.Add(new DrawableFillColor(new MagickColor(v, v, v)));
} else {
drawables.Add(new DrawableColor(x, GRID_SIZE/2 - y, PaintMethod.Point));
drawables.Add(new DrawableColor(x, GRID_SIZE/2 + y, PaintMethod.Point));
}
}
}
image.Draw(drawables);
image.Write(new FileStream(@"C:\Users\Brent\AppData\Local\Colossal Order\Cities_Skylines\Addons\MapEditor\Heightmaps\benchmark3.png", FileMode.Create));
}
}
}

}

Structure for holding a 16bits grayscale image

As you could see, the GDI+ Bitmap does not support the 16bpp grayscale pixel format on Linux at all, and actually its support is quite limited on Windows, too. Once I collected the limitations for both platforms, see the table under the Restrictions of Possible Pixel Formats on Different Platforms section here.

I need to mask image from memory buffers

To use completely managed in-memory representation of a bitmap both on Linux and Windows, you can use this library (disclaimer: written by me). You can create a 16bpp grayscale bitmap data by the BitmapDataFactory.CreateBitmapData method, which returns an IReadWriteBitmapData that allows a lot of managed operations (see the link that enlists the usable extension methods). You can even convert it to an actual Bitmap by the ToBitmap extension, but on Linux this converts the result to a Bitmap with 24bpp RGB pixel format.

Example:

var image16 = BitmapDataFactory.CreateBitmapData(new Size(512, 512), PixelFormat.Format16bppGrayScale);
var row = image16.FirstRow;
do
{
for (int x = 0; x < image16.Width; x++)
{
// row[x] uses a Color32 structure (8 bit per color) but raw access
// enables you to use the full 16-bit color range:
row.WriteRaw<ushort>(x, 32768); // 50% gray
}
} while (row.MoveNextRow());

As for the 8bpp indexed and 24bpp RGB formats, these are supported by the native Bitmap also on Linux, but please note that starting with version .NET 6 System.Drawing will be supported only on Windows by default. Microsoft recommends using other libraries instead, but you can still enable the Unix support by adding "System.Drawing.EnableUnixSupport": true to runtimeconfig.json. Or, if you decide to use my library I mentioned above, just call DrawingModule.Initialize() before anything else, which enables the Unix support without editing any config files.

How can I convert an ushort 16 bit array to an image C#

Adapting from the linked answer on how to store a Format16bppGrayScale bitmap as TIFF, but without creating the actual bitmap first. This requires some .NET dlls that you may not usually add as references, namely PresentationCore and WindowsBase.

The BitmapSource necessary to construct the BitmapFrame that the TIFF encoder can encode can be created straight from an array, so:

var bitmapSrc = BitmapSource.Create(Width, Height, 96, 96,
PixelFormats.Gray16, null, rawData, Width * 2);
TiffBitmapEncoder encoder = new TiffBitmapEncoder();
encoder.Compression = TiffCompressOption.Zip;
encoder.Frames.Add(BitmapFrame.Create(bitmapSrc));
encoder.Save(outputStream);

When I tried this, the file seemed to be a real 16bit image.

Creating 16-bit+ grayscale images in WPF

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.

How do I save a bitmap as a 16-color grayscale GIF or PNG in ASP.NET?

It's called quantization, and it's complicated. I've worked extensively with this problem, and my best results have been using Octree quantization and a custom diffusion algorithm.

Your fastest point from A to B is grab my code (open-source, but $69 to download) and use the extremely simple API to set the color count to 16 and save as GIF or PNG. Should be about 2 lines of code if you want to do it via code-behind... or, you can use a querystring if it's on the filesystem:

image.bmp?format=gif&colors=16

If the image isn't already grayscale, you can do that using the ImageAttributes class of the module. The resulting GIF will automatically have a grayscale palette. Minimal work, great results.

Remember you don't have to use it as an HttpModule - it's primarily a library for resizing, modifying, and encoding images.

If you want to roll your own, here's what I started with:
http://codebetter.com/blogs/brendan.tompkins/archive/2007/06/14/gif-image-color-quantizer-now-with-safe-goodness.aspx

Read through the comments and patch the pointer arithmetic errors per my comments....

No dithering, though, and you may have trouble running the original in less than a full trust environment. I've made a lot of patches over the years, and I don't remember them all.



Related Topics



Leave a reply



Submit