使用WIC快速创建符合EXIF方向标记的按比例缩小的位图 [英] Using WIC to Quickly Create Scaled Down Bitmap that honors EXIF Orientation Tag
问题描述
我正在寻找创建符合EXIF方向标签的缩小位图的最快方法
I'm looking for the fastest way to create scaled down bitmap that honors EXIF orientation tag
Ref: https://weblogs.asp。净/ bleroy /从asp调整图像大小的最快方法净并获得更多支持的图像
当前,我使用以下代码创建一个使用EXIF方向标记的位图
Currently i use the following code to create a Bitmap that honors EXIF Orientation tag
static Bitmap FixImageOrientation(Bitmap srce)
{
const int ExifOrientationId = 0x112;
// Read orientation tag
if (!srce.PropertyIdList.Contains(ExifOrientationId)) return srce;
var prop = srce.GetPropertyItem(ExifOrientationId);
var orient = BitConverter.ToInt16(prop.Value, 0);
// Force value to 1
prop.Value = BitConverter.GetBytes((short)1);
srce.SetPropertyItem(prop);
// Rotate/flip image according to <orient>
switch (orient)
{
case 1:
srce.RotateFlip(RotateFlipType.RotateNoneFlipNone);
return srce;
case 2:
srce.RotateFlip(RotateFlipType.RotateNoneFlipX);
return srce;
case 3:
srce.RotateFlip(RotateFlipType.Rotate180FlipNone);
return srce;
case 4:
srce.RotateFlip(RotateFlipType.Rotate180FlipX);
return srce;
case 5:
srce.RotateFlip(RotateFlipType.Rotate90FlipX);
return srce;
case 6:
srce.RotateFlip(RotateFlipType.Rotate90FlipNone);
return srce;
case 7:
srce.RotateFlip(RotateFlipType.Rotate270FlipX);
return srce;
case 8:
srce.RotateFlip(RotateFlipType.Rotate270FlipNone);
return srce;
default:
srce.RotateFlip(RotateFlipType.RotateNoneFlipNone);
return srce;
}
}
我首先创建方向固定图像,然后调整其大小(保留长宽比)以进行快速处理。
I'm first creating a orientation fixed image ,then resizing it (preserving aspect ratio) for fast processing.
public static Bitmap UpdatedResizeImage(Bitmap source, Size size)
{
var scale = Math.Min(size.Width / (double)source.Width, size.Height / (double)source.Height);
var bmp = new Bitmap((int)(source.Width * scale), (int)(source.Height * scale));
using (var graph = Graphics.FromImage(bmp))
{
graph.InterpolationMode = InterpolationMode.High;
graph.CompositingQuality = CompositingQuality.HighQuality;
graph.SmoothingMode = SmoothingMode.AntiAlias;
graph.DrawImage(source, 0, 0, bmp.Width, bmp.Height);
}
return bmp;
}
现在 WIC 允许更快的图像处理。 : https://stackoverflow.com/a/57987315/848968
Now WIC allows much faster image manipulation.Ref:https://stackoverflow.com/a/57987315/848968
如何创建按比例缩小的BitmapImage ,以兑现EXIF标记
How can i create a Scaled Down BitmapImage that honours the EXIF tag
更新:
if ((bitmapMetadata != null) && (bitmapMetadata.ContainsQuery("System.Photo.Orientation")))
{
object o = bitmapMetadata.GetQuery("System.Photo.Orientation");
if (o != null)
{
switch ((ushort)o)
{
case 3:
rotatedImage = new TransformedBitmap(resized, new RotateTransform(180));
break;
case 6:
rotatedImage = new TransformedBitmap(resized, new RotateTransform(90));
break;
case 8:
rotatedImage = new TransformedBitmap(resized, new RotateTransform(270));
break;
}
}
}
推荐答案
以下是一些示例代码,该示例代码基于WPF类(并通过一点WIC互操作来确定给定文件扩展名的适当编码器,在保存图像缩略图的同时保留图像方向),但是这是可选的):
Here is some sample code that saves a thumbnail while preserving the image orientation, based on WPF classes (and a small WIC interop fun to determine the proper encoder for a given file extension, but this is optional):
static void Main()
{
SaveThumbnail("new.jpg", 64); // auto jpg
SaveThumbnail("new.jpg", 64, "new.png"); // explicit png output
}
public static void SaveThumbnail(string inputFilePath, int thumbnailSize, string outputFilePath = null)
{
if (inputFilePath == null)
throw new ArgumentNullException(inputFilePath);
// decode frame
var frame = BitmapDecoder.Create(new Uri(inputFilePath, UriKind.RelativeOrAbsolute), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None).Frames[0];
// read input transformations
var transformations = new Transformations(frame.Metadata as BitmapMetadata);
int width;
int height;
if (frame.Width > frame.Height)
{
width = thumbnailSize;
height = (int)(frame.Height * thumbnailSize / frame.Width);
}
else
{
width = (int)(frame.Width * thumbnailSize / frame.Height);
height = thumbnailSize;
}
Guid containerFormat;
if (outputFilePath == null)
{
// use input format same as decode.
containerFormat = frame.Decoder.CodecInfo.ContainerFormat;
outputFilePath = Path.ChangeExtension(inputFilePath, thumbnailSize + Path.GetExtension(inputFilePath));
}
else
{
// icing on the cake..., we determine the format from the output file extension, using some WIC voodoo (code below)
// you could make it simpler and harcode things out but this way you can use other 3rd parties codecs
containerFormat = WicUtilities.EnumerateDecoderFormatsForExtension(Path.GetExtension(outputFilePath)).FirstOrDefault();
if (containerFormat == Guid.Empty) // this extension is not supported on this system
throw new ArgumentNullException(outputFilePath);
}
var encoder = BitmapEncoder.Create(containerFormat);
Transform transform = new ScaleTransform(width / frame.Width * 96 / frame.DpiX, height / frame.Height * 96 / frame.DpiY, 0, 0);
// the jpeg encoder has a built-in flip & rotate system
if (encoder is JpegBitmapEncoder jpeg)
{
// exif is counter clockwise
switch (transformations.Rotation)
{
case Rotation.Rotate90:
jpeg.Rotation = Rotation.Rotate270;
break;
case Rotation.Rotate180:
jpeg.Rotation = Rotation.Rotate180;
break;
case Rotation.Rotate270:
jpeg.Rotation = Rotation.Rotate90;
break;
}
jpeg.FlipVertical = transformations.FlipVertical;
jpeg.FlipHorizontal = transformations.FlipHorizontal;
// option: change quality level here
// jpeg.QualityLevel = xx
}
else
{
// other codecs need transform
var group = new TransformGroup();
// we must flip before rotate
// https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/how-to-flip-a-uielement-horizontally-or-vertically
if (transformations.FlipHorizontal)
{
group.Children.Add(new ScaleTransform(-1, 1, 0.5, 0.5));
}
if (transformations.FlipVertical)
{
group.Children.Add(new ScaleTransform(1, -1, 0.5, 0.5));
}
// exif is counter clockwise
switch (transformations.Rotation)
{
case Rotation.Rotate90:
group.Children.Add(new RotateTransform(270));
break;
case Rotation.Rotate180:
group.Children.Add(new RotateTransform(180));
break;
case Rotation.Rotate270:
group.Children.Add(new RotateTransform(90));
break;
}
// I scale *after* rotate/flip, but it's up to you, not sure it changes anything in perf or quality...
group.Children.Add(transform);
transform = group;
}
var resized = BitmapFrame.Create(new TransformedBitmap(frame, transform));
encoder.Frames.Add(resized);
using (var stream = File.OpenWrite(outputFilePath))
{
encoder.Save(stream);
}
}
// helper class that exposes supported transformations (rotate/flip)
public class Transformations
{
public Transformations(BitmapMetadata md)
{
// https://docs.microsoft.com/en-us/uwp/api/windows.storage.fileproperties.photoorientation
// https://docs.microsoft.com/en-us/windows/win32/wic/-wic-photoprop-system-photo-orientation
// https://docs.microsoft.com/en-us/windows/win32/properties/props-system-photo-orientation
const string orientationProperty = "System.Photo.Orientation";
if (md != null && md.ContainsQuery(orientationProperty))
{
var orientation = (Orientation)md.GetQuery(orientationProperty);
switch (orientation)
{
case Orientation.FlipHorizontal:
FlipHorizontal = true;
break;
case Orientation.FlipVertical:
FlipVertical = true;
break;
case Orientation.Rotate90:
Rotation = Rotation.Rotate90;
break;
case Orientation.Rotate180:
Rotation = Rotation.Rotate180;
break;
case Orientation.Rotate270:
Rotation = Rotation.Rotate270;
break;
case Orientation.Transpose:
Rotation = Rotation.Rotate90;
FlipHorizontal = true;
break;
case Orientation.Transverse:
Rotation = Rotation.Rotate270;
FlipHorizontal = true;
break;
}
}
}
public Rotation Rotation { get; set; }
public bool FlipHorizontal { get; set; }
public bool FlipVertical { get; set; }
}
public enum Orientation : ushort
{
Undefined,
Normal,
FlipHorizontal,
Rotate180,
FlipVertical,
Transpose,
Rotate270,
Transverse,
Rotate90
}
// some WIC tool, need System.Runtime.InteropServices namespace
public static class WicUtilities
{
public static IEnumerable<Guid> EnumerateEncoderFormatsForExtension(string extension) => EnumerateFormatsForExtension(WICComponentType.WICEncoder, extension);
public static IEnumerable<Guid> EnumerateDecoderFormatsForExtension(string extension) => EnumerateFormatsForExtension(WICComponentType.WICDecoder, extension);
private static IEnumerable<Guid> EnumerateFormatsForExtension(WICComponentType type, string extension)
{
if (extension == null)
throw new ArgumentNullException(nameof(extension));
foreach (var info in EnumerateCodecs(type))
{
info.GetFileExtensions(0, null, out var len);
if (len >= 0)
{
var sb = new StringBuilder(len);
info.GetFileExtensions(len + 1, sb, out _);
var supportedExtensions = sb.ToString().Split(',');
if (supportedExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
if (info.GetContainerFormat(out var format) == 0)
yield return format;
}
}
}
}
private static IEnumerable<IWICBitmapCodecInfo> EnumerateCodecs(WICComponentType type)
{
var wfac = (IWICImagingFactory)new WICImagingFactory();
wfac.CreateComponentEnumerator(type, 0, out var unks);
if (unks != null)
{
var array = new object[1];
do
{
if (unks.Next(1, array, out var _) != 0)
break;
yield return (IWICBitmapCodecInfo)array[0];
}
while (true);
}
}
[Guid("CACAF262-9370-4615-A13B-9F5539DA4C0A"), ComImport]
private class WICImagingFactory { }
[Guid("00000100-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IEnumUnknown
{
[PreserveSig]
int Next(int celt, [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.IUnknown)] object[] rgelt, out int celtFetched);
// we don't need the rest
}
[Flags]
private enum WICComponentType
{
WICDecoder = 0x1,
WICEncoder = 0x2,
// we don't need the rest
}
[Guid("ec5ec8a9-c395-4314-9c77-54d7a935ff70"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IWICImagingFactory
{
void _VtblGap1_20(); // skip 20 methods we don't need
[PreserveSig]
int CreateComponentEnumerator(WICComponentType componentTypes, int options, out IEnumUnknown ppIEnumUnknown);
// we don't need the rest
}
[Guid("E87A44C4-B76E-4c47-8B09-298EB12A2714"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IWICBitmapCodecInfo
{
void _VtblGap1_8(); // skip 8 methods we don't need
[PreserveSig]
int GetContainerFormat(out Guid pguidContainerFormat);
void _VtblGap2_5(); // skip 5 methods we don't need
[PreserveSig]
int GetFileExtensions(int cchFileExtensions, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzFileExtensions, out int pcchActual);
// we don't need the rest
}
}
这篇关于使用WIC快速创建符合EXIF方向标记的按比例缩小的位图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!