C# 将多个图片合并成TIFF文件的两种方法(续集)

  • A+
所属分类:.NET技术
摘要

01—名词解释 首先要弄清楚几个名词:① 图像的压缩品质:指的就是压缩后的图象清晰度,通俗地讲,就是用图像处理软件保存图像时,为了节省硬盘空间,一般是要对原始图像通过一定的算法进行压缩的,而压缩品质实质上就是压缩比率的大小,压缩的比率越高,生成的图像所占硬盘空间就越少,便是图片质量也就越差;相反,压缩比率越低,图像的品质越高,但是相应地占用硬盘空间也就越大。

01

名词解释

 

首先要弄清楚几个名词:

① 图像的压缩品质:指的就是压缩后的图象清晰度,通俗地讲,就是用图像处理软件保存图像时,为了节省硬盘空间,一般是要对原始图像通过一定的算法进行压缩的,而压缩品质实质上就是压缩比率的大小,压缩的比率越高,生成的图像所占硬盘空间就越少,便是图片质量也就越差;相反,压缩比率越低,图像的品质越高,但是相应地占用硬盘空间也就越大。

② Tiff:标签图像文件格式(Tag Image File Format,TIFF)是一种灵活的位图格式,TIFF(Tag Image File Format)图像文件是图形图像处理中常用的格式之一,其图像格式很复杂,但由于它对图像信息的存放灵活多变,可以支持很多色彩系统,而且独立于操作系统,因此得到了广泛应用。TIFF文件以.tif为扩展名。

 

02

新的探索

 

变化一:这位仁兄的处理方式是把每张图片先用CompressionImage这个方法加载到内存进行编码压缩的,实际上这一步是没有必要的,不仅仅浪费了时间还没有节省空间,因为调用的第三方本身就带了图片压缩的功能,所以这一段我的项目去掉了;

变化二:这位仁兄处理是把一组图片一次压缩成一张tiff,我这边的应用场景是图片一张一张来,所以就是每来一张就压缩一张;

变化三:除了图片合成,我的项目中添加了tiff文件拆分的方法;

变化四:记录图片加载、合成、保存的时间并写入log文件.

03

源码分享

我这里测试采用的是控制台,运行后输入你也数值:要合并的图片的数量,就可以执行了,测试过程我只有一张图片,所以我将这张图片进行了克隆:

Program.cs:这里有三个方法,依次是Main、BmpToTiff和CopyImage,CopyImage负责图片克隆,BmpToTiff方法的for循环中可以选择图片合成的方案一或者二。

 

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:UsersmajmDesktop新建文件夹";             CopyImage(filePath);             BmpToTiff(filePath);             //SplitTiffToBMP.SplitTiff(filePath);//图片分割             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++)             {                 //调用方案1                 //var timeSpan = BitMiracle.Jpeg2Tiff(i, imagePaths.Length, filePath,  $"{i}.tif", 100);                 //调用方案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("合成图片总计耗时:" + stopwatch.ElapsedMilliseconds.ToString() + "毫秒", true);             Console.WriteLine("合成图片总计耗时:" + stopwatch.ElapsedMilliseconds.ToString() + "毫秒");         }         public static void CopyImage(string filePath)         {             Console.WriteLine("请输入合并图片的数量:");             //控制台输入图片数量             bool flag = false;             int count = 0;             while (!flag)             {                 flag = int.TryParse(Console.ReadLine(), out count);             }              //复制图片             Bitmap bitmap = new Bitmap(filePath + "0.bmp");             for (int i = 1; i < count; i++)             {                 bitmap.Save(filePath + $"{i}.bmp");             }             bitmap.Dispose();         }     } } 

  

合图和拆分的代码我写到了类库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     {         /// 合并jpg         /// </summary>         /// <param name="bmps">bitmap数组</param>         /// <param name="tiffSavePath">保存路径</param>         /// <param name="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图片合成,一次合并一张         /// </summary>         /// <param name="index">图片id</param>         /// <param name="totalCount">图片数量</param>         /// <param name="tiffSavePath">tiff保存路径</param>         /// <param name="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:UserszhuyrDesktopAutoFocus" + $"{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;                 }             }         }      } } 

  Jpeg2Tiff这个方法的参数quality可以调节压缩比率,这个值默认是75,范围是【0,100】,当然你也可以通过tif.SetField(TiffTag.COMPRESSION, Compression.NONE);设置压缩方式,会有以下这些可选项,每种方式都采用了不同算法,所以压缩效率都是不一样的.

C# 将多个图片合并成TIFF文件的两种方法(续集)

 

 RasterEdge.cs  方案2,每次把新来的图片插入上一次合成的tiff中

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;         }     } } 

  C# 将多个图片合并成TIFF文件的两种方法(续集)

 

 SplitTiffToBMP.cs:图片拆分

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>();              //分割Tif图片为多个Gif图片             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("拆分图片总计耗时:" + stopwatch.ElapsedMilliseconds.ToString() + "毫秒", true);             Console.WriteLine("拆分图片总计耗时:" + stopwatch.ElapsedMilliseconds.ToString() + "毫秒");         }     } } 

  

数据模型:

FileWrite.cs  记录耗时,写入

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文件打印         /// </summary>         /// <param name="logFullPath">文件全路径</param>         /// <param name="content">写入内容</param>         /// <param name="flag">true:追加,false:覆盖</param>         public static void WriteLogFile(string content, bool flag,string logFullPath = @"C:UsersmajmDesktop新建文件夹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

运行演示

C# 将多个图片合并成TIFF文件的两种方法(续集)

 

 

05

项目源码

 

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

提取码:点击在看后添加小编微信zls20210502获取