0

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?

gchq
  • 1,603
  • 2
  • 27
  • 52

1 Answers1

1

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.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
  • I looked up this link https://drewnoakes.com/code/exif/sampleUsage.html to get some more info, but I still cannot find JpegSegmentReader under MetadataExtractor - is it hidden under another class name? – gchq Sep 30 '17 at 19:03
  • It's in the `MetadataExtractor.Formats.Jpeg` namespace. – Drew Noakes Sep 30 '17 at 20:19
  • I just found that by trial and error before heading back here.. In your sample code 'Dim segmentReader As New JpegSegmentReader(jpegFile)' it throws the toys out with 'No new is accessible' What class is metadata in 'Dim metadata As New Metadata()?' – gchq Sep 30 '17 at 20:31
  • `Metadata` is a class from the Java implementation (the link you posted in your first comment relates to that, in general). `JpegSegmentReader` is a static class. You must use its static members. As the compiler says, you cannot new an instance of it. – Drew Noakes Sep 30 '17 at 20:42
  • I have decided to approach this from a different angle - return the EXIF data to string and save it separately with the image. I also need to get the orientation number. If it's 6 or 8 we can rotate the new image, but it's returning a string 'Exif IFD0 Orientation Right side, top (Rotate 90 CW)' instead. How do I get the number? Thank you for your patience :-) – gchq Sep 30 '17 at 23:20
  • Thank you for the update, and code sample. What namespace is ExifSubIfdDirectory under? – gchq Oct 01 '17 at 12:36
  • It's OK - I found it - MetadataExtractor.Formats.Exif – gchq Oct 01 '17 at 14:29
  • I have added an edit to the original question as ExifSubIfdDirectory always returns Nothing, even though I know an image does have an orientation code – gchq Oct 01 '17 at 22:23
  • Works perfectly now. Thank you. Whilst not an issue with MetadataExtractor I have an ongoing 'image is missing a frame' post that nobody seems to be able to answer, if that falls under your area of expertise? https://stackoverflow.com/questions/46409225/the-image-is-missing-a-frame-comexception-0x88982f62 – gchq Oct 02 '17 at 15:15