C# two methods of merging multiple pictures into TIFF files (sequel)

01

Noun interpretation

 

First of all, we should clarify several nouns:

① Image compression quality: refers to the definition of the compressed image. Generally speaking, when saving the image with image processing software, in order to save hard disk space, the original image is generally compressed through a certain algorithm, and the compression quality is essentially the size of the compression ratio. The higher the compression ratio, the less hard disk space the generated image occupies, The worse the picture quality; On the contrary, the lower the compression ratio, the higher the image quality, but the larger the hard disk space.

②   TIFF: Tag Image File Format (TIFF) is a flexible bitmap format. TIFF (Tag Image File Format) image file is one of the commonly used formats in graphics and image processing. Its image format is very complex, but due to its flexible storage of image information, it can support many color systems and is independent of the operating system, Therefore, it has been widely used. TIFF files have a. tif extension.

 

02

New exploration

 

Change 1: the brother's processing method is to load each picture into the memory with the CompressionImage method for encoding and compression. In fact, this step is unnecessary. It not only wastes time, but also saves space. Because the third party called itself has the function of picture compression, so my project is removed in this section;

Change 2: this guy compresses a group of pictures into one tiff at a time. My application scenario is to compress one picture after another, so each one is compressed;

Change 3: in addition to image synthesis, I added the method of splitting tiff files in my project;

Change 4: record the time of loading, synthesizing and saving pictures and write them to the log file

03

Source sharing

I use the console for the test here. After running, enter the value: the number of pictures to be merged can be executed. In the test process, I only have one picture, so I cloned this picture:

Program.cs: there are three methods, Main, BmpToTiff and CopyImage. CopyImage is responsible for image cloning. Scheme 1 or 2 of image synthesis can be selected in the for loop of BmpToTiff method.

 

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TiffHelper;

namespace TiffConsole
{
    class Program
    {
        private static Stopwatch stopwatch = new Stopwatch();
        static void Main(string[] args)
        {
            string filePath = @"C:\Users\majm\Desktop\New folder\";
            CopyImage(filePath);
            BmpToTiff(filePath);
            //SplitTiffToBMP.SplitTiff(filePath);// Image segmentation
            Console.ReadKey();
        }
        
        public static void BmpToTiff(string filePath)
        {
            List<TiffHelper.TimeSpan> timeSpans = new List<TiffHelper.TimeSpan>();
           
            string[] imagePaths = System.IO.Directory.GetFiles(filePath, "*.bmp");
            stopwatch.Start();
            for (int i = 0; i < imagePaths.Length; i++)
            {
                //Call scenario 1
                //var timeSpan = BitMiracle.Jpeg2Tiff(i, imagePaths.Length, filePath,  $"{i}.tif", 100);
                //Call scenario 2
                var timeSpan = RasterEdge.TIFFDocument(i,filePath);
                timeSpans.Add(timeSpan);
            }
            stopwatch.Stop();

            FileWrite.WriteLogFile("Id,LoadTime,MergeTime,SaveTime,TotalTime,flag", true);
            timeSpans.ForEach(n => FileWrite.WriteLogFile(n.ToString(), true));
            FileWrite.WriteLogFile("Total time taken to compose pictures:" + stopwatch.ElapsedMilliseconds.ToString() + "millisecond", true);
            Console.WriteLine("Total time taken to compose pictures:" + stopwatch.ElapsedMilliseconds.ToString() + "millisecond");
        }
        public static void CopyImage(string filePath)
        {
            Console.WriteLine("Please enter the number of merged pictures:");
            //Console input number of pictures
            bool flag = false;
            int count = 0;
            while (!flag)
            {
                flag = int.TryParse(Console.ReadLine(), out count);
            }

            //Copy picture
            Bitmap bitmap = new Bitmap(filePath + "0.bmp");
            for (int i = 1; i < count; i++)
            {
                bitmap.Save(filePath + $"{i}.bmp");
            }
            bitmap.Dispose();
        }
    }
}

  

I wrote the code of combining and splitting into the class library TiffHelper:

 

BitMiracle.cs

using BitMiracle.LibTiff.Classic;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace TiffHelper
{
    public class BitMiracle
    {
        ///Merge jpg
        /// </summary>
        ///< param name = "BMPs" > bitmap array < / param >
        ///< param name = "tiffsavepath" > save path < / param >
        ///< param name = "quality" > picture quality, 1-100 < / param >
        /// <returns></returns>
        public static bool Jpegs2Tiff(Bitmap[] bmps, string tiffSavePath, int quality = 15)
        {
            try
            {
                MemoryStream ms = new MemoryStream();
                using (Tiff tif = Tiff.ClientOpen(@"in-memory", "w", ms, new TiffStream()))
                {
                    foreach (var bmp in bmps)//
                    {
                        byte[] raster = GetImageRasterBytes(bmp, PixelFormat.Format24bppRgb);
                        tif.SetField(TiffTag.IMAGEWIDTH, bmp.Width);
                        tif.SetField(TiffTag.IMAGELENGTH, bmp.Height);
                        tif.SetField(TiffTag.COMPRESSION, Compression.JPEG);
                        tif.SetField(TiffTag.PHOTOMETRIC, Photometric.RGB);
                        tif.SetField(TiffTag.JPEGQUALITY, quality);
                        tif.SetField(TiffTag.ROWSPERSTRIP, bmp.Height);


                        tif.SetField(TiffTag.XRESOLUTION, 90);
                        tif.SetField(TiffTag.YRESOLUTION, 90);


                        tif.SetField(TiffTag.BITSPERSAMPLE, 8);
                        tif.SetField(TiffTag.SAMPLESPERPIXEL, 3);


                        tif.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG);


                        int stride = raster.Length / bmp.Height;
                        ConvertSamples(raster, bmp.Width, bmp.Height);


                        for (int i = 0, offset = 0; i < bmp.Height; i++)
                        {
                            tif.WriteScanline(raster, offset, i, 0);
                            offset += stride;
                        }


                        tif.WriteDirectory();
                    }
                    System.IO.FileStream fs = new FileStream(tiffSavePath, FileMode.Create);

                    ms.Seek(0, SeekOrigin.Begin);
                    fs.Write(ms.ToArray(), 0, (int)ms.Length);
                    fs.Close();
                    return true;
                }
            }
            catch (Exception ex)
            {
                return false;
            }
        }
        private static MemoryStream ms1 = new MemoryStream();
        private static Tiff tif = Tiff.ClientOpen(@"in-memory", "w", ms1, new TiffStream());
        /// <summary>
        ///tiff image synthesis, one at a time
        /// </summary>
        ///< param name = "index" > picture ID < / param >
        ///< param name = "totalcount" > number of pictures < / param >
        ///< param name = "tiffsavepath" > TIFF save path < / param >
        ///< param name = "quality" > compression quality < / param >
        /// <returns></returns>
        public static TimeSpan Jpeg2Tiff(int index, int totalCount, string tiffSavePath,string fileName, int quality = 75)
        {
            try
            {
                string fileFullPath = Path.Combine(tiffSavePath, fileName);
                Stopwatch sTotal = new Stopwatch();
                sTotal.Start();
                TimeSpan timeSpan = new TimeSpan();
                timeSpan.Id = index;
                //Bitmap[] bmps = new Bitmap[1] {new Bitmap( @"C:\Users\zhuyr\Desktop\AutoFocus\" + $"{i}.bmp")};
                Stopwatch sw = new Stopwatch();

                sw.Start();
                Bitmap bmp = new Bitmap(tiffSavePath + $"{index}.bmp");
                sw.Stop();
                timeSpan.LoadTime = sw.ElapsedMilliseconds.ToString();

                sw = new Stopwatch();
                sw.Start();
                byte[] raster = GetImageRasterBytes(bmp, PixelFormat.Format24bppRgb);
                tif.SetField(TiffTag.IMAGEWIDTH, bmp.Width);
                tif.SetField(TiffTag.IMAGELENGTH, bmp.Height);
                tif.SetField(TiffTag.COMPRESSION, Compression.NONE);
                tif.SetField(TiffTag.PHOTOMETRIC, Photometric.RGB);
                tif.SetField(TiffTag.JPEGQUALITY, quality);
                tif.SetField(TiffTag.ROWSPERSTRIP, bmp.Height);


                tif.SetField(TiffTag.XRESOLUTION, 90);
                tif.SetField(TiffTag.YRESOLUTION, 90);


                tif.SetField(TiffTag.BITSPERSAMPLE, 8);
                tif.SetField(TiffTag.SAMPLESPERPIXEL, 3);


                tif.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG);


                int stride = raster.Length / bmp.Height;
                ConvertSamples(raster, bmp.Width, bmp.Height);


                for (int i = 0, offset = 0; i < bmp.Height; i++)
                {
                    tif.WriteScanline(raster, offset, i, 0);
                    offset += stride;
                }


                tif.WriteDirectory();
                sw.Stop();
                timeSpan.MergeTime = sw.ElapsedMilliseconds.ToString();

                sw = new Stopwatch();
                sw.Start();
                if (totalCount == 1000)
                {
                    if (index % 10 == 9)
                    {
                        System.IO.FileStream fs = new FileStream(fileFullPath, FileMode.Create);
                        ms1.Seek(0, SeekOrigin.Begin);
                        fs.Write(ms1.ToArray(), 0, (int)ms1.Length);
                        fs.Close();
                        sw.Stop();
                    }
                }
                else
                {
                    System.IO.FileStream fs = new FileStream(fileFullPath, FileMode.Create);
                    ms1.Seek(0, SeekOrigin.Begin);
                    fs.Write(ms1.ToArray(), 0, (int)ms1.Length);
                    fs.Close();
                    sw.Stop();
                }
                timeSpan.SaveTime = sw.ElapsedMilliseconds.ToString();

                sTotal.Stop();
                timeSpan.TotalTime = sTotal.ElapsedMilliseconds.ToString();
                timeSpan.flag = true;
                return timeSpan;

            }
            catch (Exception ex)
            {
                return null;
            }
        }

        private static byte[] GetImageRasterBytes(Bitmap bmp, PixelFormat format)
        {
            Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
            byte[] bits = null;
            try
            {
                BitmapData bmpdata = bmp.LockBits(rect, ImageLockMode.ReadWrite, format);
                bits = new byte[bmpdata.Stride * bmpdata.Height];
                System.Runtime.InteropServices.Marshal.Copy(bmpdata.Scan0, bits, 0, bits.Length);
                bmp.UnlockBits(bmpdata);
            }
            catch
            {
                return null;
            }
            return bits;
        }
        private static void ConvertSamples(byte[] data, int width, int height)
        {
            int stride = data.Length / height;
            const int samplesPerPixel = 3;


            for (int y = 0; y < height; y++)
            {
                int offset = stride * y;
                int strideEnd = offset + width * samplesPerPixel;


                for (int i = offset; i < strideEnd; i += samplesPerPixel)
                {
                    byte temp = data[i + 2];
                    data[i + 2] = data[i];
                    data[i] = temp;
                }
            }
        }

    }
}

The parameter quality of Jpeg2Tiff can adjust the compression ratio. The default value is 75 and the range is [0100]. Of course, you can also use tif.SetField(TiffTag.COMPRESSION, Compression.NONE); There are the following options for setting the compression mode. Each mode adopts different algorithms, so the compression efficiency is different

 

  RasterEdge.cs   Scheme 2: insert the new image into the tiff synthesized last time

using RasterEdge.Imaging.Basic;
using RasterEdge.XDoc.TIFF;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TiffHelper
{
    public class RasterEdge
    {
        public static TimeSpan TIFFDocument(int index,string filePath)
        {
            Stopwatch sTotal = new Stopwatch();
            sTotal.Start();
            TimeSpan timeSpan = new TimeSpan();
            timeSpan.Id = index;

            Stopwatch sw = new Stopwatch();
            sw.Start();
            Bitmap[] bmps = new Bitmap[1] { new Bitmap(filePath + $"{index}.bmp") };
            sw.Stop();
            timeSpan.LoadTime = sw.ElapsedMilliseconds.ToString();

            sw = new Stopwatch();
            sw.Start();
            //ImageOutputOption option = new ImageOutputOption() { Color = ColorType.Color, Compression = ImageCompress.CCITT };
            ImageOutputOption option = new ImageOutputOption() { Type = ImageType.JPEG, Color = ColorType.Color, Compression = ImageCompress.Uncompressed };
            TIFFDocument tifDoc_new = new TIFFDocument(bmps, option);
            TIFFDocument tifDoc_old;
            if (index > 0)
            {
                tifDoc_old = new TIFFDocument(filePath + $"{index - 1}.tif");
                BasePage page = tifDoc_new.GetPage(0);
                tifDoc_old.InsertPage(page, index);
            }
            else
            {
                tifDoc_old = tifDoc_new;
            }
            if (tifDoc_old == null)
                throw new Exception("Fail to construct TIFF Document");
            sw.Stop();
            timeSpan.MergeTime = sw.ElapsedMilliseconds.ToString();

            sw = new Stopwatch();
            sw.Start();
            tifDoc_old.Save(filePath + $"{index}.tif");
            sw.Stop();
            timeSpan.SaveTime = sw.ElapsedMilliseconds.ToString();

            sTotal.Stop();
            timeSpan.TotalTime = sTotal.ElapsedMilliseconds.ToString();
            timeSpan.flag = true;
            return timeSpan;
        }
    }
}

  

 

  SplitTiffToBMP.cs: picture split

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TiffHelper
{
    public class SplitTiffToBMP
    {
        private static Stopwatch stopwatch = new Stopwatch();
        public static void SplitTiff(string filePath)
        {
            stopwatch.Start();
            List<TimeSpan> timeSpans = new List<TimeSpan>();

            //Split a Tif picture into multiple Gif pictures
            System.Drawing.Image img = System.Drawing.Image.FromFile(filePath+"9.tif");
            Guid guid = (Guid)img.FrameDimensionsList.GetValue(0);
            FrameDimension dimension = new FrameDimension(guid);
            int totalPage = img.GetFrameCount(dimension);

            for (int i = 0; i < totalPage; i++)
            {
                TimeSpan timeSpan = new TimeSpan();
                Stopwatch sw = new Stopwatch();
                sw.Start();
                img.SelectActiveFrame(dimension, i);
                img.Save(filePath + i + ".bmp", System.Drawing.Imaging.ImageFormat.Bmp);
                sw.Stop();
                timeSpan.Id = i;
                timeSpan.TotalTime = sw.ElapsedMilliseconds.ToString();
                timeSpans.Add(timeSpan);
            }
            stopwatch.Stop();
            FileWrite.WriteLogFile("Id,,,,TotalTime", true);
            timeSpans.ForEach(n => FileWrite.WriteLogFile(n.ToString(), true));
            FileWrite.WriteLogFile("Total time taken to split pictures:" + stopwatch.ElapsedMilliseconds.ToString() + "millisecond", true);
            Console.WriteLine("Total time taken to split pictures:" + stopwatch.ElapsedMilliseconds.ToString() + "millisecond");
        }
    }
}

  

Data model:

FileWrite.cs   Recording time, writing

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TiffHelper
{
    public class FileWrite
    {
        /// <summary>
        ///log file printing
        /// </summary>
        ///< param name = "logfullpath" > full path of file < / param >
        ///< param name = "content" > write content < / param >
        ///< param name = "flag" > true: append, false: overwrite < / param >
        public static void WriteLogFile(string content, bool flag,string logFullPath = @"C:\Users\majm\Desktop\New folder\log.txt")
        {
            StreamWriter sw = new StreamWriter(logFullPath ,flag);
            sw.WriteLine(content);
            sw.Close();
        }
    }
}

  TimeSpan.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TiffHelper
{
    public class TimeSpan
    {
        public int Id;
        public string LoadTime;
        public string MergeTime;
        public string SaveTime;
        public string TotalTime;
        public bool flag;
        public override string ToString()
        {
            StringBuilder report = new StringBuilder();
            report.Append($"{Id},{LoadTime},{MergeTime},{SaveTime},{TotalTime},{flag}");
            return report.ToString();
        }
    }
}

  

04

Run demo

 

 

05

Project source code

 

Link: https://pan.baidu.com/s/1xw6iJkPg_5-P63QpLlX0kA  

Extraction code: Click to add Xiaobian wechat zls20210502 after viewing

Tags: C# Software winform

Posted on Sun, 07 Nov 2021 16:31:37 -0500 by stageguys