47

How can i convert an array of bitmaps into a brand new image of TIFF format, adding all the bitmaps as frames in this new tiff image?

using .NET 2.0.

mirezus
  • 13,892
  • 11
  • 37
  • 42

7 Answers7

85

Start with the first bitmap by putting it into an Image object

Bitmap bitmap = (Bitmap)Image.FromFile(file);

Save the bitmap to memory as tiff

MemoryStream byteStream = new MemoryStream();
bitmap.Save(byteStream, ImageFormat.Tiff);

Put Tiff into another Image object

Image tiff = Image.FromStream(byteStream)

Prepare encoders:

var encoderInfo = ImageCodecInfo.GetImageEncoders().First(i => i.MimeType == "image/tiff");

EncoderParameters encoderParams = new EncoderParameters(2);
encoderParams.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionNone);
encoderParams.Param[1] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.MultiFrame);

Save to file:

tiff.Save(sOutFilePath, encoderInfo, encoderParams);

For subsequent pages, prepare encoders:

EncoderParameters EncoderParams = new EncoderParameters(2);
EncoderParameter SaveEncodeParam = new EncoderParameter(
     Encoder.SaveFlag, 
     (long)EncoderValue.FrameDimensionPage);
EncoderParameter CompressionEncodeParam = new EncoderParameter(
     Encoder.Compression, (long)EncoderValue.CompressionNone);
EncoderParams.Param[0] = CompressionEncodeParam;
EncoderParams.Param[1] = SaveEncodeParam;
tiff.SaveAdd(/* next image as tiff - do the same as above with memory */, EncoderParams);

Finally flush the file:

EncoderParameter SaveEncodeParam = new EncoderParameter(
     Encoder.SaveFlag, (long)EncoderValue.Flush);
EncoderParams = new EncoderParameters(1);
EncoderParams.Param[0] = SaveEncodeParam;
tiff.SaveAdd(EncoderParams);

That should get you started.

SSS
  • 4,807
  • 1
  • 23
  • 44
Otávio Décio
  • 73,752
  • 17
  • 161
  • 228
  • 4
    Excellent answer. Could only be more complete if you describe how you arrived at it (where you learned it if not from trial and error) since the MSDN docs make it next to impossible to understand. – Peter T. LaComb Jr. Jun 16 '09 at 19:47
  • What I was wondering about is the use of ImageFormat.Tiff in MemoryStream's Save method - obviously "encoding" done later is a totally unrelated process - i.e. dumping the bytes of the memory stream that was saved using ImageFormat.Tiff into a file will not create a TIFF file, i suppose? – hello_earth Jan 13 '11 at 11:36
  • 7
    @alabamasucks - It's a function that returns an ImageCodecInfo object for the specified mime type. It can be replaced with: ImageCodecInfo.GetImageEncoders().First(i => i.MimeType == "image/tiff"); – Seth Reno Jun 01 '11 at 16:06
  • Usign that method above, the first page of my file is being saved black. – Random May 22 '12 at 09:32
  • 2
    I had the same black image issue, turns out I was closing the bitmap stream before I'd called write on the tiff. – aboy021 Feb 15 '13 at 02:28
  • This will not work on win server 2003 EncoderValue.CompressionCCITT4 produces error. – Dexters Mar 05 '13 at 06:41
  • 2
    It converts image to TIFF but the output file is Black&White. any reason why? – Chirag Oct 06 '15 at 07:28
  • 3
    Excellent answer. I would like to add that the GetEncoderInfo() method is documented on MSDN at https://msdn.microsoft.com/en-us/library/system.drawing.imaging.encoder.compression(v=vs.110).aspx and Encoder.SaveFlag may need to be fully qualified -- System.Drawing.Imaging.Encoder.SaveFlag. – Dan7el Jan 08 '16 at 23:18
  • 1
    @Chirag i changed my compression flag to EncoderValue.CompressionNone and then i didn't lose color – JDPeckham Jul 14 '17 at 16:02
  • 1
    Per @JDPeckham I changed the flag to CompressionNone to resolve the black&white issue – SSS May 14 '19 at 02:13
  • It's the throws an error while working with more than 50,000 files having 1mb size. – Hiren Patel Sep 30 '19 at 08:37
  • Don't forget to wrap memory objects and images in using clause. – Pratap Singh Mehra Aug 06 '20 at 10:42
23

Came across this post after a bit of searching on Google. I tried the code that was in the post by a'b'c'd'e'f'g'h', but that didn't work for me. Perhaps, I was not doing something correctly.

In any case, I found another post that saved images to multi page tiffs. Here is the link to the post: Adding frames to a Multi-Frame TIFF.

Also, here is the code that worked for me. It should be identical to that post.

Encoder encoder = Encoder.SaveFlag;
ImageCodecInfo encoderInfo = ImageCodecInfo.GetImageEncoders().First(i => i.MimeType == "image/tiff");
EncoderParameters encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(encoder, (long)EncoderValue.MultiFrame);
            
// Save the first frame of the multi page tiff
Bitmap firstImage = (Bitmap) _scannedPages[0].RawContent;
firstImage.Save(fileName, encoderInfo, encoderParameters);

encoderParameters.Param[0] = new EncoderParameter(encoder, (long)EncoderValue.FrameDimensionPage);

// Add the remaining images to the tiff
for (int i = 1; i < _scannedPages.Count; i++)
{
   Bitmap img = (Bitmap) _scannedPages[i].RawContent;
   firstImage.SaveAdd(img, encoderParameters);
}

// Close out the file
encoderParameters.Param[0] = new EncoderParameter(encoder, (long)EncoderValue.Flush);
firstImage.SaveAdd(encoderParameters);
Andrew Morton
  • 24,203
  • 9
  • 60
  • 84
Skadoosh
  • 2,575
  • 8
  • 40
  • 53
5

Necromancing
The accepted answer is a bit vague.
Here's full working code:

public class MultiPageTiff
{


    private static System.Drawing.Imaging.ImageCodecInfo GetEncoderInfo(string mimeType)
    {
        System.Drawing.Imaging.ImageCodecInfo[] encoders =
            System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();

        if (encoders != null)
        {
            for (int i = 0; i < encoders.Length; i++)
            {
                if (encoders[i].MimeType == mimeType)
                {
                    return encoders[i];
                } // End if (encoders[i].MimeType == mimeType) 
            } // Next i 

        } // End if (encoders != null) 

        return null;
    } // End Function GetEncoderInfo 


    public static System.Drawing.Image Generate(string[] filez)
    {
        System.Drawing.Image multiPageFile = null;
        byte[] ba = null;


        System.Drawing.Imaging.ImageCodecInfo tiffCodec = GetEncoderInfo("image/tiff");


        using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
        {
            for (int i = 0; i < filez.Length; ++i)
            {
                using (System.Drawing.Image inputImage = System.Drawing.Image.FromFile(filez[i]))
                {

                    using (System.IO.MemoryStream byteStream = new System.IO.MemoryStream())
                    {
                        inputImage.Save(byteStream, System.Drawing.Imaging.ImageFormat.Tiff);

                        if (i == 0)
                        {
                            multiPageFile = System.Drawing.Image.FromStream(byteStream);
                            multiPageFile = SaveImages(tiffCodec, ms, multiPageFile, null);
                        }
                        else
                        {

                            using (System.Drawing.Image tiffImage = System.Drawing.Image.FromStream(byteStream))
                            {
                                multiPageFile = SaveImages(tiffCodec, ms, tiffImage, multiPageFile);
                            } // End Using tiffImage 

                        }
                    } // End Using byteStream 

                } // End Using inputImage 

            } // Next i 

            ba = ms.ToArray();
        } // End Using ms 

        System.IO.File.WriteAllBytes(@"d:\mytiff.tiff", ba);

        //if (multiPageFile != null)
        //{
        //    multiPageFile.Dispose();
        //    multiPageFile = null;
        //}

        return multiPageFile;
    }


    private static System.Drawing.Image SaveImages(
          System.Drawing.Imaging.ImageCodecInfo tiffCodec
        , System.IO.MemoryStream outputStream
        , System.Drawing.Image tiffImage, System.Drawing.Image firstImage)
    {


        using (System.Drawing.Imaging.EncoderParameters encParameters =
              new System.Drawing.Imaging.EncoderParameters(3))
        {

            if (firstImage == null)
            {
                encParameters.Param[0] = new System.Drawing.Imaging.EncoderParameter(
                    System.Drawing.Imaging.Encoder.SaveFlag
                    , (long)System.Drawing.Imaging.EncoderValue.MultiFrame // 18L 
                );
            }
            else
            {
                encParameters.Param[0] = new System.Drawing.Imaging.EncoderParameter(
                    System.Drawing.Imaging.Encoder.SaveFlag
                    , (long)System.Drawing.Imaging.EncoderValue.FrameDimensionPage // 23L
                );
            }

            encParameters.Param[1] = new System.Drawing.Imaging.EncoderParameter(
                System.Drawing.Imaging.Encoder.ColorDepth, 24L
            );


            encParameters.Param[2] = new System.Drawing.Imaging.EncoderParameter(
                System.Drawing.Imaging.Encoder.Compression
                , (long)System.Drawing.Imaging.EncoderValue.CompressionLZW
            );


            if (firstImage == null)
            {
                firstImage = tiffImage;

                ((System.Drawing.Bitmap)tiffImage).SetResolution(96, 96);
                firstImage.Save(outputStream, tiffCodec, encParameters);
            }
            else
            {
                ((System.Drawing.Bitmap)tiffImage).SetResolution(96, 96);

                firstImage.SaveAdd(tiffImage, encParameters);
            }

            if (encParameters.Param[0] != null)
                encParameters.Param[0].Dispose();

            if (encParameters.Param[1] != null)
                encParameters.Param[1].Dispose();

            if (encParameters.Param[2] != null)
                encParameters.Param[2].Dispose();

        } // End Using encParameters 

        return firstImage;
    }


}
Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
  • Hey, "string[] filez" what type of files this should be? I am sending path of pdf file to it but got errors – Fahad Ejaz Butt Nov 02 '19 at 08:53
  • 1
    Any type System.Drawing.Image can open, so that is png, bmp, jpg, gif, tiff, wmf , exif and memorybmp. In other words, no PDF and no SVG. https://learn.microsoft.com/en-us/dotnet/api/system.drawing.imaging.imageformat?view=netframework-4.8 – Stefan Steiger Nov 04 '19 at 10:52
4

helpful topic. thanks for the info. I had a need to stitch a multipage image from an array of base64 encoded strings. This is what i put together based on the information in this thread. I dont quite underststand why i have to create a memory stream with the image format specified explicitly but this is what ended up working, if there is a better way to deal with this please let me know. thanks

/// <summary>
    /// Takes in an array of base64 encoded strings and creates a multipage tiff.
    /// </summary>
    /// <param name="sOutFile">file to be generated</param>
    /// <param name="pagesbase64Array"></param>
    private void SaevAsMultiPageTiff(string sOutFile, string[] pagesbase64Array)
    {
        System.Drawing.Imaging.Encoder encoder = System.Drawing.Imaging.Encoder.SaveFlag;
        ImageCodecInfo encoderInfo = ImageCodecInfo.GetImageEncoders().First(i => i.MimeType == "image/tiff");
        EncoderParameters encoderParameters = new EncoderParameters(1);
        encoderParameters.Param[0] = new EncoderParameter(encoder, (long)EncoderValue.MultiFrame);

        Bitmap firstImage = null;
        try
        {

            using (MemoryStream ms1 = new MemoryStream())
            {
                using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(pagesbase64Array[0])))
                {
                    Image.FromStream(ms).Save(ms1, ImageFormat.Tiff);
                    firstImage = (Bitmap)Image.FromStream(ms1);
                }
                // Save the first frame of the multi page tiff
                firstImage.Save(sOutFile, encoderInfo, encoderParameters); //throws Generic GDI+ error if the memory streams are not open when this is called
            }


            encoderParameters.Param[0] = new EncoderParameter(encoder, (long)EncoderValue.FrameDimensionPage);

            Bitmap imagePage;
            // Add the remining images to the tiff
            for (int i = 1; i < pagesbase64Array.Length; i++)
            {

                using (MemoryStream ms1 = new MemoryStream())
                {
                    using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(pagesbase64Array[i])))
                    {
                        Image.FromStream(ms).Save(ms1, ImageFormat.Tiff);
                        imagePage = (Bitmap)Image.FromStream(ms1);
                    }

                    firstImage.SaveAdd(imagePage, encoderParameters); //throws Generic GDI+ error if the memory streams are not open when this is called
                }
            }

        }
        catch (Exception)
        {
            //ensure the errors are not missed while allowing for flush in finally block so files dont get locked up.
            throw;
        }
        finally
        {
            // Close out the file
            encoderParameters.Param[0] = new EncoderParameter(encoder, (long)EncoderValue.Flush);
            firstImage.SaveAdd(encoderParameters);
        }
    }   
Karshan
  • 51
  • 3
3

Not being a fan of Microsoft's track record when it comes to handling and creating files of standardized formats, I would suggest using ImageMagick, available as a .Net library in the form of MagickNet (beware, http://midimick.com/magicknet/ currently has a spyware popup, I have alerted the site owner).

Sparr
  • 7,489
  • 31
  • 48
2

Here is the split operation of multi-tiff file. It works just like string substring function. The first index of image will be your MasterBitMap and you will keep adding frame to the MasterBitmap till the end of index.

public void SaveMultiFrameTiff(int start, int end)
    {            
        string outputFileName = "out.TIF";  
        string inputFileName = "input.TIF";            

        try
        {                

            Bitmap MasterBitmap = new Bitmap(inputFileName ); //Start page of document(master)
            Image imageAdd = Image.FromFile(inputFileName );  //Frame Image that will be added to the master          
            Guid guid = imageAdd.FrameDimensionsList[0]; //GUID
            FrameDimension dimension = new FrameDimension(guid);
            // start index cannot be less than 0 and cannot be greater than frame count        
            if (start < 1 || end > MasterBitmap.GetFrameCount(dimension)) { return; }        

            EncoderParameters ep = new EncoderParameters(1);

            //Get Image Codec Information
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
            ImageCodecInfo codecInfo = codecs[3]; //image/tiff

            //MultiFrame Encoding format
            EncoderParameter epMultiFrame = new EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, (long)EncoderValue.MultiFrame);
            ep.Param[0] = epMultiFrame;
            MasterBitmap.SelectActiveFrame(dimension, start - 1);
            MasterBitmap.Save(outputFileName, codecInfo, ep);//create master document

            //FrameDimensionPage Encoding format
            EncoderParameter epFrameDimensionPage = new EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, (long)EncoderValue.FrameDimensionPage);
            ep.Param[0] = epFrameDimensionPage;

            for (int i = start; i < end; i++)
            {
                imageAdd.SelectActiveFrame(dimension, i);//select next frame
                MasterBitmap.SaveAdd(new Bitmap(imageAdd), ep);//add it to the master
            }

            //Flush Encoding format
            EncoderParameter epFlush = new EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, (long)EncoderValue.Flush);
            ep.Param[0] = epFlush;
            MasterBitmap.SaveAdd(ep); //flush the file                   
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
}
cihadakt
  • 3,054
  • 11
  • 37
  • 59
1

Here is how to do this in vb.net:

Imports System.Drawing.Imaging

Sub CreateTiff(sOutTiffFile As String, sInFile1 As String, sInFile2 As String)
    Dim bitmap As Bitmap = Image.FromFile(sInFile1)
    Dim byteStream As MemoryStream = New MemoryStream()
    bitmap.Save(byteStream, System.Drawing.Imaging.ImageFormat.Tiff)

    Dim tiff As Image = Image.FromStream(byteStream)

    Dim oParams As EncoderParameters = New EncoderParameters(2)
    oParams.Param(0) = New EncoderParameter(Imaging.Encoder.Compression, EncoderValue.CompressionCCITT4)
    oParams.Param(1) = New EncoderParameter(Imaging.Encoder.SaveFlag, EncoderValue.MultiFrame)
    tiff.Save(sOutTiffFile, GetEncoderInfo("image/tiff"), oParams)

    'Next Page
    Dim bitmap2 As Bitmap = Image.FromFile(sInFile2)
    oParams.Param(1) = New EncoderParameter(Imaging.Encoder.SaveFlag, EncoderValue.FrameDimensionPage)
    tiff.SaveAdd(bitmap2, oParams)

    'Flush 
    Dim oFlushParams As EncoderParameters = New EncoderParameters(1)
    oFlushParams.Param(0) = New EncoderParameter(Imaging.Encoder.SaveFlag, EncoderValue.Flush)
    tiff.SaveAdd(oFlushParams)
End Sub

Private Function GetEncoderInfo(mimeType As String) As System.Drawing.Imaging.ImageCodecInfo
    Dim encoders = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()
    For i As Integer = 0 To encoders.Length - 1
        If encoders(i).MimeType = mimeType Then
            Return encoders(i)
        End If
    Next
    Return Nothing
End Function
Igor Krupitsky
  • 787
  • 6
  • 9