Phones often store a photo’s pixels sideways and record how to turn them in the EXIF orientation tag. Viewers and browsers apply the tag, so the photo looks right until your code resizes or re-saves it without applying it. With OneImage, Image.Load applies the tag, so the image is upright from the start and Save writes it upright. The program below uses a public test photo stored sideways.
dotnet new console -o ExifOrientation && cd ExifOrientation dotnet add package RecursiveAI.OneImage --version 1.10.4 repo=https://raw.githubusercontent.com/recurser/exif-orientation-examples curl -L -o phone.jpg $repo/master/Portrait_6.jpg
OneImage needs the .NET 10 SDK. Put this in Program.cs and run dotnet run:
using OneImage; // Identify reads the headers: the size as stored, and the tag. ImageInfo stored = Image.Identify("phone.jpg"); Console.WriteLine($"stored: {stored.Width}x{stored.Height}, " + $"orientation {stored.Metadata.Orientation}"); // Load applies the tag, so the image is upright from here on. Image photo = Image.Load("phone.jpg"); Console.WriteLine($"loaded: {photo.Width}x{photo.Height}, " + $"orientation {photo.Metadata.Orientation}"); // Save writes upright pixels and orientation 1, so no viewer turns it twice. photo.Save("upright.jpg"); ImageInfo saved = Image.Identify("upright.jpg"); Console.WriteLine($"saved: {saved.Width}x{saved.Height}, " + $"orientation {saved.Metadata.Orientation}");
stored: 1800x1200, orientation 6 loaded: 1200x1800, orientation 1 saved: 1200x1800, orientation 1
Width, Height, crops and resizes all work in upright coordinates, which is also how a browser shows the photo, so crop rectangles picked in a web page line up. The rest of the EXIF block (camera, date, GPS) is kept. Image.Identify is the exception: it reads only the headers and reports the size as stored.
What the eight values mean
Orientation is EXIF tag 274 (0x0112), defined in the Exif standard (CIPA DC-X008):
| Value | To show the stored pixels upright |
|---|---|
| 1 | Nothing |
| 2 | Flip horizontally |
| 3 | Rotate 180° |
| 4 | Flip vertically |
| 5 | Transpose (swap rows and columns) |
| 6 | Rotate 90° clockwise |
| 7 | Transverse (swap across the other diagonal) |
| 8 | Rotate 90° counterclockwise |
Values 5 to 8 swap width and height. OneImage handles all eight.
Keep the stored layout
To compare files or pass pixels on untouched, turn auto-orientation off and apply it later yourself:
using OneImage; Image stored = Image.Load("phone.jpg", new LoadOptions { AutoOrient = false }); Console.WriteLine($"{stored.Width}x{stored.Height}, " + $"orientation {stored.Metadata.Orientation}"); Image upright = stored.AutoOrient(); Console.WriteLine($"{upright.Width}x{upright.Height}, " + $"orientation {upright.Metadata.Orientation}");
1800x1200, orientation 6 1200x1800, orientation 1
If the orientation comes from somewhere else, such as a database column, Orient(6) applies a given value.
Limits
- Re-saving a JPEG re-encodes it. Fixing orientation on disk costs a little quality; skip files whose
Image.Identify(path).Metadata.Orientationis already 1. - GPS stays. Upright doesn’t mean private: remove the metadata before publishing, or use
Image.Thumbnail, which does both. - Coming from ImageSharp? Remove your
AutoOrient()calls;Loadalready applies the tag. See the migration guide.
To read the other EXIF fields, see Read EXIF data. Metadata handling is in the guide.