.netexifmetadata-extractor

Using MetaDataExtractor to add EXIF back to resized image


Using an MVC application to upload an image, we need to reduce the file size before saving to the DB, but want to retain the EXIF data... The only way I can think of this is to get this from the original uploaded image and then add it to the resized one, using MetadataExtractor

We can get the metadata like this

 Dim vMetadata As IEnumerable(Of System.IO.Directory) = MetadataExtractor.ImageMetadataReader.ReadMetadata(file.InputStream)

Then resize the image like this

fext = IO.Path.GetExtension(file.FileName).ToLower
Dim vLen As Integer = file.ContentLength
Dim vData(vLen - 1) As Byte
Dim image_file As System.Drawing.Image = System.Drawing.Image.FromStream(file.InputStream)
Dim image_height As Integer = image_file.Height
Dim image_width As Integer = image_file.Width
Dim max_height As Integer = 240
Dim max_width As Integer = 320

image_height = (image_height * max_width) / image_width
image_width = max_width

If image_height > max_height Then
    image_width = (image_width * max_height) / image_height
    image_height = max_height
End If

Dim bitmap_file As New System.Drawing.Bitmap(image_file, image_width, image_height)

Using vStream As New IO.MemoryStream
    Select Case fext
        Case ".jpg"
            bitmap_file.Save(vStream, System.Drawing.Imaging.ImageFormat.Jpeg)
        Case ".jpeg"
            bitmap_file.Save(vStream, System.Drawing.Imaging.ImageFormat.Jpeg)
        Case ".png"
            bitmap_file.Save(vStream, System.Drawing.Imaging.ImageFormat.Png)
        Case ".gif"
            bitmap_file.Save(vStream, System.Drawing.Imaging.ImageFormat.Gif)
        Case Else
            bitmap_file.Save(vStream, System.Drawing.Imaging.ImageFormat.Jpeg)
    End Select
    vStream.Position = 0
    vStream.Read(vData, 0, vStream.Length)
    vImageFile = vData

    vData = Nothing
End Using

So we have the metadata saved as Dictionary, and the reduced size image file as the variable vImageFile that we can now save...

The question is - how do we add that metadata back to the new file?

Thank you

------------------- Edit added 01 October 2017 --------------------

I have added this code

Dim vOrientationNumber As Integer = 1

                    Dim vEXIF As String = ""
                    Dim vDirectories = ImageMetadataReader.ReadMetadata(file.InputStream)
                    Dim vSub = vDirectories.OfType(Of ExifSubIfdDirectory)().FirstOrDefault
                    If Not vSub Is Nothing Then
                        Dim vOrientationObj = vSub.GetObject(ExifDirectoryBase.TagOrientation)
                        If Not vOrientationObj Is Nothing Then
                            If Not vOrientationObj.Equals(DBNull.Value) Then
                                vOrientationNumber = Convert.ToInt16(vOrientationObj)
                            End If
                        End If
                    End If

                    For Each vDirectory In vDirectories
                        For Each Tag In vDirectory.Tags
                            vEXIF += vDirectory.Name & " " & Tag.Name & " " & Tag.Description & Environment.NewLine
                        Next
                    Next

to get a handle on the Orientation number, but the variable vSub is always Nothing. I know with this image the orientation number is there (as it finds it in the main WPF desktop app and rotates it). Any idea what I could be doing wrong now?


Solution

  • MetadataExtractor doesn't support writing metadata to files. It's a popular feature request, but to do it properly (which is obviously essential given people will likely overwrite their files) will take some work.

    However to do this the library does provide some code that might be useful, so long as you're dealing with JPEG files.

    JPEG files are basically a list of so-called JPEG segments. The Exif data lives within one of those segments. So if you isolate that segment in the original image, you can replace it after you've resized it.

    I don't have any code for this unfortunately. You can use JpegSegmentReader to extract the segment(s) you need (Exif is in JpegSegmentType.App1) which should get you started.


    The string value you are seeing is a description. To access the raw orientation value, use code like this:

    var directories = ImageMetadataReader.ReadMetadata(imagePath);
    
    var subIfd = directories.OfType<ExifIfd0Directory>().FirstOrDefault();
    
    int? orientation = subIfd?.GetObject(ExifDirectoryBase.TagOrientation);
    

    Note that both subIfd and orientation can be null, depending upon the image.

    It's C# as I don't know VB.NET, sorry. Hopefully it's a straightforward conversion for you.