Click here to Skip to main content
Click here to Skip to main content

Bitonal (TIFF) Image Converter for .NET

By , 16 Feb 2010
 
Sample Image - BitonalImageConverter.gif

Introduction

The .NET framework provides rich support for generating and manipulating bitmap images, but it lacks one significant feature that is imperative for image processing -- the ability to modify and then save modified bitonal (i.e., black and white or one-bit per pixel) images. Bitonal images are commonly used in document management and document imaging applications for scanned documents. Bitonal images are most commonly stored in the TIFF (Tagged Image File Format) file format with a CCITT Group IV compression algorithm.

The Problem

The .NET Framework supports loading and displaying bitonal images, but that's where the support ends. All drawing in .NET requires a Graphics object, but a Graphics object cannot be created from a bitonal image. Go ahead and try it now if you don't believe me. I'll wait...

Here's some code that demonstrates the issue (assuming Bitonal-In.tif is a bitonal image):

Bitmap originalBitmap = new Bitmap(@"Bitonal-In.tif");
Graphics g2 = Graphics.FromImage(originalBitmap);

This code will generate an "A Graphics object cannot be created from an image that has an indexed pixel format." exception, thereby thwarting our desire to modify our bitonal image. I know, I couldn't believe it either.

The Solution (Almost)

We can work around the previous exception by converting the bitonal image into an RGB (Red/Green/Blue) bitmap for modification. The Converter class included with this article contains a static method, ConvertToRGB, for doing so. The code for this method is as follows:

public static Bitmap ConvertToRGB(Bitmap original)
{
    Bitmap newImage = new Bitmap(original.Width, original.Height, 
                                 PixelFormat.Format32bppArgb);
    newImage.SetResolution(original.HorizontalResolution, 
                           original.VerticalResolution);
    Graphics g = Graphics.FromImage(newImage);
    g.DrawImageUnscaled(original, 0, 0);
    g.Dispose();
    return newImage;
}

This gives us a bitmap we can use to create a Graphics object and modify the image, so all is well with the world once again. Well... almost, but not quite.

A New, But Different Problem

We can now happily modify and display our image all day long (albeit with the larger memory footprint of a 32 bit per pixel RGB image), but we are thwarted once again if we wish to save our modified image back to disk in a bitonal format. The following code will generate a "Parameter is not valid." exception when attempting to save our 32 bit RGB image back to a bitonal format.

// 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);

The problem arises as a result of the .NET framework's inability to encode an RGB image into a bitonal file format. It is the primary intent of this article to address this issue.

The Solution (I Really Mean It This Time)

While the .NET framework does indeed support saving bitonal images, it provides no means for converting an RGB image into a bitonal image, which is the crux of the problem. We can't use the same method used to go from bitonal to RGB because we can't create a new bitonal image and get a Graphics object to draw on it. We must resort to something completely different -- direct image byte manipulation (Aaahh!!! Did he just say that !??).

While it is beyond the scope of this article to dig into the memory structure of bitmaps, I will mention briefly the task at hand. 32-bit RGB bitmaps use four bytes of memory for each pixel (picture element) in the bitmap. One byte each is used for the Red-ness, Green-ness, and Blue-ness of the pixel, and one byte is used to represent the Alpha (or transparency) of the pixel. The RGB value of 255-255-255 represents white, and a value of 0-0-0 represents black. Bitonal images, on the other hand, use a single bit to represent each pixel in the image, and eight pixels are packed into each byte of memory used to represent the image.

The BitmapData class in .NET provides a LockBits method, which gives us direct access to the image bytes for a bitmap. We can use this method to retrieve the image bytes for an existing image into a byte[], modify the image bytes, and then write the image bytes back to the bitmap, thus modifying the bitmap. To convert an RGB bitmap into a bitonal bitmap, we proceed as follows:

  1. Copy the image bytes for the original RGB image into a byte array.
  2. Create a new, bitonal image with the same dimensions as the source image.
  3. Create a byte array of the necessary size to contain the bits for the bitonal image.
  4. Walk the pixels in the source data, and set the appropriate bit in the destination data if the sum of the red, green, and blue values exceeds a certain threshold.
  5. Copy the destination byte array back to the new bitonal bitmap.

Comments

While searching for a solution to this problem, I came across other snippets to do this type of conversion, but they all suffered from the same problem: They were S....L....O....W..... The method provided with this article performs the conversion of a typical 300 DPI (Dot Per Inch) image on my machine (a 3 gigahertz P4) in about 100 milliseconds. While a lot of C# imaging applications resort to pointer arithmetic and unsafe code blocks, the code in this article is hereby deemed Completely Safe and does not need to resort to such medieval methods.

Room For Improvement

The RGB to bitonal conversion method provided with this article performs a threshold type conversion of the image in which a destination pixel is either black or white depending on the brightness of the pixel in the source image. One beneficial improvement to the code would be the addition of half-toning or dithering algorithms to produce a higher quality output from color images. The method provided with this article was written for use in document imaging applications in which the source image is already a bitonal image, so this was not a necessity for my purposes, but I see how it could prove useful if this code was to be used to produce bitonal images from color images with a wider variety of source colors.

Sample Project

The sample project included with this project is a Windows Forms application that utilizes the Converter class to convert an existing bitonal image to RGB, draw some text on it, then save it back to disk in bitonal format. The project contains the minimal amount of code necessary to demonstrate the technique.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Michael A. McCloskey
Web Developer
United States United States
Member
I have been a "true blue" Microsoft developer since the earliest DOS days. I have coded in Turbo-C for DOS, VB 2->6, VB.NET, and C#. I enjoy graphics coding when I have the time, but tend to earn my living doing data layer and middle tier stuff. I love it all and only regret there's not enough time in the day to know everything about everything.
 
I am currently working as a software architect in Atlanta, Georgia and when I'm not coding, I'm pondering or reading anything that helps increase my understanding of the meaning of life and the nature of the universe, or at least my understanding of the .NET framework class library.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberveggiemanuk5 Jun '12 - 9:23 
What can I say other than outstanding?
 
I only needed the first method to solve my problem as I only need to display images and not truly convert them.
 
Cheers.
QuestionThis example not workingmemberMember 302199712 Dec '11 - 22:11 
Hi,
I tried this example for my 200 dpi, Black and White Tiff image, but it throws an Exception on statement
 
Bitmap original = New Bitmap(strSrcFileName);
 
an Exception is "Invalid Parameters".
Questionconversion to base64 stringmemberDaveeeeeeeeee1 Dec '11 - 2:19 
Hi Micheal
 
Great Article, well written as well as well coded.
 
Quick question.
 
I am currently working on a project which requires TIFF images to be converted into bitonal format before being read into a base64 string.
 
I have it working using your methodolgy to generate the bitonal file and the MSDN method of reading a file into a byte array and then into a base 64 string.
 
However this requires writing the file out and back in, what I am trying to achive is combining both functions in memory (byte Arrays, steams) however it seams that the Codec and Compression details are not being corporated correctly.
 
...and the question - should it be possible to combine the bitonal conversion with the base64 conversion without writing out to file and if so do you know what I am missing.
 
Dave
AnswerRe: conversion to base64 stringmemberMichael A. McCloskey1 Dec '11 - 2:59 
I modified the example code below to read a file into a byte array, convert to base64, then convert back to a byte array, create a memory stream and create a bitmap from the stream. On the output side I show how to save directly to a memory stream and convert the bytes into base 64. I hope this helps.
 
// Load a bitmap from disk
// Bitmap originalBitmap = new Bitmap(@"..\..\Bitonal-In.tif");

// Read TIFF into byte array
byte[] bytes = File.ReadAllBytes(@"..\..\Bitonal-In.tif");
 
// Convert to base 64
string base64 = Convert.ToBase64String(bytes);
 
// Convert back to byte array
bytes = Convert.FromBase64String(base64);
 
// Create memory stream
MemoryStream ms = new MemoryStream(bytes);
 
// Create bitmap from stream
Bitmap originalBitmap = new Bitmap(ms);
 
// Display image
originalImage.Image = originalBitmap;
 
// Convert bitmap to RGB format for drawing
Bitmap rgbBitmap = Converter.ConvertToRGB(originalBitmap);
 
// Get a graphics object for drawing on bitmap
using (Graphics g = Graphics.FromImage(rgbBitmap))
{
    // Create a font to use for drawing text
    using (Font f = new Font("Arial", 1.0f, GraphicsUnit.Inch))
    {
        // Draw text
        g.DrawString("Test Imprint", f, Brushes.Black, 500, 1200);
    }
}
 
// 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);

// Write to memory stream
ms = new MemoryStream();
bitonalBitmap.Save(ms, imageCodecInfo, encoderParameters);
 
// Convert to base64 string
base64 = Convert.ToBase64String(ms.ToArray());

GeneralRe: conversion to base64 stringmemberDaveeeeeeeeee1 Dec '11 - 4:08 
Micheal,
 
Thanks a million, code works a treat,
 
It looks like the "convert to base64, then convert back to a byte array" is the missing link and I know I would never have come up with it if I lived to be 100
 
Again Thanks
 
Dave
GeneralRe: conversion to base64 stringmemberMichael A. McCloskey1 Dec '11 - 13:44 
Smile | :) Glad to be of service.
GeneralRe: conversion to base64 stringmemberafcm66620 Mar '13 - 4:47 
Hi, I just need convert the bitmap image to byte[], because I save it in DB. In Windows XP I got the problem "The parameter is not valid" but now with this code is working fine. I pase my code if someone need it.
 
private byte[] EncodeImageAlternative(Bitmap image)
        {
            try
            {
               
               
                // Create bitmap from stream
                Bitmap originalBitmap = image;
 
                // Display image
                //image.Image = originalBitmap;
 
                // Convert bitmap to RGB format for drawing
                Bitmap rgbBitmap = BitonalConverter.Converter.ConvertToRGB(originalBitmap);
 
                // Get a graphics object for drawing on bitmap
                using (Graphics g = Graphics.FromImage(rgbBitmap))
                {
                    // Create a font to use for drawing text
                    using (Font f = new Font("Arial", 1.0f, GraphicsUnit.Inch))
                    {
                        // Draw text
                        g.DrawString("Test Imprint", f, Brushes.Black, 500, 1200);
                    }
                }
 
                // Convert image to bitonal for saving to file
                Bitmap bitonalBitmap = BitonalConverter.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);
 
                // Write to memory stream
                using (MemoryStream ms = new MemoryStream())
                {
                    bitonalBitmap.Save(ms, imageCodecInfo, encoderParameters);
 
                    // Convert to base64 string
                    return ms.ToArray();
                }
            }
            catch (Exception ex)
            {
                string strError = string.Empty; Process.Utility.CustomInsertLoggingAudit(ex, out strError);
                MessageBox.Show(LanguageResources.GetStringFromResources("ExceptionHandling_Message") + " (" + strError + ")", LanguageResources.GetStringFromResources("Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

Questionusing TIFF with 2400 dpi?memberMember 46061351 Nov '11 - 3:32 
Before i try your Solution.
Did you ever try using very big Images with 2400 dpi and 27 Inches ?
I am looking for a Solution loading a 1bit tiff lwz, inserting a Text Info and barcode Image, and saving into a 1bit tiff lwz back.
Thanks for your answer.
Best regards.
Bertrand Cadrot

AnswerRe: using TIFF with 2400 dpi?memberMichael A. McCloskey1 Nov '11 - 5:57 
First thought --- Yikes! OMG | :OMG:
 
If my math is correct, a 2400dpi 27" square image would require about 524 meg of memory in 1 bit per pixel mode. Converted to an RGB format for drawing using a .NET Graphics object would require on the order of 12 or 16 gigs of memory, well beyond the bounds of something a sane person would attempt, in my opinion.
 
If I were to be handed this task and wanted to use the general techniques written about here, I would probably do something like the following.
 
- Determine if .NET can load the bitonal image in the first place. Assuming a square image, this alone will require on the order of 524 meg of memory.
- I would then use the LockBits method to obtain a byte array for the image data.
- Converting the entire source image to RGB for drawing is ill-advised, so I would create an image just large enough to render what I wanted to draw on the main image. Hopefully this is a much smaller area than the main image. I would render what I wanted to overlay on the main image into this image and then convert this sub-image into a bitonal image using the techniques discussed.
- I would then get a byte array for the sub-image using lock bits and start manually compositing the smaller image onto the larger image. This would require computing the offset into the byte arrays of the main image and sub-image, then walking the bytes and merging the bytes from the sub-image into the main image directly rather than trying to use drawing function.
 
This technique would still allow you to use .NET drawing functions on the smaller image, but you would need to do some direct manipulation of the raw image data to overlay the drawn image onto the original image.
 
I hope I made sense here and this helps. I also hope your computer has lots and lots of RAM.
QuestionHelp!memberimran_akh18 Jul '11 - 2:15 
I am using the GDI+ library to draw into a bitmap (width:3492 x height:2481) @ 300 dpi for both horizontle and vertical resolution. makes almost an A4 paper.
 
The bitmap works fine if i save it as tiff without any encoders.
 
my task is to save my image as 1 bit tiff.
 
but for some reason when i convert using your function ConvertToBitonal the output bitmap is all black.
 
for Info: when i save my bitmap normally as tiff following are the specs
Dimension: 3492x2481
resolution: 300x300
bit depth: 32
compression: LZW
 

Thank you.
AnswerRe: Help!memberMichael A. McCloskey18 Jul '11 - 3:22 
The code uses a threshold value for the source pixel to determine if the output pixel should be white or black and doesn't do any sort of dithering or color/grayscale to bitonal conversion. If your source image is either a color or grayscale image and all the pixels are below the threshold, then you would get a solid black image. If your image is already black and white and not color or grayscale and the conversion is resulting in a solid black result, then it will be difficult to diagnose without looking at the image data. It's now been quite a while since I've worked with this code or imaging in general, but if you're working with a color/grayscale image, then you'll have to dither it or use some other sort of conversion to get it into just black and white pixels before this method will work to save it was a bitonal (1-bit) TIFF.
GeneralRe: Help!memberimran_akh18 Jul '11 - 6:06 
Thank you for a quick reply.
My image is already black and white. its actually text data with lines and grids produced through GDI+.
The point is i am not even writing the image to file. I am passing your function BITMAP object of the System.Drawing.Bitmap class. and since i cant DIRECTLY save it as CCIT4 @ 1bit , I am trying to use your technique.
The total pixel value where you compare the RGB always return as zero.
GeneralRe: Help!memberMichael A. McCloskey18 Jul '11 - 7:35 
I'm happy to try to help figure out what's going on and why it's not working for you as it should. I understand that you're passing in a bitmap object generated in code and that if you save that bitmap to a file directly, it looks right, but if you first convert it with my code, then it comes out all black. If the pixelTotal that results from adding up the RGB values for each pixel is always zero, then it sounds like the buffer doesn't contain your image for some reason or the LockBits function isn't returning good data or something like that. It's hard to debug without being able to step through the code and see what the data looks like. One thing I would try, for debugging purposes, is save the generated bitmap out to a file without encoding and make sure it looks right, then read the file in and send it through the function and see if you get the same result. If you do, then perhaps you could send me the BMP file and I would have something more to go on for debugging.
GeneralRe: Help!memberImran Akhtar 9918 Jul '11 - 10:45 
I figured that the problem is not with what i am doing to the bitmap but seems inherent with any bitmap. So i created a simple project and write one line to the bitmap using Graphics.
 
I have two picture boxes PictureBox1 and PictureBox2 just to show the image, you can skip that and check out the result through files.
 
I am using MS VS 2010 on 64BIT WINDOWS 7.
 

//#########################################
//USING A STRUCTURE FOR MARGINS
//#########################################
public struct MarginBounds
{
public static int X = 25;
public static int Y = 35;
public static int Top = 35;
public static int Bottom = 2455;
public static int Left = 25;
public static int Right = 3465;
public static int Height = 2455;
public static int Width = 3465;
}
 
private static ImageCodecInfo getEncoderInfo(string mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for (j = 0; j < encoders.Length; ++j)
{
if (encoders[j].MimeType == mimeType)
return encoders[j];
}
return null;
}
 

private void generateImage()
{
Bitmap b = new Bitmap(3492, 2481);
b.SetResolution(300, 300);
 
Graphics printer = Graphics.FromImage(b);
 
StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Near;
stringFormat.LineAlignment = StringAlignment.Center;
 
// Draw Block code
Font font = new Font("Arial", 10);
SizeF pSize = printer.MeasureString("TESTING", font, MarginBounds.Width, stringFormat);
printer.DrawString("TESTING", font, new SolidBrush(System.Drawing.Color.Black), new Rectangle(MarginBounds.Left, Top, MarginBounds.Width, (int)pSize.Height), stringFormat);
 
b.Save("c:\\test\\testing.tiff", System.Drawing.Imaging.ImageFormat.Tiff);
pictureBox1.Image = b;
 
Bitmap bitonal = Converter.ConvertToBitonal(b);
pictureBox2.Image = bitonal;
 
ImageCodecInfo tiffCodec = getEncoderInfo("image/tiff");
System.Drawing.Imaging.Encoder compressionEncoder = System.Drawing.Imaging.Encoder.Compression;
System.Drawing.Imaging.Encoder depthEncoder = System.Drawing.Imaging.Encoder.ColorDepth;

EncoderParameters params1 = new EncoderParameters(2);
 

EncoderParameter param1 = new EncoderParameter(compressionEncoder, (long)EncoderValue.CompressionCCITT4);
EncoderParameter param2 = new EncoderParameter(depthEncoder, 1L);
 
params1.Param[0] = param1;
params1.Param[1] = param2;
bitonal.Save("c:\\test\\temp1Bit.tif", tiffCodec , params1);
}
 

 
and thank you for all your support.
 
Regards
GeneralRe: Help!memberImran Akhtar 9918 Jul '11 - 10:57 
while posting i just had a thought. Can it be because the bitmap has no background? It is transparent instead of white.
GeneralRe: Help!memberImran Akhtar 9918 Jul '11 - 11:12 
I guess discussion does produce new ideas. It was the transparency of the bitmap. i filled it with white color and then printed on it, solved the problem.
 
Thank you Micheal for your time and all your help!
 
Regards Smile | :)
GeneralRe: Help!memberMichael A. McCloskey18 Jul '11 - 11:28 
Excellent! I'm sure I probably experienced something similar with dynamically generating images somewhere along the line, so I feel your pain. I'm very happy to hear you found the solution and bouncing ideas back and forth helped. I can't say I did anything, but you're welcome for my moral support. Smile | :)
QuestionHelp needed to convert the tiff images into bitonalmemberSrikanthRN4 Oct '11 - 12:05 
I have a tiff image which I believe is already bitonal. I want to make it lighter or gray(where the black areas have been speckeled so they look gray). This will allow us to see through the data even if the alignment is not correct.
GeneralGreat Article... Thanksmembermeeram3952 May '11 - 22:47 
Thank you for providing a nice article. I was actually searching for one like this.
 
I have one query... What if the tiff has multipages? I mean how to convert the multipage tiff to bmp? The same compression technique (CCITT-4) needs to be maintained in all the pages of each file. How to achieve that?
Success is the good fortune that comes from aspiration, desperation, perspiration and inspiration.

GeneralRe: Great Article... ThanksmemberMichael A. McCloskey16 May '11 - 2:56 
It has been a while since I have worked with multipage TIFF's, but I found the following old snippet of code I used to split a multipage TIFF into separate files.
 
public static void Split(string inputFile)
{
// Open TIFF file
Bitmap bitmap = new Bitmap(inputFile);
 
// Create output filename template
FileInfo finfo = new FileInfo(inputFile);
string directory = finfo.DirectoryName;
string baseFilename = finfo.Name.Substring(0, finfo.Name.Length - finfo.Extension.Length);
 
// Get framecount (pages)
int pageCount = bitmap.GetFrameCount(FrameDimension.Page);
 
// Export pages
for(int i = 0; i < pageCount; i++)
{
bitmap.SelectActiveFrame(FrameDimension.Page, i);
bitmap.Save(Path.Combine(directory, string.Format("{0}-{1:0000}.tif", baseFilename, i + 1)),
ImageFormat.Tiff);
}
 
// Dispose of bitmap
bitmap.Dispose();
}
 
The GetFrameCount method gives you the number of pages and the SelectActiveFrame sets the page you want to work with. I think you may be able to use these calls to modify a multipage TIFF in place. Hopefully this will help.
GeneralCreating a new bitonal bitmap from an existing...memberMightyZot11 Apr '11 - 9:46 
static public class BitonalBitmap
{
static BitonalBitmap() { }

static public Bitmap FromBitmap(Bitmap source)
{
BitmapData data = null;
try
{
data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
return new Bitmap(data.Width, data.Height, data.Stride, PixelFormat.Format1bppIndexed, data.Scan0);
}
finally
{
if (data != null) { source.UnlockBits(data); }
}
}
}
GeneralVB.NET Versionmemberbryonmvi16 Mar '11 - 13:25 
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices
 
'****************************
'* http://www.codeproject.com/KB/GDI-plus/BitonalImageConverter.aspx
'* Licensed under The Code Project Open License (copy must be included with code if code is redistributed - CPOL.html)
'****************************
 
Public Class Converter
 
Public Shared Function ConvertToRGB(ByRef original As Bitmap) As Bitmap
Dim newImage As Bitmap = New Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb)
 
newImage.SetResolution(original.HorizontalResolution, original.VerticalResolution)
Using g As Graphics = Graphics.FromImage(newImage)
g.DrawImageUnscaled(original, 0, 0)
End Using
 
Return newImage
End Function
 
Public Shared Function ConvertToBitonal(ByRef original As Bitmap) As Bitmap
Dim source As Bitmap = Nothing
 
' If original bitmap is not already in 32 BPP, ARGB format, then convert
If original.PixelFormat <> PixelFormat.Format32bppArgb Then
source = New Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb)
source.SetResolution(original.HorizontalResolution, original.VerticalResolution)
Using g As Graphics = Graphics.FromImage(source)
g.DrawImageUnscaled(original, 0, 0)
End Using
Else
source = original
End If
 
' Lock source bitmap in memory
Dim sourceData As BitmapData = source.LockBits(New Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
 
' Copy image data to binary array
Dim imageSize As Integer = sourceData.Stride * sourceData.Height
Dim sourceBuffer(imageSize - 1) As Byte
Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, imageSize)
 
' Unlock source bitmap
source.UnlockBits(sourceData)
 
' Create destination bitmap
Dim destination As Bitmap = New Bitmap(source.Width, source.Height, PixelFormat.Format1bppIndexed)
destination.SetResolution(original.HorizontalResolution, original.VerticalResolution)
 
' Lock destination bitmap in memory
Dim destinationData As BitmapData = destination.LockBits(New Rectangle(0, 0, destination.Width, destination.Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed)
 
' Create destination buffer
imageSize = destinationData.Stride * destinationData.Height
 
Dim destinationBuffer(imageSize - 1) As Byte
Dim sourceIndex As Integer = 0
Dim destinationIndex As Integer = 0
Dim pixelTotal As Integer = 0
Dim destinationValue As Byte = 0
Dim pixelValue As Integer = 128
Dim height As Integer = source.Height
Dim width As Integer = source.Width
Dim threshold As Integer = 500
 
' Iterate lines
For y As Integer = 0 To height - 1
 
sourceIndex = y * sourceData.Stride
destinationIndex = y * destinationData.Stride
destinationValue = 0
pixelValue = 128
 
' Iterate pixels
For x As Integer = 0 To width - 1
' Compute pixel brightness (i.e. total of Red, Green, and Blue values) - Thanks murx
' B G R
pixelTotal = CInt(sourceBuffer(sourceIndex)) + CInt(sourceBuffer(sourceIndex + 1)) + CInt(sourceBuffer(sourceIndex + 2))
If (pixelTotal > threshold) Then
destinationValue += CType(pixelValue, Byte)
End If
If pixelValue = 1 Then
destinationBuffer(destinationIndex) = destinationValue
destinationIndex += 1
destinationValue = 0
pixelValue = 128
Else
pixelValue >>= 1
End If
sourceIndex += 4
Next
If pixelValue <> 128 Then
destinationBuffer(destinationIndex) = destinationValue
End If
Next
 
' 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 Not Object.ReferenceEquals(source, original) Then
source.Dispose()
End If
 
Return destination
End Function
 
End Class
GeneralMy vote of 5memberMember 307909830 Nov '10 - 22:05 
Good work michael.
Keep it UP!!!
GeneralSaving the converted bitonal imagememberSaransh Chintha25 Oct '10 - 9:33 
Hi,
 
First of all, this is a great tool and I appreciate the effort you have put in.
 
I tried converting a grayscale tiff image (in the CCITT Group IV compression format) to a bitonal image and was able to save it too, it works great but when I check the image compression format it is using the LZW compression and not the original CCITT Group IV format.
 
Can you please confirm and provide any suggestions for saving it with the CCITT Group IV format?
 
Regards,
Saransh.
GeneralRe: Saving the converted bitonal imagememberMichael A. McCloskey25 Oct '10 - 11:52 
I'm afraid I don't understand. The example code that comes with the project saves the result out to a CCITT Group IV Format. It's important to use the encoder parameters to trigger the codec to use the CCITT4 format.
 
The code is shown below:
 
// 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);

GeneralThanks!memberTomDratler30 Aug '10 - 18:16 
Great job! Very well written and organized code. I was able to plug it right into my app without any problems at all.
 
-Tom
GeneralRe: Thanks!memberMichael A. McCloskey1 Sep '10 - 2:58 
Thanks for the kind words. I'm glad it helped you out.
GeneralGood workmembermsp.netdev10 May '10 - 11:03 
ConvertToBitonal() is solution to my problem, thanks Smile | :)
 
msp.netdev
GeneralTurbo-CmemberStevenHenning25 Feb '10 - 5:28 
Hey Michael, that brings back memories. I used to also use Turbo-C on a Toshiba laptop which had to stiffy drives, one with the program disk and the other had your source code on it. Thanks for the article, very interesting.
Steve
Generalbyte order !membermurx15 Feb '10 - 10:41 
You might have wondered why your images come out so pale. You add up GRA instead of RGB and A is most likely 255. Replace your code:-
 
// Compute pixel brightness (i.e. total of Red, Green, and Blue values)
pixelTotal = sourceBuffer[sourceIndex + 1] + sourceBuffer[sourceIndex + 2] + sourceBuffer[sourceIndex + 3];
 
with:-
 
// Compute pixel brightness (i.e. total of Red, Green, and Blue values)
//                           B                             G                              R
pixelTotal = sourceBuffer[sourceIndex] + sourceBuffer[sourceIndex + 1] + sourceBuffer[sourceIndex + 2];
 
Though we are used to read and write our numbers with the hi-order digits to the left, at least in windows world it's mostly the other way round.
 
Cool | :cool:
GeneralRe: byte order !memberMichael A. McCloskey15 Feb '10 - 14:05 
Thanks for the catch. I'm working on a patch now.
GeneralRe: byte order !memberMichael A. McCloskey17 Feb '10 - 1:12 
The article code has been updated accordingly. Thank you.
Questionhow to get data from the tags of a TIFF image [modified]membergilvani11 Aug '09 - 2:55 
Getting the data from the tags of a TIFF image
I'm trying to make a program in C# that opens a TIFF image and puts the data from the tags in texboxs but I do not know how to extract the data. If anyone knows how can help me.
thanksConfused | :confused:
 
modified on Tuesday, August 11, 2009 9:06 AM

AnswerRe: how to get data from the tags of a TIFF imagememberShayne P Boyer9 Mar '10 - 2:07 
here is a routine that I use to copy tags from one tif to another:
 
private static void CopyTags(Bitmap source, Bitmap destination)
{
destination.SetResolution(source.HorizontalResolution, source.VerticalResolution);
foreach (System.Drawing.Imaging.PropertyItem pi in source.PropertyItems)
{
destination.SetPropertyItem(pi);
}
}
GeneralThank you!memberdaelin24 Apr '09 - 4:47 
I needed something to insert a line of text (electronic audit trail) on images that come through. The documents are bi-tonal TIFFs coming in, but I was having a difficult time trying to write onto the image and then save the bi-tonal TIFF out.
 
Thanks!
 
Lauren
GeneralRe: Thank you!memberMichael A. McCloskey24 Apr '09 - 11:14 
You are quite welcome. I'm glad it helped you out.
GeneralRe: Thank you!memberMember 26480075 May '09 - 23:54 
thanks for sharing this. really helped me out!
 
Brendan
GeneralBug with the convertermemberchucklepie31 Mar '09 - 3:30 
Hello,
In your line of code:
source = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
 
It saves the image as 96dpi regardless of the actual dpi of the image, resulting in image viewers/libraries rendering it incorrectly. If you add:
 
source.SetResolution(original.HorizontalResolution, original.VerticalResolution);
 
immediately afterwards, all is well, i.e. when saving a 200dpi image, it used to class it as 96dpi, when with the code above it saves it as 200dpi.
GeneralRe: Bug with the convertermembermurx2 Feb '10 - 9:03 
... and of course we need to apply the same to the destination bitmap:
 
// Create destination bitmap
     Bitmap destination = new Bitmap(source.Width, source.Height, PixelFormat.Format1bppIndexed);
     destination.SetResolution( original.HorizontalResolution, original.VerticalResolution ); // <-- inserted

GeneralRe: Bug with the convertermemberMichael A. McCloskey15 Feb '10 - 14:12 
Thank you. I'm putting in a fix for this.
GeneralRe: Bug with the convertermemberMichael A. McCloskey17 Feb '10 - 1:13 
The code has been updated with this change. Thank you.
Questiontiff image controlling using asp.net with C# codingmemberSumesh N24 Sep '08 - 19:01 
How can I contol a tiff image like edit,create delete it using
GeneralToo difficultmemberJiri Stybnar10 Dec '07 - 11:47 
1. Code below draws any bitmap correctly, including 1bpp bitmap:
Graphics g = pictBox.CreateGraphics();
Bitmap bitmap = new Bitmap("...");
e.Graphics.DrawImage(bitmap);

2. To edit 1bpp bitmap, create new 1bpp Bitmap with the same size, select black pixels by using BitmapData.Scan0 and pointers, and apply new bitmap by using AND operator to the first bitmap. Then, Bitmap.Save() works fine.
Questionprinter spooler size biggermemberJwalant Natvarlal Soneji29 Aug '07 - 22:07 
Dear sir,
thanks for the great code!
I have been merging multiple tiff images in to one with this code.
But when I try to print the result document of say 5 mb, it takes 150mb in the spooler.
what to do?
 

AnswerRe: printer spooler size biggermemberMichael A. McCloskey31 Aug '07 - 8:38 
I'm glad you are finding the code useful. I'm afraid I can't be of much help with the printing. I suspect that when the image is sent to the print spooler, it is getting uncompressed to the full resolution of the printer, perhaps as an RGB image. A 600 dpi RGB image, at an 8.5" x 11" size will take up a lot of memory.
 
For example:
 
((8.5" x 600dpi) x (11" x 600dpi)) * 3 bytes per pixel = 100,980,000 bytes (or about 100 meg)
 
One possible solution that comes to mind is seeing if you can drop down the printer resolution to 300 dpi if you don't need to print at a high resolution.
 
Another solution might be to reduce the size of the bitmap itself if you don't need a high resolution.
 
I'm not sure which problem is affecting you, these are just my thoughts and hopefully useful advice.
 
Best Regards,
Michael McCLoskey
QuestionRe: printer spooler size biggermemberJwalant Natvarlal Soneji2 Sep '07 - 22:14 
Dear Michael McCLoskey Sir,
Thanks for the information.
The origional files are with 200dpi and the new file comes with 96dpi, but the size is almost 12 times the original files total size.
And still facing the same problem with printing.
The print spool size goies to 152mb.
Thanks,
 

GeneralThanks! Here's a URL to FAX program, with dithering.memberEricp5621 May '07 - 11:19 
I have placed simple a C# 2005 command-line project on my website. It's an easy way to fax dynamic reports.
 
Link to the blog page.
 
If you have FaxComEx.dll, you can simply provide the URL, FAX number and FAX Server name.
 
If you don't, you can provide a URL, and stdout will provide the generated TIFF name.
 
I do some basic random dithering that doesn't eat up too many cycles - good enough for my project!
 

 


GeneralRe: Thanks! Here's a URL to FAX program, with dithering.memberMichael A. McCloskey21 May '07 - 12:14 
Very cool stuff! I'm very happy to see my code put to good use. Thanks for the credit. I might have call to use that dithering code you added at some point. I hope you don't mind. Smile | :)
GeneralRe: Thanks! Here's a URL to FAX program, with dithering.memberabimage15 May '13 - 17:26 
the codes for image converter for .net is great, i hope i can come up with that.
GeneralResolution & Size problemmembertushar_pk19 May '07 - 4:41 
When I use Bitonal code output image resolution is 120 X 120 dpi instead of 300 x 300 dpi and also size of image is 27.50" and 21.33" instead of 11.00" x 8.5". Please advice me what change I need in code to get mention result. Thanks for nice code.Smile | :)

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 16 Feb 2010
Article Copyright 2006 by Michael A. McCloskey
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid