From ac58565b9fe6475b6bf851b9f3a23f03e76ac06b Mon Sep 17 00:00:00 2001 From: dirk Date: Sat, 13 Aug 2016 18:54:04 +0200 Subject: [PATCH] Added option to change the orientation of an image based on the information in the Exif profile. Former-commit-id: ae4de1addaed801930aea365f4bc0a87d03f6e3f Former-commit-id: bb1e39e2ef759b391e88b7f3fbfd98ea97913b3f Former-commit-id: 97d2de47e68c5dafc524656ec3d95c1c1f72e7c2 --- src/ImageProcessorCore/Samplers/AutoOrient.cs | 84 ++++++++++++++++++ .../Samplers/Options/Orientation.cs | 20 +++++ .../Processors/Samplers/AutoOrientTests.cs | 60 +++++++++++++ tests/ImageProcessorCore.Tests/TestImages.cs | 3 + .../TestImages/Formats/Bmp/F.bmp | Bin 0 -> 65502 bytes 5 files changed, 167 insertions(+) create mode 100644 src/ImageProcessorCore/Samplers/AutoOrient.cs create mode 100644 src/ImageProcessorCore/Samplers/Options/Orientation.cs create mode 100644 tests/ImageProcessorCore.Tests/Processors/Samplers/AutoOrientTests.cs create mode 100644 tests/ImageProcessorCore.Tests/TestImages/Formats/Bmp/F.bmp diff --git a/src/ImageProcessorCore/Samplers/AutoOrient.cs b/src/ImageProcessorCore/Samplers/AutoOrient.cs new file mode 100644 index 0000000000..62c7a55845 --- /dev/null +++ b/src/ImageProcessorCore/Samplers/AutoOrient.cs @@ -0,0 +1,84 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Adjusts an image so that its orientation is suitable for viewing. + /// + /// The pixel format. + /// The packed format. long, float. + /// The image to crop. + /// The + public static Image AutoOrient(this Image source, ProgressEventHandler progressHandler = null) + where T : IPackedVector + where TP : struct + { + Orientation orientation = GetExifOrientation(source); + + switch (orientation) + { + case Orientation.Unknown: + case Orientation.TopLeft: + default: + return source; + + case Orientation.TopRight: + return source.Flip(FlipType.Horizontal, progressHandler); + + case Orientation.BottomRight: + return source.Rotate(RotateType.Rotate180, progressHandler); + + case Orientation.BottomLeft: + return source.Flip(FlipType.Vertical, progressHandler); + + case Orientation.LeftTop: + return source + .Rotate(RotateType.Rotate90, progressHandler) + .Flip(FlipType.Horizontal, progressHandler); + + case Orientation.RightTop: + return source.Rotate(RotateType.Rotate90, progressHandler); + + case Orientation.RightBottom: + return source + .Flip(FlipType.Vertical, progressHandler) + .Rotate(RotateType.Rotate270, progressHandler); + + case Orientation.LeftBottom: + return source.Rotate(RotateType.Rotate270, progressHandler); + } + } + + private static Orientation GetExifOrientation(Image source) + where T : IPackedVector + where TP : struct + { + if (source.ExifProfile == null) + { + return Orientation.Unknown; + } + + ExifValue value = source.ExifProfile.GetValue(ExifTag.Orientation); + if (value == null) + { + return Orientation.Unknown; + } + + Orientation orientation = (Orientation)value.Value; + + source.ExifProfile.SetValue(ExifTag.Orientation, (ushort)Orientation.TopLeft); + + return orientation; + } + } +} diff --git a/src/ImageProcessorCore/Samplers/Options/Orientation.cs b/src/ImageProcessorCore/Samplers/Options/Orientation.cs new file mode 100644 index 0000000000..7ace8ab2d4 --- /dev/null +++ b/src/ImageProcessorCore/Samplers/Options/Orientation.cs @@ -0,0 +1,20 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore +{ + internal enum Orientation : ushort + { + Unknown = 0, + TopLeft = 1, + TopRight = 2, + BottomRight = 3, + BottomLeft = 4, + LeftTop = 5, + RightTop = 6, + RightBottom = 7, + LeftBottom = 8 + } +} diff --git a/tests/ImageProcessorCore.Tests/Processors/Samplers/AutoOrientTests.cs b/tests/ImageProcessorCore.Tests/Processors/Samplers/AutoOrientTests.cs new file mode 100644 index 0000000000..e9d93ac3c0 --- /dev/null +++ b/tests/ImageProcessorCore.Tests/Processors/Samplers/AutoOrientTests.cs @@ -0,0 +1,60 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Tests +{ + using System.IO; + using Xunit; + + public class AutoOrientTests : FileTestBase + { + public static readonly TheoryData OrientationValues + = new TheoryData + { + { RotateType.None, FlipType.None, 0 }, + { RotateType.None, FlipType.None, 1 }, + { RotateType.None, FlipType.Horizontal, 2 }, + { RotateType.Rotate180, FlipType.None, 3 }, + { RotateType.Rotate180, FlipType.Horizontal, 4 }, + { RotateType.Rotate90, FlipType.Horizontal, 5 }, + { RotateType.Rotate270, FlipType.None, 6 }, + { RotateType.Rotate90, FlipType.Vertical, 7 }, + { RotateType.Rotate90, FlipType.None, 8 }, + }; + + [Theory] + [MemberData("OrientationValues")] + public void ImageShouldFlip(RotateType rotateType, FlipType flipType, ushort orientation) + { + const string path = "TestOutput/AutoOrient"; + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + string file = TestImages.Bmp.F; + + using (FileStream stream = File.OpenRead(file)) + { + string filename = Path.GetFileNameWithoutExtension(file) + "-" + orientation + Path.GetExtension(file); + + Image image = new Image(stream); + image.ExifProfile = new ExifProfile(); + image.ExifProfile.SetValue(ExifTag.Orientation, orientation); + + using (FileStream before = File.OpenWrite($"{path}/before-{filename}")) + { + using (FileStream after = File.OpenWrite($"{path}/after-{filename}")) + { + image.RotateFlip(rotateType, flipType) + .Save(before) + .AutoOrient() + .Save(after); + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageProcessorCore.Tests/TestImages.cs b/tests/ImageProcessorCore.Tests/TestImages.cs index f9c5bb2433..f8379d9f0f 100644 --- a/tests/ImageProcessorCore.Tests/TestImages.cs +++ b/tests/ImageProcessorCore.Tests/TestImages.cs @@ -38,6 +38,9 @@ namespace ImageProcessorCore.Tests private static readonly string folder = "TestImages/Formats/Bmp/"; public static string Car { get { return folder + "Car.bmp"; } } + + public static string F { get { return folder + "F.bmp"; } } + public static string Neg_height { get { return folder + "neg_height.bmp"; } } } diff --git a/tests/ImageProcessorCore.Tests/TestImages/Formats/Bmp/F.bmp b/tests/ImageProcessorCore.Tests/TestImages/Formats/Bmp/F.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a618074e17d86d4db7ee8dcc8531578b8f1f70e5 GIT binary patch literal 65502 zcmeI)v29dA6a~;v7sQUp$S8mu(Fldn0R@l|xC9!Yi0?1U(Hj|g?hV)#$MUZ9#`CVM z;?J+J!IGm^a*;%uovhP^pIgM&?o32!(O0I&_jm3K%bz8410k-K@S=B z0)2uWGVBHV1U+Qf3-k$k$gmga6ZDW_FVH9GA;VsvPtZe#y+EI!hYWjxK0yx|_5yu^ z9y06&`UE{>*bDRtddRRB=o9piVK2}p=pn;ipij_4hP^ zHdYu8kYHpOpp6xV10)z3254i2;Q$Fnh5_1GVK_j7kzs%~Ru~SDU}PAejTMFiBp4Y6 zXk&%p00~Bh0oqt$I6#7tVSqMP7!HtNWEh~06@~*O7#Rj=V};=W2}Xti+E`&YK!TBB zfHqbb4v=7E7@&<6h65xR83t%$h2a1RMuq{}SYbFof{|f>HdX?|JAHr3U*1sjI8Zjn zz;JyF(!HO2-9#JL8D0fo*#8$WWY`Pz33|w|7w8l8kYO*-C+H!=UZ79VLx#OTpP+{f zdx1Wo?BRGB$4Gzx0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF J5Fn5WJOO=HTk!w@ literal 0 HcmV?d00001