Read image with WPF
Read Pipeline Metadata TIFF Write Format Conversion WPF Stream
Demonstrates passing an image to a pipeline with the custom image reader.
Сode Snippet
public sealed class WpfImageReader : IImageReader, IImageParams
{
private readonly BitmapSource bitmapSource;
public WpfImageReader(System.IO.Stream stream)
{
this.bitmapSource = BitmapFrame.Create(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
if (this.bitmapSource.Metadata is BitmapMetadata metadata)
{
this.Exif = new ExifDictionary();
this.CaptureMetadata(metadata, string.Empty);
}
}
public Aurigma.GraphicsMill.Ink Ink => null;
public ColorPalette Palette => null;
public ColorProfile ColorProfile => null;
public float DpiX
{
get
{
return (float)this.bitmapSource.DpiX;
}
}
public float DpiY
{
get
{
return (float)this.bitmapSource.DpiY;
}
}
public PixelFormat PixelFormat
{
get
{
return ConvertFormat(this.bitmapSource.Format);
}
}
public int Height
{
get
{
return this.bitmapSource.PixelHeight;
}
}
public int Width
{
get
{
return this.bitmapSource.PixelWidth;
}
}
public ExifDictionary Exif { get; private set; }
public ImageParams GetImageParams()
{
return ImageParams.Create(this);
}
public Bitmap GetStripe(int stripeY, int stripeHeight)
{
// In production, it makes sense to consider more sophisticated logic
// for disposing stripe bitmaps. The possible approach is to dispose
// the previous stripe with generating the next one.
var stripe = new Bitmap(this.Width, stripeHeight, this.PixelFormat);
stripe.DpiX = this.DpiX;
stripe.DpiY = this.DpiY;
this.bitmapSource.CopyPixels(new System.Windows.Int32Rect(0, stripeY, stripe.Width, stripe.Height), stripe.Scan0, stripe.Stride * stripe.Height, stripe.Stride);
return stripe;
}
/// <summary>
/// Converts WPF pixel format to corresponding one for Graphics Mill.
/// </summary>
private static PixelFormat ConvertFormat(System.Windows.Media.PixelFormat format)
{
var dict = new Dictionary<System.Windows.Media.PixelFormat, Aurigma.GraphicsMill.PixelFormat>
{
{ System.Windows.Media.PixelFormats.Bgr24, Aurigma.GraphicsMill.PixelFormat.Format24bppRgb },
{ System.Windows.Media.PixelFormats.Bgr32, Aurigma.GraphicsMill.PixelFormat.Format32bppRgb },
{ System.Windows.Media.PixelFormats.Bgra32, Aurigma.GraphicsMill.PixelFormat.Format32bppArgb },
{ System.Windows.Media.PixelFormats.Gray8, Aurigma.GraphicsMill.PixelFormat.Format8bppGrayscale },
{ System.Windows.Media.PixelFormats.Gray16, Aurigma.GraphicsMill.PixelFormat.Format16bppGrayscale },
};
if (!dict.ContainsKey(format))
{
throw new Aurigma.GraphicsMill.UnsupportedPixelFormatException();
}
return dict[format];
}
private static UnsignedRational ToRational(ulong value)
{
var firstHalf = (uint)(value >> 32);
var secondHalf = (uint)(value & 0xffffffff);
return new UnsignedRational(firstHalf, secondHalf);
}
private static Rational ToRational(long value)
{
var firstHalf = (int)(value >> 32);
var secondHalf = (int)(value & 0xffffffff);
return new Rational(firstHalf, secondHalf);
}
private void WriteTag(long tag, object value)
{
if (this.Exif.Contains(tag))
{
return;
}
if (value is byte)
{
this.Exif.Add(tag, (byte)value);
}
else if (value is long)
{
this.Exif.Add(tag, ToRational((long)value));
}
else if (value is ulong)
{
this.Exif.Add(tag, ToRational((ulong)value));
}
else if (value is ushort)
{
this.Exif.Add(tag, (ushort)value);
}
else if (value is short)
{
this.Exif.Add(tag, (short)value);
}
else if (value is string)
{
this.Exif.Add(tag, value as string);
}
else if (value is ulong[])
{
var data = value as ulong[];
this.Exif.Add(tag, new UnsignedRational[] { ToRational(data[0]), ToRational(data[1]), ToRational(data[2]) });
}
else if (value is long[])
{
var data = value as long[];
this.Exif.Add(tag, new Rational[] { ToRational(data[0]), ToRational(data[1]), ToRational(data[2]) });
}
else
{
const long exifTagUserComment = 0x9286;
if (tag == exifTagUserComment)
{
var blob = value as BitmapMetadataBlob;
this.Exif.Add(tag, blob.GetBlobValue());
}
}
}
private void CaptureMetadata(BitmapMetadata metadata, string query)
{
foreach (var relativeQuery in metadata)
{
var queryReader = metadata.GetQuery(relativeQuery);
if (queryReader is BitmapMetadata innerMetadata)
{
this.CaptureMetadata(innerMetadata, query + relativeQuery);
}
else if (query.Contains("app1") || query.Contains("ifd"))
{
var m = Regex.Match(relativeQuery, @"\d+");
if (m.Length == 0)
{
continue;
}
var tag = long.Parse(m.Value);
WriteTag(tag, queryReader);
}
}
}
internal class ReadImageWithWPF
{
public static void Run()
{
using (var fs = new FileStream("IMG_4212.HEIC", FileMode.Open, FileAccess.Read))
using (var reader = new CustomImageReader(new WpfImageReader(fs)))
using (var writer = new TiffWriter("ReadImageWithWPF.tif"))
{
var wpfReader = (WpfImageReader)reader.InternalImageReader;
if (wpfReader.Exif != null)
{
writer.Exif = wpfReader.Exif;
foreach (object key in wpfReader.Exif.Keys)
{
Console.WriteLine("{0}: {1}, {2}", wpfReader.Exif.GetKeyDescription(key), wpfReader.Exif[key], wpfReader.Exif.GetItemString(key));
}
}
Pipeline.Run(reader + writer);
}
}
}
}
Input
IMG_4212.HEIC
DownloadOutput
ReadImageWithWPF.tif
DownloadReadImageWithWPF.txt
Image input equipment manufacturer: Apple, Apple
Image input equipment model: iPhone 8, iPhone 8
Image resolution in width direction: 1/72, 1/72
Image resolution in height direction: 1/72, 1/72
Resolution unit: 2, inches
Software used: 15.4.1, 15.4.1
File change date and time: 2022:06:23 13:02:55, 2022:06:23 13:02:55
Exposure time: 1372/1, 1372/1
F number: 5/9, 5/9
Exposure program: 2, normal
ISO speed rating: 20, 20
Date and time of original data generation: 2022:06:23 13:02:55, 2022:06:23 13:02:55
Date and time of digital data generation: 2022:06:23 13:02:55, 2022:06:23 13:02:55
Shutter speed: 16693/173971, 16693/173971
Aperture: 32325/54823, 32325/54823
Brightness: 132492/1249693, 132492/1249693
Exposure bias: 1/0, 1/0
Metering mode: 5, pattern
Flash: 16, flash did not fire
Lens focal length: 100/399, 100/399
DateTimeOriginal subseconds: 274, 274
DateTimeDigitized subseconds: 274, 274
Color space information: 65535, uncalibrated
Sensing method: 2, one-chip color area sensor
Exposure mode: 0, auto exposure
White balance: 0, auto white balance
Focal length in 35 mm film: 28, 28
Latitude: 1/54, 0.0185185/0.0175439/0.0323625
East or West Longitude: E, E
Longitude: 1/20, 0.05/0.037037/0.0187793
Altitude reference: 0, sea level
Altitude: 32603/40692, 32603/40692
Speed unit: K, Km/h
Speed of Gps receiver: 17411/21253, 17411/21253
Reference for direction of image: T, true direction
Direction of image: 6445/649856, 6445/649856
Reference for bearing of destination: T, true direction
Bearing of destination: 6445/649856, 6445/649856
GPS date: 2022:06:23, 2022:06:23
GPS horizontal positioning errors in meters: 1/24, 1/24
Interoperability identification: N, N
For AI-assisted development: Download Graphics Mill Code Samples XML Catalog