使用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,指定旋轉參數就好了?