Question

I am trying to split and merge a multi-page tiff image. The reason I split is to draw annotations at each image level. The code is working fine, however the merged tiff is pretty large compared to source tiff. For example, I have tested with 17 color pages tiff image(size is 5MB), after splitting and merging, it produces 85MB tiff image. I am using BitMiracle.LibTiff.I actually commented the annotations code temporary as well to resolve this size issue, I am not sure what I am doing wrong.. Here is the code to split.

private List<Bitmap> SplitTiff(byte[] imageData)
{
    var bitmapList = new List<Bitmap>();
    var tiffStream = new TiffStreamForBytes(imageData);

    //open tif file
    var tif = Tiff.ClientOpen("", "r", null, tiffStream);

    //get number of pages
    var num = tif.NumberOfDirectories();
    if (num == 1) 
        return new List<Bitmap> 
        { 
            new Bitmap(GetImage(imageData)) 
        };

    for (short i = 0; i < num; i++)
    {
         //set current page
         tif.SetDirectory(i);
         FieldValue[] photoMetric = tif.GetField(TiffTag.PHOTOMETRIC);
         Photometric photo = Photometric.MINISBLACK;
         if (photoMetric != null && photoMetric.Length > 0)
             photo = (Photometric)photoMetric[0].ToInt();

         if (photo != Photometric.MINISBLACK && photo != Photometric.MINISWHITE)
             bitmapList.Add(GetBitmapFromTiff(tif));
         // else
             // bitmapList.Add(GetBitmapFromTiffBlack(tif, photo));// commented temporrarly to fix size issue
     }
     return bitmapList;
}

private static Bitmap GetBitmapFromTiff(Tiff tif)
{
    var value = tif.GetField(TiffTag.IMAGEWIDTH);
    var width = value[0].ToInt();

    value = tif.GetField(TiffTag.IMAGELENGTH);
    var height = value[0].ToInt();

    //Read the image into the memory buffer
    var raster = new int[height * width];
    if (!tif.ReadRGBAImage(width, height, raster))
    {
        return null;
    }

    var bmp = new Bitmap(width, height, PixelFormat.Format32bppRgb);

    var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);

    var bmpdata = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
    var bits = new byte[bmpdata.Stride * bmpdata.Height];

    for (var y = 0; y < bmp.Height; y++)
    {
        int rasterOffset = y * bmp.Width;
        int bitsOffset = (bmp.Height - y - 1) * bmpdata.Stride;

        for (int x = 0; x < bmp.Width; x++)
        {
            int rgba = raster[rasterOffset++];
            bits[bitsOffset++] = (byte)((rgba >> 16) & 0xff);
            bits[bitsOffset++] = (byte)((rgba >> 8) & 0xff);
            bits[bitsOffset++] = (byte)(rgba & 0xff);
            bits[bitsOffset++] = (byte)((rgba >> 24) & 0xff);
         }
    }

    System.Runtime.InteropServices.Marshal.Copy(bits, 0, bmpdata.Scan0, bits.Length);
    bmp.UnlockBits(bmpdata);

    return bmp;
}

and the code to merge individual Bitmaps to tiff is here...

public static PrizmImage PrizmImageFromBitmaps(List<Bitmap> imageItems, string ext)
{
     if (imageItems.Count == 1 && !(ext.ToLower().Equals(".tif") || ext.ToLower().Equals(".tiff")))
         return new PrizmImage(new MemoryStream(ImageUtility.BitmapToByteArray(imageItems[0])), ext);

     var codecInfo = GetCodecInfo();
     var memoryStream = new MemoryStream();

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

     var initialImage = imageItems[0];
     var masterBitmap = imageItems[0];// new Bitmap(initialImage);
     masterBitmap.Save(memoryStream, codecInfo, encoderParams);
     encoderParams.Param[0] = new EncoderParameter(Encoder.SaveFlag,  (long)EncoderValue.FrameDimensionPage);

     for (var i = 1; i < imageItems.Count; i++)
     {
          var img = imageItems[i];
          masterBitmap.SaveAdd(img, encoderParams);
          img.Dispose();
     }

     encoderParams.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.Flush);
     masterBitmap.SaveAdd(encoderParams);

     memoryStream.Seek(0, SeekOrigin.Begin);
     encoderParams.Dispose();
     masterBitmap.Dispose();
     return new PrizmImage(memoryStream, ext);
}

No correct solution

OTHER TIPS

Most probably, the issue is caused by the fact you are converting all images to 32 bits-per-pixel bitmaps.

Suppose, you have a black and white fax-encoded image. It might be encoded as a 100 Kb TIFF file. The same image might take 10+ megabytes when you save it as a 32bpp bitmap. Compressing these megabytes will help, but you never achieve the same compression ratio as in source image because you increased amount of image data from 1 bit per pixel to 32 bits per pixel.

So, you should not convert images to 32bpp bitmaps, if possible. Try preserve their properties and compression as much as possible. Have a look at source code of the TiffCP utility for hints how to do that.

If you absolutely have to convert images to 32bpp bitmaps (you might have to if you add colorful annotations to them) then there is not much can be done to reduce the resulting size. You might decrease output size by 10-20% percents if you choose better compression scheme and tune up the scheme properly. But that's all, I am afraid.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top