使用C#进行图片转换格式,缩放,自动旋转,保留exif
2010-06-02 20:37 by hackerzhou这几天心血来潮做了一个批量图片缩放,转换格式,并且可以根据exif的信息旋转图片,校正exif信息后保存的小程序.根据配置文件指定需要的功能.
using System; using System.IO; using System.Drawing.Imaging; using System.Drawing; using System.Drawing.Drawing2D; using System.Runtime.InteropServices; using System.Text; namespace PhotoUtil { class Program { [DllImport("kernel32")] private static extern long GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); private const String configFile = "Config.ini"; public static void Main(string[] args) { DirectoryInfo workingDir = new DirectoryInfo(ReadConfig("General", "WorkingDir",Environment.CurrentDirectory)); if (!workingDir.Exists) { workingDir = new DirectoryInfo(Environment.CurrentDirectory); } int quality=int.Parse(ReadConfig("General", "Quality", "85")); bool needResize = Boolean.Parse(ReadConfig("ResizeImage", "Enable", "false")); int newWidth = int.Parse(ReadConfig("ResizeImage", "NewWidth", "800")); int newHeight = int.Parse(ReadConfig("ResizeImage", "NewHeight", "600")); bool padding = Boolean.Parse(ReadConfig("ResizeImage", "Padding", "false")); bool needRotate = Boolean.Parse(ReadConfig("RotateImage", "Enable", "true")); FileInfo[] files = workingDir.GetFiles(); DirectoryInfo output = workingDir.CreateSubdirectory(DateTime.Now.ToString("yyyyMMdd") + "转换\r foreach (FileInfo i in files) { String type = i.Extension.ToLower(); if (type.Contains("jpg") || type.Contains("jpeg") || (type.Contains("png")) || type.Contains("tif") || type.Contains("bmp")) { Image img = Image.FromFile(i.FullName); if (needResize) { Console.WriteLine("Resizing " + i.FullName); ResizeImage(ref img, newWidth, newHeight, padding); } if (needRotate) { Console.WriteLine("Rotating " + i.FullName); RotateImage(img); } SaveAs(img, output.FullName+"\\\\"+i.Name, quality); } } Console.ReadLine(); } private static void SaveAs(Image img, string dest, long quality) { if (quality > 100 || quality < 1) { quality = 85; } EncoderParameters para = new EncoderParameters(); para.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality); String extension = new FileInfo(dest).Extension; ImageCodecInfo info = GetImageCodecInfoByExtension(extension); if (info != null) { img.Save(dest, info, para); } else { throw new Exception("Unrecognized format \\"" + extension + "\\"!\r } } private static void ResizeImage(ref Image image, int expectDestWidth, int expectDestHeight,bool padding) { PropertyItem[] exif = image.PropertyItems; int targetWidth = 0; int targetHeight = 0; double srcHWRate = (double)image.Width / (double)image.Height; double expectHWRate = (double)expectDestWidth / (double)expectDestHeight; if (srcHWRate > expectHWRate) { targetWidth = expectDestWidth; targetHeight = System.Convert.ToInt32(Math.Round(expectDestWidth / srcHWRate, 0)); } else { targetHeight = expectDestHeight; targetWidth = System.Convert.ToInt32(Math.Round(expectDestHeight * srcHWRate, 0)); } Image bitmap = null; if (!padding) { bitmap = new Bitmap(targetWidth, targetHeight); } else { bitmap = new Bitmap(expectDestWidth, expectDestHeight); } Graphics g = Graphics.FromImage(bitmap); g.CompositingQuality = CompositingQuality.HighQuality; g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.SmoothingMode = SmoothingMode.HighQuality; g.DrawImage(image, new Rectangle(0, 0, bitmap.Width, bitmap.Height), new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel); foreach (PropertyItem i in exif) { if (i.Id == 40962) { i.Value = BitConverter.GetBytes(targetWidth); } else if (i.Id == 40963) { i.Value = BitConverter.GetBytes(targetHeight); } bitmap.SetPropertyItem(i); } g.Dispose(); image.Dispose(); image = bitmap; } private static string ReadConfig(String Section, String Key, String defaultValue) { if (File.Exists(configFile)) { StringBuilder temp = new StringBuilder(1024); GetPrivateProfileString(Section, Key, String.Empty, temp, 1024, new FileInfo(configFile).FullName); if (!String.IsNullOrEmpty(temp.ToString())) { return temp.ToString(); } else { return defaultValue; } } else { return defaultValue; } } public static void RotateImage(Image img) { PropertyItem[] exif = img.PropertyItems; byte orientation = 0; foreach (PropertyItem i in exif) { if (i.Id == 274) { orientation = i.Value[0]; i.Value[0] = 1; img.SetPropertyItem(i); } } switch (orientation) { case 2: img.RotateFlip(RotateFlipType.RotateNoneFlipX); break; case 3: img.RotateFlip(RotateFlipType.Rotate180FlipNone); break; case 4: img.RotateFlip(RotateFlipType.RotateNoneFlipY); break; case 5: img.RotateFlip(RotateFlipType.Rotate90FlipX); break; case 6: img.RotateFlip(RotateFlipType.Rotate90FlipNone); break; case 7: img.RotateFlip(RotateFlipType.Rotate270FlipX); break; case 8: img.RotateFlip(RotateFlipType.Rotate270FlipNone); break; default: break; } foreach (PropertyItem i in exif) { if (i.Id == 40962) { i.Value = BitConverter.GetBytes(img.Width); } else if (i.Id == 40963) { i.Value = BitConverter.GetBytes(img.Height); } } } private static ImageCodecInfo GetImageCodecInfoByExtension(String extension) { ImageCodecInfo[] list = ImageCodecInfo.GetImageEncoders(); foreach(ImageCodecInfo i in list) { if (i.FilenameExtension.ToLower().Contains(extension.ToLower())) { return i; } } return null; } } }
配置文件名称:Config.ini,放在和程序同目录下,格式如下:
[General]
#工作目录
WorkingDir=.
#输出质量,1-100范围内的整数
Quality=80
[ResizeImage]
#是否启用
Enable=true
#新的宽值和高值
NewWidth=1024
NewHeight=768
#是否对超出原来比例的部分进行填充
Padding=false
#是否使用Exif中的旋转信息对图片进行旋转
[RotateImage]
Enable=true
1.保留exif信息的技巧是获取原始的Image.PropertyItems,对新图片添加即可.
2.关于Exif Orientation标志的定义 http://sylvana.net/jpegcrop/exif_orientation.html
3.变更jpeg输出质量的方法:
EncoderParameters para = new EncoderParameters();
para.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
//这里的quality是从1-100的long值,一开始想当然的以为1-100的值就用了byte,结果出错了.
最后调用Image.Save(String, ImageCodecInfo, EncoderParameters)来进行输出.
4.进行缩放/旋转后需要调整原始的exif信息,id查文档可知.列出一些
id=40962,width
id=40963,height
id=274,orientation type
对PropertyItem信息设置完了别忘了Image.SetPropertyItem(PropertyItem)添加到image中,我出一个很傻的bug,就是在修改了orientation type=1之后没有SetPropertyItem,于是导致图片实际被旋转至正确角度了,但是用ACDSee打开后可以看到exif的旋转信息依旧是原来的值.
2010-10-13 21:02
没有问题啊,旋转方向是储存在Exif结构体里的,有1-8几个可能的值,而我需要做的就是把2-8这些需要旋转的都转成1这样的方向,然后把旋转方向赋成1。你去看一下Exif的文档就明白了。
2010-10-13 16:50
rotate中的
orientation = i.Value[0];
i.Value[0] = 1;
似乎有問題!
是不是直接傳入函數參數orientation,指定旋轉參數就好了?