Marshal.Copy() is not copying to bitmap - c#

I'm working on an image processing project, and I've read that the fastest way to manipulate a bitmap image is to copy it from a byte array using Marshal.Copy(). However, for whatever reason, nothing is being copied from my byte array to my Bitmap, and there's not a clear reason why. This is the code I'm using to copy into my Bitmap:
public void UpdateImage()
{
var data = image.LockBits(
new Rectangle(Point.Empty, image.Size),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppArgb);
Marshal.Copy(backBuffer, 0, data.Scan0, backBuffer.Length);
image.UnlockBits(data);
Console.WriteLine("UpdateImage");//For debugging purposes
}
I'm attempting to fill the image with complete black, and looking at the data of the backBuffer, it appears to be correct, and as expected, where as when I check any pixel of 'image' it is completely blank. I have no idea why nothing is happening. Any advice would be much appreciated!
Edit: I apologize, I'm a bit new around here, let me provide some more information. Specifically, I am working on some GPU accelerated image processing using Cloo/OpenCL. I wanted to fill the screen with black to make sure that I am doing things correctly, although I am evidently not. Here is the entire class file I'm using:
public class RenderTarget
{
public GraphicsDevice GraphicsDevice;
private byte[] backBuffer;
public Bitmap image;
private ComputeKernel fillKernel;
private ComputeProgram fillProgram;
public void UpdateImage()
{
var data = image.LockBits(
new Rectangle(Point.Empty, image.Size),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppArgb);
Marshal.Copy(backBuffer, 0, data.Scan0, backBuffer.Length);
image.UnlockBits(data);
Console.WriteLine("UpdateImage");
}
//Test method ONLY
public void FillScreen(Color color)
{
if (fillProgram == null)//temporary, all kernels should be compiled on start up. In fact, these probably should be static
{
string fillText = #"
kernel void fillScreen(global uchar* data_out, int from, int to, uchar a, uchar r, uchar g, uchar b){
for (int i = from; i < to; i += 4){
data_out[i] = a;
data_out[i + 1] = r;
data_out[i + 2] = g;
data_out[i + 3] = b;
}
}";
fillProgram = new ComputeProgram(GraphicsDevice.context, fillText);
fillProgram.Build(null, null, null, IntPtr.Zero);
fillKernel = fillProgram.CreateKernel("fillScreen");
}
ComputeBuffer<byte> backBufferBuffer = new ComputeBuffer<byte>(GraphicsDevice.context, ComputeMemoryFlags.ReadOnly | ComputeMemoryFlags.UseHostPointer, backBuffer);
fillKernel.SetMemoryArgument(0, backBufferBuffer);
for (int i = 0; i < backBuffer.Length / 10000; i++)
{
fillKernel.SetValueArgument<int>(1, i * 10000);
fillKernel.SetValueArgument<int>(2, (i + 1) * 10000);
fillKernel.SetValueArgument<byte>(3, color.A);
fillKernel.SetValueArgument<byte>(4, color.R);
fillKernel.SetValueArgument<byte>(5, color.G);
fillKernel.SetValueArgument<byte>(6, color.B);
GraphicsDevice.queue.ExecuteTask(fillKernel, null);
}
GraphicsDevice.queue.ReadFromBuffer(backBufferBuffer, ref backBuffer, false, null);
GraphicsDevice.queue.Finish();
}
public RenderTarget(int Width, int Height, GraphicsDevice device)
{
image = new Bitmap(Width, Height);
backBuffer = new byte[4 * Width * Height];
GraphicsDevice = device;
//Fill the screen with black
FillScreen(Color.Black);
UpdateImage();
Console.WriteLine(image.GetPixel(0, 0).A);
}
}
I have checked to make absolutely sure that the backBuffer is correct. (The values I expected were 255, 0, 0, 0 for the first four elements of the backBuffer).

Okay, I figured out what was going wrong. I had the format in the backBuffer wrong. I was expecting it to be ARGB when it should be ordered RGBA. So, it was a problem with my code in 'fillText'.

Related

Transform 8bpp image into 24bpp and preserve color

I have an 8bpp image with a custom palete that holds a colored picture.
Now, I'm trying to convert it to PixelFormat.Format24bppRgb format picture. I'm using direct pixels access using the code from here http://www.codeproject.com/Tips/240428/Work-with-bitmap-faster-with-Csharp
and the usage is
Bitmap bmp = (Bitmap) Image.FromFile("T:\\500-blue-orig.png");
LockBitmap lbmpSrc = new LockBitmap(bmp);
Bitmap dst = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format24bppRgb);
LockBitmap lbmpDst = new LockBitmap(dst);
lbmpSrc.LockBits();
lbmpDst.LockBits();
dst.Palette = bmp.Palette;
for (int y = 0; y < lbmpSrc.Height; y++)
{
for (int x = 0; x < lbmpSrc.Width; x++)
{
Color c = lbmpSrc.GetPixel(x, y);
lbmpDst.SetPixel(x, y, c);
}
}
lbmpDst.UnlockBits();
lbmpSrc.UnlockBits();
dst.Save("T:\\x.png", ImageFormat.Png);
However the ending result is a grayscale image even though I do copy the original palette.
What am I doing wrong here? How do I get a 24bpp colored image from a 8bpp picture which actually has colors?
I tried a manual approach using unmanaged code - local benchmark shows it to be 99.7% faster than the ignorant (Bitmap.GetPixel > Bitmap.SetPixel) approach.
Basically, we use the LockBits pointer and assign bytes one by one based on the color palette.
static unsafe void To24Bpp(Bitmap source, Bitmap dest)
{
var sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly,
PixelFormat.Format8bppIndexed);
var destData = dest.LockBits(new Rectangle(0, 0, dest.Width, dest.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
var paletteBytes = source.Palette.Entries.Select(ColorToUintRgbLeftAligned).ToArray();
var current = (byte*) sourceData.Scan0.ToPointer();
var lastPtr = (byte*) (sourceData.Scan0 + sourceData.Width*sourceData.Height).ToPointer();
var targetPtr = (byte*) destData.Scan0;
while (current <= lastPtr)
{
var value = paletteBytes[*current++];
targetPtr[0] = (byte) (value >> 24);
targetPtr[1] = (byte) (value >> 16);
targetPtr[2] = (byte) (value >> 8);
targetPtr += 3;
}
source.UnlockBits(sourceData);
dest.UnlockBits(destData);
}
static uint ColorToUintRgbLeftAligned(Color color)
{
return ((uint) color.B << 24) + ((uint) color.G << 16) + ((uint) color.R << 8);
}
The code could be improved to write 4 bytes at a time from the color pallette, reducing the amount of random memory access. My local benchmark showed the performance of this improved by a further 25%. Note the difference in building the uint color bytes - the alignment of bytes in a uint was opposite of what I expected.
private static unsafe void To24BppUintAssignment(Bitmap source, Bitmap dest)
{
var sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
var destData = dest.LockBits(new Rectangle(0, 0, dest.Width, dest.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
uint[] paletteBytes = source.Palette.Entries.Select(ColorToUintRgbRightAligned).ToArray();
var current = (byte*)sourceData.Scan0.ToPointer();
var lastPtr = (byte*)(sourceData.Scan0 + sourceData.Width * sourceData.Height).ToPointer();
var targetPtr = (byte*) destData.Scan0;
while (current < lastPtr)
{
var targetAsUint = ((uint*) targetPtr);
targetAsUint[0] = paletteBytes[*current++];
targetPtr += 3;
}
uint finalValue = paletteBytes[*current];
targetPtr[0] = (byte)(finalValue >> 24);
targetPtr[1] = (byte)(finalValue >> 16);
targetPtr[2] = (byte)(finalValue >> 8);
source.UnlockBits(sourceData);
dest.UnlockBits(destData);
}
private static uint ColorToUintRgbRightAligned(Color color)
{
return ((uint)color.B) + ((uint)color.G << 8) + ((uint)color.R << 16);
}
I didn't create the bitmap in the method for benchmarking purposes, it should be called as such:
static Bitmap To24Bpp(Bitmap source)
{
var dest = new Bitmap(source.Width, source.Height, PixelFormat.Format24bppRgb);
To24BppUintAssignment(source, dest);
return dest;
}
The LockBitmap class you are using doesn't care about palette, it assumes 8bpp images are always grayscale and will return only grays.
Also the class is far from fast since it copies bitmap data to another array and back, creates Color when not necessarily needed etc. If you really want performance you will do the handling yourself.
You have two choices:
use GetPixel and SetPixel from Bitmap directly. It will work as it should.
copy the 8bpp palette image into a 32/24bpp image first, then use that class for processing
People here seem to all be ridiculously overcomplicating matters. Converting an 8BPP image to 24BPP or 32BPP doesn't need any special code.
The only thing that's difficult about 8BPP images is manipulating them, and, you don't need to do that at all; your end result isn't one of these problematic 8BPP images.
You can do it in less than five lines:
public static Bitmap PaintOn32bpp(Image image)
{
Bitmap bp = new Bitmap(image.Width, image.Height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(bp))
gr.DrawImage(image, new Rectangle(0, 0, bp.Width, bp.Height));
return bp;
}
This will work with any image, regardless of its colour format.

How to create a Byte array that contains a real Image?

Please see my code below.
I want to create a Byte array with data that I can convert into a real image. When I try to run this code I get an argumentException. What do I need to do in the For loop in order to create a legitimate Byte array that will hold data of an image? I don't want to use a real image and convert it to byte array, I want to create an image form random numbers.
Random Rnd = new Random();
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Byte[] ByteArray = new Byte[1000];
for (int i = 0; i < 1000; i++)
{
ByteArray[i] = Convert.ToByte(Rnd.Next(9));
}
ImageConverter Convertor = new ImageConverter();
BitmapImage image = (BitmapImage)Convertor.ConvertFrom(ByteArray);
MyImage.Source = image;
}
Notice please that I don't want to work with WinForms types or libraries like system.drawing / bitmap - I only want to use WPF technology.
This is the solution you are looking for, using only WPF technology.
Note that the constant value of 16 used in the stride parameter calculation comes directly from the fact that I am using a 16-bit pixel format.
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Random rnd = new Random();
Byte[] ByteArray = new Byte[(int)MyImage.Width * (int)MyImage.Height * 3];
rnd.NextBytes(ByteArray);
var image = BitmapSource.Create((int) MyImage.Width, (int) MyImage.Height, 72, 72,
PixelFormats.Bgr565, null, ByteArray, (4*((int)MyImage.Width * 16 + 31)/32));
MyImage.Source = image;
}
This just might do the trick for you:
private static Bitmap GenBitmap(int width, int height) {
int ch = 3; //number of channels (ie. assuming 24 bit RGB in this case)
Random rnd = new Random();
int imageByteSize = width * height * ch;
byte[] imageData = new byte[imageByteSize]; //your image data buffer
rnd.NextBytes(imageData); //Fill with random bytes;
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr pNative = bmData.Scan0;
Marshal.Copy(imageData, 0, pNative, imageByteSize);
bitmap.UnlockBits(bmData);
return bitmap;
}
I'm not sure how Converter.ConvertFrom works but I prefer to do my bitmaps the lower-level way with Bitmap.LockBits() and a little Marshal.Copy().
See this method:
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
static Bitmap CreateRandomBitmap(Size size)
{
// Create a new bitmap for the size requested.
var bitmap = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb);
// Lock the entire bitmap for write-only acccess.
var rect = new Rectangle(0, 0, size.Width, size.Height);
var bitmapData = bitmap.LockBits(rect, ImageLockMode.WriteOnly, bitmap.PixelFormat);
// Calculate the number of bytes required and allocate them.
var numberOfBytes = bitmapData.Stride * size.Height;
var bitmapBytes = new byte[numberOfBytes];
// Fill the bitmap bytes with random data.
var random = new Random();
for (int x = 0; x < size.Width; x++)
{
for (int y = 0; y < size.Height; y++)
{
// Get the index of the byte for this pixel (x/y).
var i = ((y * size.Width) + x) * 4; // 32bpp
// Generate the next random pixel color value.
var value = (byte)random.Next(9);
bitmapBytes[i] = value; // BLUE
bitmapBytes[i + 1] = value; // GREEN
bitmapBytes[i + 2] = value; // RED
bitmapBytes[i + 3] = 0xFF; // ALPHA
}
}
// Copy the randomized bits to the bitmap pointer.
var ptr = bitmapData.Scan0;
Marshal.Copy(bitmapBytes, 0, ptr, numberOfBytes);
// Unlock the bitmap, we're all done.
bitmap.UnlockBits(bitmapData);
return bitmap;
}
Then you can do something like this:
public void Run()
{
using(var bitmap = CreateRandomBitmap(new Size(64, 64)))
{
bitmap.Save("random.png", ImageFormat.Png);
}
}
You can't use random bytes to create an image, because each type of image (bmp, jpeg, png) is constructed with a certain format. The code wouldn't know how to interpret random bytes.
http://en.wikipedia.org/wiki/Image_file_formats

Saving many screenshots consecutively with Texture2D.SaveAsPng causes out of memory exception

When I make screenshots in my XNA game, each texture.SaveAsPng consumes some memory and it doesn't seem to return back to the game. So eventually I run out of memory. I tried saving the texture data into FileStream and MemoryStream, hoping I could save it from there as Bitmap, but the results are the same. Is there a way to forcefully free this memory or some workaround that will let me get image data and save it some other way without running into out of memory exception?
sw = GraphicsDevice.Viewport.Width;
sh = GraphicsDevice.Viewport.Height;
int[] backBuffer = new int[sw * sh];
GraphicsDevice.GetBackBufferData(backBuffer);
using(Texture2D texture = new Texture2D(GraphicsDevice, sw, sh, false,
GraphicsDevice.PresentationParameters.BackBufferFormat))
{
texture.SetData(backBuffer);
using(var fs = new FileStream("screenshot.png", FileMode.Create))
texture.SaveAsPng(fs, sw, sh); // ← this line causes memory leak
}
You might be able to create the Bitmap from the texture bytes directly and bypass the internal method to check if the SaveAsPng is leaking or its something else.
Try This extension method (unfortunatly I cannot test (no xna at work) but it should work.)
public static class TextureExtensions
{
public static void TextureToPng(this Texture2D texture, int width, int height, ImageFormat imageFormat, string filename)
{
using (Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb))
{
byte blue;
IntPtr safePtr;
BitmapData bitmapData;
Rectangle rect = new Rectangle(0, 0, width, height);
byte[] textureData = new byte[4 * width * height];
texture.GetData<byte>(textureData);
for (int i = 0; i < textureData.Length; i += 4)
{
blue = textureData[i];
textureData[i] = textureData[i + 2];
textureData[i + 2] = blue;
}
bitmapData = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
safePtr = bitmapData.Scan0;
Marshal.Copy(textureData, 0, safePtr, textureData.Length);
bitmap.UnlockBits(bitmapData);
bitmap.Save(filename, imageFormat);
}
}
}
Its a bit crude but you can clean it up (if it even works).
An last but not least (if all other attempts fail) you could call GarbageCollection yourself but this is NOT recommended as its pretty bad practice.
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
The above code should be LAST resort only.
Good luck.

Calculate image differences in C#

How could I generate a System.Drawing.Image that contains the differences between the pixels of two other images?
Something similar to GitHub does, but written in C#
The algorithm that GiHub uses is implemented in javascript. There is a context-blender project that replicates Photoshop blend modes.
Do you know if is translated to C# or a similar algorithm that has the same quality level? I need to manage also transparent images (with alpha channel).
Here's a quick and dirty implementation:
void Main()
{
var a = (Bitmap)Image.FromFile("image1.png");
var b = (Bitmap)Image.FromFile("image2.png");
var diff = PixelDiff(a, b);
}
unsafe Bitmap PixelDiff(Bitmap a, Bitmap b)
{
Bitmap output = new Bitmap(a.Width, a.Height, PixelFormat.Format32bppArgb);
Rectangle rect = new Rectangle(Point.Empty, a.Size);
using (var aData = a.LockBitsDisposable(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb))
using (var bData = b.LockBitsDisposable(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb))
using (var outputData = output.LockBitsDisposable(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb))
{
byte* aPtr = (byte*)aData.Scan0;
byte* bPtr = (byte*)bData.Scan0;
byte* outputPtr = (byte*)outputData.Scan0;
int len = aData.Stride * aData.Height;
for (int i = 0; i < len; i++)
{
// For alpha use the average of both images (otherwise pixels with the same alpha won't be visible)
if ((i + 1) % 4 == 0)
*outputPtr = (byte)((*aPtr + *bPtr) / 2);
else
*outputPtr = (byte)~(*aPtr ^ *bPtr);
outputPtr++;
aPtr++;
bPtr++;
}
}
return output;
}
static class Extensions
{
public static DisposableImageData LockBitsDisposable(this Bitmap bitmap, Rectangle rect, ImageLockMode flags, PixelFormat format)
{
return new DisposableImageData(bitmap, rect, flags, format);
}
public class DisposableImageData : IDisposable
{
private readonly Bitmap _bitmap;
private readonly BitmapData _data;
internal DisposableImageData(Bitmap bitmap, Rectangle rect, ImageLockMode flags, PixelFormat format)
{
bitmap.CheckArgumentNull("bitmap");
_bitmap = bitmap;
_data = bitmap.LockBits(rect, flags, format);
}
public void Dispose()
{
_bitmap.UnlockBits(_data);
}
public IntPtr Scan0
{
get { return _data.Scan0; }
}
public int Stride
{
get { return _data.Stride;}
}
public int Width
{
get { return _data.Width;}
}
public int Height
{
get { return _data.Height;}
}
public PixelFormat PixelFormat
{
get { return _data.PixelFormat;}
}
public int Reserved
{
get { return _data.Reserved;}
}
}
}
Notes:
this implementation assumes that both images have the same size, which might not be the case... taking different sizes into account is possible of course, just a little harder.
the LockBitsDisposable method is just a convenience, if you prefer you can use the standard LockBits method (but don't forget to unlock the bits when you're done)
A quick google search yielded this:
http://www.bryancook.net/2009/10/find-differences-between-images-c.html
If your're going to be using ARGB rather then RGB, it'll probably need a bit of editing. If you wanted to get that 'inverted difference' effect, like in the Github link posted, you could find the differance between RGB colors and use that for each pixel in the difference image, etc.

convert bitonal TIFF to bitonal PNG in C#

I need to convert bitonal (black and white) TIFF files into another format for display by a web browser, currently we're using JPGs, but the format isn't crucial. From reading around .NET doesn't seem to easily support writing bitonal images, so we're ending up with ~1MB files instead of ~100K ones. I'm considering using ImageMagick to do this, but ideally i'd like a solution which doesn't require this if possible.
Current code snippet (which also does some resizing on the image):
using (Image img = Image.FromFile(imageName))
{
using (Bitmap resized = new Bitmap(resizedWidth, resizedHeight)
{
using (Graphics g = Graphics.FromImage(resized))
{
g.DrawImage(img, new Rectangle(0, 0, resized.Width, resized.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel);
}
resized.Save(outputFilename, System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
Is there any way to achieve this?
Thanks.
I believe the problem can be solved by checking that resized bitmap is of PixelFormat.Format1bppIndexed. If it's not, you should convert it to 1bpp bitmap and after that you can save it as black and white png without problems.
In other words, you should use following code instead of resized.Save(outputFilename, System.Drawing.Imaging.ImageFormat.Jpeg);
if (resized.PixelFormat != PixelFormat.Format1bppIndexed)
{
using (Bitmap bmp = convertToBitonal(resized))
bmp.Save(outputFilename, System.Drawing.Imaging.ImageFormat.Png);
}
else
{
resized.Save(outputFilename, System.Drawing.Imaging.ImageFormat.Png);
}
I use following code for convertToBitonal :
private static Bitmap convertToBitonal(Bitmap original)
{
int sourceStride;
byte[] sourceBuffer = extractBytes(original, out sourceStride);
// Create destination bitmap
Bitmap destination = new Bitmap(original.Width, original.Height,
PixelFormat.Format1bppIndexed);
destination.SetResolution(original.HorizontalResolution, original.VerticalResolution);
// Lock destination bitmap in memory
BitmapData destinationData = destination.LockBits(
new Rectangle(0, 0, destination.Width, destination.Height),
ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
// Create buffer for destination bitmap bits
int imageSize = destinationData.Stride * destinationData.Height;
byte[] destinationBuffer = new byte[imageSize];
int sourceIndex = 0;
int destinationIndex = 0;
int pixelTotal = 0;
byte destinationValue = 0;
int pixelValue = 128;
int height = destination.Height;
int width = destination.Width;
int threshold = 500;
for (int y = 0; y < height; y++)
{
sourceIndex = y * sourceStride;
destinationIndex = y * destinationData.Stride;
destinationValue = 0;
pixelValue = 128;
for (int x = 0; x < width; x++)
{
// Compute pixel brightness (i.e. total of Red, Green, and Blue values)
pixelTotal = sourceBuffer[sourceIndex + 1] + sourceBuffer[sourceIndex + 2] +
sourceBuffer[sourceIndex + 3];
if (pixelTotal > threshold)
destinationValue += (byte)pixelValue;
if (pixelValue == 1)
{
destinationBuffer[destinationIndex] = destinationValue;
destinationIndex++;
destinationValue = 0;
pixelValue = 128;
}
else
{
pixelValue >>= 1;
}
sourceIndex += 4;
}
if (pixelValue != 128)
destinationBuffer[destinationIndex] = destinationValue;
}
Marshal.Copy(destinationBuffer, 0, destinationData.Scan0, imageSize);
destination.UnlockBits(destinationData);
return destination;
}
private static byte[] extractBytes(Bitmap original, out int stride)
{
Bitmap source = null;
try
{
// If original bitmap is not already in 32 BPP, ARGB format, then convert
if (original.PixelFormat != PixelFormat.Format32bppArgb)
{
source = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
source.SetResolution(original.HorizontalResolution, original.VerticalResolution);
using (Graphics g = Graphics.FromImage(source))
{
g.DrawImageUnscaled(original, 0, 0);
}
}
else
{
source = original;
}
// Lock source bitmap in memory
BitmapData sourceData = source.LockBits(
new Rectangle(0, 0, source.Width, source.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
// Copy image data to binary array
int imageSize = sourceData.Stride * sourceData.Height;
byte[] sourceBuffer = new byte[imageSize];
Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, imageSize);
// Unlock source bitmap
source.UnlockBits(sourceData);
stride = sourceData.Stride;
return sourceBuffer;
}
finally
{
if (source != original)
source.Dispose();
}
}
Have you tried saving using the Image.Save overload with Encoder parameters?
Like the Encoder.ColorDepth Parameter?
Trying jaroslav's suggestion for color depth doesn't work:
static void Main(string[] args)
{
var list = ImageCodecInfo.GetImageDecoders();
var jpegEncoder = list[1]; // i know this is the jpeg encoder by inspection
Bitmap bitmap = new Bitmap(500, 500);
Graphics g = Graphics.FromImage(bitmap);
g.DrawRectangle(new Pen(Color.Red), 10, 10, 300, 300);
var encoderParams = new EncoderParameters();
encoderParams.Param[0] = new EncoderParameter(Encoder.ColorDepth, 2);
bitmap.Save(#"c:\newbitmap.jpeg", jpegEncoder, encoderParams);
}
The jpeg is still a full color jpeg.
I don't think there is any support for grayscale jpeg in gdi plus. Have you tried looking in windows imaging component?
http://www.microsoft.com/downloads/details.aspx?FamilyID=8e011506-6307-445b-b950-215def45ddd8&displaylang=en
code example: http://www.codeproject.com/KB/GDI-plus/windows_imaging.aspx
wikipedia: http://en.wikipedia.org/wiki/Windows_Imaging_Component
This is an old thread. However, I'll add my 2 cents.
I use AForge.Net libraries (open source)
use these dlls. Aforge.dll, AForge.Imaging.dll
using AForge.Imaging.Filters;
private void ConvertBitmap()
{
markedBitmap = Grayscale.CommonAlgorithms.RMY.Apply(markedBitmap);
ApplyFilter(new FloydSteinbergDithering());
}
private void ApplyFilter(IFilter filter)
{
// apply filter
convertedBitmap = filter.Apply(markedBitmap);
}
Have you tried PNG with 1 bit color depth?
To achieve a size similar to a CCITT4 TIFF, I believe your image needs to use a 1-bit indexed pallette.
However, you can't use the Graphics object in .NET to draw on an indexed image.
You will probably have to use LockBits to manipulate each pixel.
See Bob Powell's excellent article.

Categories