C# 将多个图片合并成TIFF文件的两种方法-程序员宅基地

技术标签: navicat  git  svg  nagios  less  

最近需要用到TIF格式的文件,研究了一段时间,终于有点结果了,

发现两种方式,第一种是使用BitMiracle.LibTiff.NET,直接在Nuget上安装即可

,第二种是使用RasterEdge.DocImageSDK,要从官网下载dll包

第一种免费,但是生成的tiff文件大小比原始图片大的多

第二种收费,但是有试用期一个月,效果很好,生成的tiff文件比原图小的多而且不失真。过期之后,只需要到官网下载最新dll,重新引用即可再来一个月试用。。。

先说第二种RasterEdge.DocImageSDK的使用方法:

官网地址:http://www.rasteredge.com/how-to/csharp-imaging/tiff-convert-bmp/
//过期后打开上面 这个网址,重新下载 dll包,重新引用即可 bin x64 4.0

新建一个cmd项目,测试代码如下:

class Program
    {
        private static byte[] CompressionImage(Stream fileStream, long quality)
        {
            using (System.Drawing.Image img = System.Drawing.Image.FromStream(fileStream))
            {
                using (Bitmap bitmap = new Bitmap(img))
                {
                    ImageCodecInfo CodecInfo = GetEncoder(img.RawFormat);
                    System.Drawing.Imaging.Encoder myEncoder = System.Drawing.Imaging.Encoder.Quality;
                    EncoderParameters myEncoderParameters = new EncoderParameters(1);
                    EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, quality);
                    myEncoderParameters.Param[0] = myEncoderParameter;
                    using (MemoryStream ms = new MemoryStream())
                    {
                        bitmap.Save(ms, CodecInfo, myEncoderParameters);
                        myEncoderParameters.Dispose();
                        myEncoderParameter.Dispose();
                        return ms.ToArray();
                    }
                }
            }
        }
        private static ImageCodecInfo GetEncoder(ImageFormat format)
        {
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == format.Guid)
                { return codec; }
            }
            return null;
        }
        static void Main(string[] args)
        {
            string[] imagePaths = System.IO.Directory.GetFiles(@"D:\images\","*.jpg");
            
            Bitmap[] bmps = new Bitmap[imagePaths.Count()];
            for (int i = 0; i < imagePaths.Length; i++)
            {
                var stream = new FileStream(imagePaths[i], FileMode.Open);
                var by = CompressionImage(stream, 100);
                stream.Close();


                Bitmap tmpBmp = new Bitmap(new MemoryStream(by));


                if (tmpBmp != null)
                    bmps[i] = tmpBmp;
            }
            ImageOutputOption option = new ImageOutputOption() { Color = ColorType.Color, Compression = ImageCompress.CCITT };
            TIFFDocument tifDoc = new TIFFDocument(bmps, option);
            if (tifDoc == null)
                throw new Exception("Fail to construct TIFF Document");
            tifDoc.Save(@"D:\images\test.tif");
        }
    
    }

下面说说第二种免费的方式:

新建一个TiffHelper帮助类:

using BitMiracle.LibTiff.Classic;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace JpgToTiff
{
    public class UserErrorHandler : TiffErrorHandler
    {
        public override void WarningHandler(Tiff tif, string method, string format, params object[] args)
        {
            //base.WarningHandler(tif, method, format, args);
        }
        public override void WarningHandlerExt(Tiff tif, object clientData, string method, string format, params object[] args)
        {
            //base.WarningHandlerExt(tif, clientData, method, format, args);
        }
    }
    public class TiffHelper
    {
        public static UserErrorHandler m_errorHandler = new UserErrorHandler();
        static TiffHelper()
        {
            Tiff.SetErrorHandler(m_errorHandler);
        }
        /// <summary>
        /// 合并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 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;
                }
            }
        }
    }
}

下面是测试代码:

class Program
    {
        private static byte[] CompressionImage(Stream fileStream, long quality)
        {
            using (System.Drawing.Image img = System.Drawing.Image.FromStream(fileStream))
            {
                using (Bitmap bitmap = new Bitmap(img))
                {
                    ImageCodecInfo CodecInfo = GetEncoder(img.RawFormat);
                    System.Drawing.Imaging.Encoder myEncoder = System.Drawing.Imaging.Encoder.Quality;
                    EncoderParameters myEncoderParameters = new EncoderParameters(1);
                    EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, quality);
                    myEncoderParameters.Param[0] = myEncoderParameter;
                    using (MemoryStream ms = new MemoryStream())
                    {
                        bitmap.Save(ms, CodecInfo, myEncoderParameters);
                        myEncoderParameters.Dispose();
                        myEncoderParameter.Dispose();
                        return ms.ToArray();
                    }
                }
            }
        }
        private static ImageCodecInfo GetEncoder(ImageFormat format)
        {
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == format.Guid)
                { return codec; }
            }
            return null;
        }
        static void Main(string[] args)
        {
            string[] imagePaths = System.IO.Directory.GetFiles(@"D:\images\","*.jpg");
            
            Bitmap[] bmps = new Bitmap[imagePaths.Count()];
            for (int i = 0; i < imagePaths.Length; i++)
            {
                var stream = new FileStream(imagePaths[i], FileMode.Open);
                var by = CompressionImage(stream, 100);
                stream.Close();


                Bitmap tmpBmp = new Bitmap(new MemoryStream(by));


                if (tmpBmp != null)
                    bmps[i] = tmpBmp;
            }
            TiffHelper.Jpegs2Tiff(bmps, @"D:\images\test.tif", 100);
        }   
    }

可以看到,两个方式生成的tif文件大小简直天壤之别。。。

7个原图大小4.8M,第一种1.36M,

第二种直接23.5M…

也可能是我没有弄好压缩方式。。。。

那我就不晓得了。

如果喜欢,点个赞呗

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sD7O95O/article/details/116358489

智能推荐

C++ Primer 3.5.4 C风格字符串 3.5.5 与旧代码接口 知识点总结 课后题重点-程序员宅基地

文章浏览阅读493次,点赞11次,收藏7次。2.尽量使用vector和迭代器,不容易出错。

基于STM32单片机智能语音识别按键温控风扇人体感应设计21-694-程序员宅基地

文章浏览阅读168次。21-694、STM32智能风扇系统设计-拨动-KEY-风扇-语音识别-DS18B20-E18-LED产品功能描述:本设计由STM32F103C8T6单片机核心板电路+拨动开关电路+按键电路+风扇控制电路+语音识别模块电路+DS18B20温度检测电路+红外避障传感器电路+LED指示灯电路组成。1、通过拨动开关选择系统工作模式。2、手动模式:通过按键或者语音识别可以控制风扇的开关和转速。3、自动模式:根据温度的大小进行控制风扇的转动速度,自动调节pwm。此模式下有2个红外避障传感器分别左右依次排开,分别记为E

Unity 发布webgl 通过IP端口访问无限制,布置IIS_unity webgl 设置指定端口-程序员宅基地

文章浏览阅读602次。首先电脑开启IIS权限,自行百度,此处不做赘述打开IIS,右键添加网站2.输入网站名称(随便输入即可)、物理路径(项目工程文件所在位置,安全情况下可以范围大一些)、端口(一般8080之类的就可以)3.双击MIME,点击右上角添加4.添加两个,一个是.unity3d,类型为application/octet-stream一个是.unityweb,类型为application/binary另外可根据自己需要增加,类似.json,等等。添加完成后如下图所示5.输入自己主机的IP地址+:_unity webgl 设置指定端口

Oracle AWR(演变过程)_oracle awr 什么时候诞生-程序员宅基地

文章浏览阅读1.1k次。Oracle AWR 2010-04-15 06:16:06| 分类:Oracle|字号 订阅Oracle AWR 转自:http://blog.csdn.net/hxf0759/archive/2009/05/15/4189275.aspx一、WHY——为什么会出现ASH和AWR?1. 10g之前用户的连接将产生会话,当前会话记录保存_oracle awr 什么时候诞生

基于javaweb+mysql的jsp+servlet电影票售票管理系统(java+jsp+servlet+javabean+mysql)_【java+jsp+mysql】订票系统-程序员宅基地

文章浏览阅读192次。基于javaweb+mysql的jsp+servlet电影票售票管理系统(java+jsp+servlet+javabean+mysql)运行环境Java≥8、MySQL≥5.7、Tomcat≥8开发工具eclipse/idea/myeclipse/sts等均可配置运行适用课程设计,大作业,毕业设计,项目练习,学习演示等功能说明后台:管理用户、影片、排片、影厅、订单、影评等前台:注册登录、查看排片、详情,选座、购票等后台前台技术框架JSP Servlet MySQL C3P0 DBUtil Tomcat J_【java+jsp+mysql】订票系统

【Flutter】AutomaticKeepAliveClientMixin解决Tab切换页面频繁build问题_flutter如何防止子view重新build-程序员宅基地

文章浏览阅读563次,点赞6次,收藏10次。Flutter中的PageView和TabBarView默认每次切换页面时都会重新构建(build)页面,这样虽然渲染了最新的UI,但是这种行为可能会导致性能问题,特别是页面中有复杂的布局和数据加载时。解决这个问题,可以用AutomaticKeepAliveClientMixin接口和它的wantKeepAlive方法。保持页面状态,避免每次切换都重新构建。_flutter如何防止子view重新build

随便推点

二叉树、平衡二叉树、完全二叉树、满二叉树_平衡二叉树和完全二叉树的区别-程序员宅基地

文章浏览阅读9.4w次,点赞15次,收藏106次。基本概念结点的层次(Level)从根开始定义,根为第一层,根的孩子为第二层。二叉树的高度:树中结点的最大层次称为树的深度(Depth)或高度。 二叉树在计算机科学中,二叉树是每个结点最多有两个子树的有序树。通常子树的根被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用作二叉查找树和二叉堆。二叉树的每个结点至多只有二棵子树(不存在_平衡二叉树和完全二叉树的区别

音频深度学习变得简单:自动语音识别 (ASR),它是如何工作的_asr语音识别-程序员宅基地

文章浏览阅读4k次,点赞2次,收藏12次。在本文中,我将重点介绍使用深度学习进行语音转文本的核心功能。我的目标不仅是了解某件事是如何工作的,而且是了解它为什么会这样工作。_asr语音识别

23种常见设计模式总结(类图,优缺点,不同设计模式之间的对比)_设计模式工厂模式的类图-程序员宅基地

文章浏览阅读1w次,点赞12次,收藏104次。设计模式的几个重要原则单一职责原则开闭原则依赖倒转原则里氏代换原则合成聚合复用原则:尽量使用组合/聚合代替继承迪米特原则:降低耦合性设计模式 类图 优点 缺点..._设计模式工厂模式的类图

pandas创建DataFrame的几种方式(建议收藏)_pandas新建一个dataframe-程序员宅基地

文章浏览阅读3.9w次,点赞25次,收藏148次。pandas创建DataFrame的几种方式如果你是一个pandas初学者,那么不知道你会不会像我一样。在学用列表或者数组创建DataFrame时理不清怎样用数据生成以及想要形状的的Dataframe,那么,现在,你不用自己琢磨了,我这里给你整理了一下,现在我们就来看看这三种生成Dataframe的方式。1.用传入列表或者数组创建DataFrame采用列表创建DataFramenums = [[i for i in range(3)] for _ in range(10)]numscol_pandas新建一个dataframe

HTML如何编写类似QQ聊天框,jQuery实现简易QQ聊天框-程序员宅基地

文章浏览阅读831次。本文实例为大家分享了jQuery实现简易QQ聊天框的具体代码,供大家参考,具体内容如下例子1*{list-style: none;margin: 0;padding: 0;}.box1{width: 500px;height: 480px;border: 1px solid #aaa;margin: 0 auto;margin-top: 20px;}.box{width: 500px;height..._pc端类似于qq的html聊天界面

知识图谱从入门到应用——知识图谱的知识表示:符号表示方法_知识图谱符号表示-程序员宅基地

文章浏览阅读1.2w次,点赞9次,收藏13次。在前面的文章中已经多次提到,知识图谱采用图的方式描述和表达知识,相比于简单图,能建模更加复杂的事物关系,但比起形式化逻辑,又免于复杂的逻辑约束,使得知识的获取过程变得更加容易。但在知识图谱的实际实践中,不同的应用场景会对知识的建模采用不同表达能力的图表示方法。例如有些应用场景仅采用最简单的无向图,通常适合于对建模要求不高,偏于数据挖掘类的应用场景。知识图谱表示应用最多的是有向标记图(Directed Labelled Graph)。_知识图谱符号表示

推荐文章

热门文章

相关标签