Get transparency percentage of a bitmap - c#

Update: Clemens's solution is the fastest. I'll leave the alternative I found just in case:
While trying to create a minimal reproducible example as Peter Duniho suggested in the comment, I found that the wrong transparency values were coming from theBitmapImageToBitmapConverter()
It was messing up the whole image. I now load the png straight to a bitmap and scan it and it gives accurate results:
Bitmap bmp = new Bitmap("icon.png");
Console.WriteLine(TransparencyPercentageInImage(bmp));
Question was:
I have a few image controls in a list:
imagesList[index].Source = ReloadIcon(index);
They load images from ".png" files like so:
public BitmapImage ReloadIcon(int index)
{
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.UriSource = new Uri(iconPaths[index], UriKind.Absolute);
image.EndInit();
return image;
}
I then convert those to bitmaps using this converter:
private Bitmap BitmapImageToBitmapConverter(BitmapImage bitmapImage)
{
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapImage));
enc.Save(outStream);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);
return new Bitmap(bitmap);
}
}
To later scan each pixel for transparency using this code:
private double TransparencyPercentageInImage(Bitmap image)
{
double transpPercentage;
double transpPixelCount = 0;
double totalPixelCount = image.Height * image.Width;
Console.WriteLine("Total pixel count: " + totalPixelCount);
for (int y = 0; y < image.Height; ++y)
{
for (int x = 0; x < image.Width; ++x)
{
if (image.GetPixel(x, y).A == 0) //or !=255
{
transpPixelCount++;
}
}
}
transpPercentage = transpPixelCount / totalPixelCount * 100;
return transpPercentage;
}
Basically, what should I do to get an accurate transparent pixels percentage/count from a bitmap?
I'm looking for the count of absolutely transparent pixels, not semi-transparent.
I'm not really looking for speed here so any solution goes. I'm already using unsafe code, so that's welcome too.

You neither need a System.Drawing.Bitmap nor a WriteableBitmap to access the pixel values in a BitmapSource.
Just call CopyPixels to get the pixel buffer and count the pixels with an alpha value of 0. First make sure you access the buffer in the desired format:
private double GetTransparentPixelsPercentage(BitmapSource bitmap)
{
if (bitmap.Format != PixelFormats.Bgra32)
{
bitmap = new FormatConvertedBitmap(bitmap, PixelFormats.Bgra32, null, 0);
}
var pixelCount = bitmap.PixelWidth * bitmap.PixelHeight;
var pixels = new byte[4 * pixelCount];
bitmap.CopyPixels(pixels, 4 * bitmap.PixelWidth, 0);
var transparentPixelCount = 0;
for (var i = 3; i < 4 * pixelCount; i += 4) // start at first alpha value
{
if (pixels[i] == 0)
{
transparentPixelCount++;
}
}
return (double)transparentPixelCount / pixelCount;
}

Related

Convert 32bpp to 8bpp UWP

I'm trying to draw InkCanvas to an 8bpp image but when I try to do so the image convert itself to 32bpp, the lower I got was 24bpp but not 8bpp. Anyone can help me out? The image I am giving as input is an 8bpp BMP image created with paint.
Image imgToEdit;
InkCanvas inkCanvas;
file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(Ambiente.imgBlankFirma);
await file.CopyAsync(photoFolder, NomeFile, NameCollisionOption.ReplaceExisting);
file = await photoFolder.GetFileAsync(NomeFile);
imgToEdit = imgFirma;
inkCanvas = inkCanvasFirma;
if (inkCanvas.InkPresenter.StrokeContainer.GetStrokes().Count <= 0)
{
errore = true;
return;
}
var randomAccessStream = await file.OpenReadAsync();
CanvasDevice device = CanvasDevice.GetSharedDevice();
CanvasRenderTarget renderTarget = new CanvasRenderTarget(device, (int)inkCanvas.ActualWidth, (int)inkCanvas.ActualHeight, 96); //inkCanvas.ActualWidth inkCanvas.ActualHeight
using (var ds = renderTarget.CreateDrawingSession())
{
var image = await CanvasBitmap.LoadAsync(device, randomAccessStream);
// draw your image first
ds.DrawImage(image);
// then draw contents of your ink canvas over it
ds.DrawInk(inkCanvas.InkPresenter.StrokeContainer.GetStrokes());
}
randomAccessStream.Dispose();
// save results
using (var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
await renderTarget.SaveAsync(fileStream, CanvasBitmapFileFormat.Tiff, 1f);
}
This shows how to do the conversion by hand. It requires direct pointer access to the image data. So just create a new 8bpp (i.e. Format8bppIndexed) bitmap with the correct size for the conversion target. So converting the data should look something like this:
public static unsafe void Bgr24ToMono8(byte* source, byte* target, int sourceStride, int targetStride, int width, int height)
{
for(var y = 0; y < height; y++)
{
var sourceRow = source + y * sourceStride;
var targetRow = y * targetStride;
for (int x = 0; x < width; x++)
{
var sourceIndex = (sourceRow + x * 3);
var value = (byte)(sourceIndex[0] * 0.11f + sourceIndex[1] * 0.59f + sourceIndex[2] * 0.3f);
target[targetRow + x] = value;
}
}
}
if you have 32bpp data it should just be a issue of changing x * 3 to x * 4. Note that there is some confusion regarding Bgr vs Rbg, different contexts uses different terms for the same thing.
Note that this converts the bitmap to 8bpp grayscale. If you need 8bpp color indexed this will be much more work since you would ideally need to find a optimal color-map, find the closest colors in said map, and apply dithering to avoid banding. For this I would recommend some image processing library. I do not think there is any built in functions for this, and it is way to much work to demonstrate here.

How to convert TIF to Black-and-White Monochrome TIF

I would like to use Magick.NET
https://magick.codeplex.com/wikipage?title=Convert%20image&referringTitle=Documentation
in order to convert TIF to Black-and-White Monochrome TIF but manual does not explain it well.
I have tried this code but I am not sure of this approach is a correct one.
using (MagickImage image = new MagickImage("input.tif"))
{
image.CompressionMethod = CompressionMethod.Group4;
image.Write("output.tif");
}
Help needed. Thank you!
I found .NET code to do the same thing without any issues.
http://www.codeproject.com/Articles/15186/Bitonal-TIFF-Image-Converter-for-NET
private void convert_Click(object sender, EventArgs e)
{
// Load a bitmap from disk
Bitmap originalBitmap = new Bitmap(#"..\..\Bitonal-In.tif");
// Display image
originalImage.Image = originalBitmap;
// Convert bitmap to RGB format for drawing
Bitmap rgbBitmap = Converter.ConvertToRGB(originalBitmap);
// Convert image to bitonal for saving to file
Bitmap bitonalBitmap = Converter.ConvertToBitonal(rgbBitmap);
// Display converted image
convertedImage.Image = bitonalBitmap;
// Get an ImageCodecInfo object that represents the TIFF codec.
ImageCodecInfo imageCodecInfo = GetEncoderInfo("image/tiff");
System.Drawing.Imaging.Encoder encoder = System.Drawing.Imaging.Encoder.Compression;
EncoderParameters encoderParameters = new EncoderParameters(1);
// Save the bitmap as a TIFF file with group IV compression.
EncoderParameter encoderParameter = new EncoderParameter(encoder, (long)EncoderValue.CompressionCCITT4);
encoderParameters.Param[0] = encoderParameter;
bitonalBitmap.Save(#"..\..\Bitonal-Out.tif", imageCodecInfo, encoderParameters);
}
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace BitonalConverter
{
public static class Converter
{
public static Bitmap ConvertToRGB(Bitmap original)
{
Bitmap newImage = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
newImage.SetResolution(original.HorizontalResolution, original.VerticalResolution);
using (Graphics g = Graphics.FromImage(newImage))
{
g.DrawImageUnscaled(original, 0, 0);
}
return newImage;
}
public static Bitmap ConvertToBitonal(Bitmap original)
{
Bitmap source = null;
// 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);
// Create destination bitmap
Bitmap destination = new Bitmap(source.Width, source.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 destination buffer
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 = source.Height;
int width = source.Width;
int threshold = 500;
// Iterate lines
for (int y = 0; y < height; y++)
{
sourceIndex = y * sourceData.Stride;
destinationIndex = y * destinationData.Stride;
destinationValue = 0;
pixelValue = 128;
// Iterate pixels
for (int x = 0; x < width; x++)
{
// Compute pixel brightness (i.e. total of Red, Green, and Blue values) - Thanks murx
// B G R
pixelTotal = sourceBuffer[sourceIndex] + sourceBuffer[sourceIndex + 1] + sourceBuffer[sourceIndex + 2];
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;
}
}
// Copy binary image data to destination bitmap
Marshal.Copy(destinationBuffer, 0, destinationData.Scan0, imageSize);
// Unlock destination bitmap
destination.UnlockBits(destinationData);
// Dispose of source if not originally supplied bitmap
if (source != original)
{
source.Dispose();
}
// Return
return destination;
}
}
}

Render 16 bits image in picturebox

I have an array which consists in PixelData extracted from a Dicom Image.
Here's the code:
byte[] bytes = img.PixelData.GetFrame(0).Data; // img is the Dicom Image
int count = bytes.Length / 2;
ushort[] words = new ushort[count];
for (int i = 0, p = 0; i < count; i++, p += 2)
{
words[i] = BitConverter.ToUInt16(bytes, p);
}
pixels16 = words.ToList(); //pixels16 contains now the PixelData for the Grayscale image
Now, here's my question, how do I render that into a Picturebox??
My code for converting Bitmaps from Format16bppGrayScale to Format8bppIndexed format. PictureBox can easy show this format. (If you want, you can use different palette).
public Bitmap Gray16To8bppIndexed(Bitmap BmpIn)
{
if (BmpIn.PixelFormat != PixelFormat.Format16bppGrayScale)
throw new BadImageFormatException();
byte[] ImageData = new byte[BmpIn.Width * BmpIn.Height * 2];
Rectangle Re = new Rectangle(0, 0, BmpIn.Width, BmpIn.Height);
BitmapData BmpData = BmpIn.LockBits(Re, ImageLockMode.ReadOnly, BmpIn.PixelFormat);
Marshal.Copy(BmpData.Scan0, ImageData, 0, ImageData.Length);
BmpIn.UnlockBits(BmpData);
byte[] ImageData2 = new byte[BmpIn.Width * BmpIn.Height];
for (long i = 0; i < ImageData2.LongLength; i++)
ImageData2[i] = ImageData[i * 2 + 1];
ImageData = null;
Bitmap BmpOut = new Bitmap(BmpIn.Width, BmpIn.Height, PixelFormat.Format8bppIndexed);
BmpData = BmpOut.LockBits(Re, ImageLockMode.WriteOnly, BmpOut.PixelFormat);
Marshal.Copy(ImageData2, 0, BmpData.Scan0, ImageData2.Length);
BmpOut.UnlockBits(BmpData);
ImageData2 = null;
BmpData = null;
ColorPalette GrayPalette = BmpOut.Palette;
Color[] GrayColors = GrayPalette.Entries;
for (int i = 0; i < GrayColors.Length; i++)
GrayColors[GrayColors.Length - 1 - i] = Color.FromArgb(i, i, i);
BmpOut.Palette = GrayPalette;
return BmpOut;
}
Well, I don't know the specifics, because it depends on how you really want to go about it (if performance is important, you need to create your own subclass of Bitmap, but otherwise, Bitmap.SetPixel would work fine).
But essentially, you need to shove those pixels into a Bitmap, then set the picture box's image to that bitmap, like:
Bitmap bitmap = new Bitmap(width, height);
for(int y = 0;y < height;y++)
for(int x = 0;x < width;x++)
bitmap.SetPixel(x,y, Color.fromRGB(/* unpack your R,G,B channel of your pixel here */);
pictureBox.Image = bitmap;
You can utilize the AForge .NET Framework, which is a great .NET library for image processing. The built-in .NET Picturebox could not nativley display images with System.Drawing.Imaging.PixelFormat.Format16bppGrayScale, but the AForge library has its own Picturebox control, check this out. It expects a .NET Image.
You can include AForge to your project easily with NuGet:
Install-Package AForge.Controls
Install-Package AForge.Imaging
Or just
Install-Package AForge
Example code below:
//SOME BYTES
//Load here the DICOM image
int width=640, height=480;
int numberOfPixels = width*height;
byte[] source = new byte[2*numberOfPixels];
//With AFORGE
var image = AForge.Imaging.UnmanagedImage.Create(width, height, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
IntPtr ptrToImage = image.ImageData;
//Copies the bytes from source to the image
//System.Runtime.InteropServices
Marshal.Copy(source, 0, ptrToImage,numberOfPixels);
//WITH .NET
System.Drawing.Bitmap bitmapImage = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
var imageData = bitmapImage.LockBits(new System.Drawing.Rectangle(0, 0, width, height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
Marshal.Copy(source, 0, imageData.Scan0, numberOfPixels);
bitmapImage.UnlockBits(imageData);
Got this idea from a friend. The inputImage.ImageSource property is a 2D array with grayscale pixel values.
Bitmap grayscaleImage = new Bitmap(inputImage.ImageSource);
for (int x = 0; x < grayscaleImage.Width; x++)
{
for (int y = 0; y < grayscaleImage.Height; y++)
{
byte[,] tempMatrix = inputImage.ImageGrayscale;
byte temp = tempMatrix[x, y];
Color tempColor = Color.FromArgb(255, temp, temp, temp);
grayscaleImage.SetPixel(x, y, tempColor);
}
}
picboxDisplay.Image = grayscaleImage;

How to display Grayscale image after changing the indexing in C#

I have a Grayscale image which I am pulling from the DB (it is in Bytes). I want to draw some boxes using graphics object on it and then display this image. This is what was coded before -
public byte[] DrawOverlayOnGreyscaleImage(byte[] buffer, List<ImagingTransaction.ImagingTransactionField> TransactionFieldList, BLLImageType imageType)
{
//Load image into a bitmap object via first going into a MemoryStream
MemoryStream msBitmap = new MemoryStream(buffer);
Bitmap BitmapObj = null;
BitmapObj = new Bitmap(msBitmap);
int bmwidth = BitmapObj.Width;
int bmheight = BitmapObj.Height;
// draw some text on top
Graphics g = Graphics.FromImage(BitmapObj);
Because of the changes in the way the image is now generated (Format8bppIndexed) the Graphics object threw an exception - "A graphics object cannot be created from an image that has an indexed pixel format". So I changed the Bitmap to be Format24bppRGB. Now, there is no exception. But after I draw boxes on the image and try to save it, the image is all black. This is because in case of "Grayscale" images R=G=B. This is lost after making it non indexed. I change the Bitmap to be again Indexed (Format8bbIndexed). Change the ColorPalette, but nothing helps. I still get the image to be totally black. Please help. My new code is as follows -
public byte[] DrawOverlayOnGreyscaleImage(byte[] buffer, List<ImagingTransaction.ImagingTransactionField> TransactionFieldList, BLLImageType imageType)
{
//Load image into a bitmap object via first going into a MemoryStream
MemoryStream msBitmap = new MemoryStream(buffer);
Bitmap BitmapObj = null;
BitmapObj = new Bitmap(msBitmap);
int bmwidth = BitmapObj.Width;
int bmheight = BitmapObj.Height;
Bitmap tmp = new Bitmap(BitmapObj.Width, BitmapObj.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
Graphics g = Graphics.FromImage(tmp);
Rectangle srcRect;
int RectWidth;
int RectHeight;
Pen myPen = new Pen(System.Drawing.Color.Red, 3);
foreach (ImagingTransaction.ImagingTransactionField Field in TransactionFieldList)
{
// first, do they want to see the rectangles
if (imageType == BLLImageType.GreyScale_With_FieldRectangles || imageType == BLLImageType.GreyScale_With_FieldRectangles_And_Field_Data)
{
RectWidth = Field.LowerRightX - Field.UpperLeftX;
RectHeight = Field.LowerRightY - Field.UpperLeftY;
// sanity check for negative values
if (RectWidth <= 0)
RectWidth = 10;
if (RectHeight <= 0)
RectHeight = 10;
srcRect = new Rectangle(Field.UpperLeftX, Field.UpperLeftY, RectWidth, RectHeight);
g.DrawRectangle(myPen, srcRect);
}
// now, do they want to see the text to the lower right of the field
if (imageType == BLLImageType.GreyScale_With_Field_Data || imageType == BLLImageType.GreyScale_With_FieldRectangles_And_Field_Data)
{
g.DrawString(Field.FieldValue, new Font("Tahoma", 12), Brushes.Red, new PointF(Field.LowerRightX, Field.LowerRightY)); ;
}
}
MemoryStream msBitmapWithRectangle = new MemoryStream();
// Save to memory using the Jpeg format
Bitmap tmp2 = new Bitmap(tmp.Width, tmp.Height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
ColorPalette pal = tmp2.Palette;
for (int i = 0; i < pal.Entries.Length; i++)
{
// create greyscale color table
pal.Entries[i] = Color.FromArgb(i, i, i);
}
tmp2.Palette = pal;
tmp2.Save(msBitmapWithRectangle, System.Drawing.Imaging.ImageFormat.Jpeg);
// read to end
byte[] ByteArrayWithRectangle = msBitmapWithRectangle.GetBuffer();
// cleanup
tmp.Dispose();
tmp2.Dispose();
BitmapObj.Dispose();
msBitmap.Close();
msBitmapWithRectangle.Close();
return ByteArrayWithRectangle;
}
Seems that tmp2 is created but never filled with original bitmap, so you create a perfectly black rectangle.
Try creating a new bitmap in the BPP and size you need, and draw the image, and then draw the rectange.

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