You are viewing limited content. For full access, please sign in.

Question

Question

Scanner Orientation EXIF Data Auto-rotation

asked on September 9, 2016

Hi all,

I'm using a scanner which includes hardware auto-rotation.

Except the scanner doesn't rotate the image, instead it puts a value in the EXIF "Orientation" field. Quickfields 9.x doesn't seem to read this flag because my documents, although correctly oriented in LF Scanning, end up the wrong way up in QF. Does QF 10 have any options to read the EXIF data for orientation?

  http://www.impulseadventure.com/photo/exif-orientation.html

 

-Ben

 

0 0

Replies

replied on September 9, 2016

I hope to get a quiet moment to install 10 and find out for myself but in the mean time, I hope some one has checked already...

0 0
replied on September 13, 2016

Hi Ben, 

Can you provide an example image for which LF Scanning seems to read the EXIF orientation data correctly? 

0 0
replied on September 15, 2016 Show version history

I can't! I'd love to! After installing QF10, I've been unable to replicate the problem. Perhaps close this thread and I'll post again when I'm able to find a reliable way to replicate the issue. There's probably more to it...

 

-Ben

 

0 0
replied on September 15, 2016

Ah okay, let us know if you're able to reproduce it!

 

Here's some more background:

I don't think Quick Fields (9 or 10) or Laserfiche Scanning uses the EXIF orientation flag. If it seemed to be working in Laserfiche Scanning I suspect it may have actually been something else going on (possibly a bug). 

That having been said, I'll consider it a feature request for Quick Fields to be able to read the flag and rotate the image accordingly (and then probably remove the flag from the file afterwards). 

0 0
replied on September 15, 2016

This is a sample C# program for reading EXIF orientation and then extracting the pages with the orientation applied.

This sample code is provided as-is, without expressed or implied warranty.

Notes

  • This program has dependency on:
    • .NET 4.5 or above,
    • WindowsBase
    • PresentationCore
    • LaserficheImaging
  • This program was tested on Windows 7. This program depends on components that are provided by Microsoft as part of the Windows platform. Earlier versions of Windows may have platform defects that affect this program's functionality.
  • This program is intended to process images before they are uploaded to Laserfiche, or during Quick Fields custom processing.
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Media.Imaging;
using Laserfiche.Imaging;

namespace ExifRotateDemo
{
    public static class ExifRotateDemoUtility
    {
        public static void ExtractPageWithRotation(string filename, string outputFolder)
        {
            string inputFilenamePart = Path.GetFileNameWithoutExtension(filename);
            Func<int, string> outputFilenameFunc = (int pageIndex) =>
            {
                return Path.Combine(outputFolder, inputFilenamePart + "_page" + pageIndex + "_converted.tif");
            };
            using (MemoryStream strm = new MemoryStream(File.ReadAllBytes(filename), false))
            {
                if (!IsTiffOrJpeg(strm))
                {
                    string pageOutputFilename = outputFilenameFunc(1);
                    File.Copy(filename, pageOutputFilename);
                }
                else
                {
                    var pageOrientations = GetRotationForImagePages(strm);
                    var pageCodecs = GetImagePageCodecs(strm);
                    var converter = new LfiImageConverter();
                    foreach (var pageIndexAndCodec in pageCodecs)
                    {
                        int pageIndex = pageIndexAndCodec.Key;
                        var codec = pageIndexAndCodec.Value;
                        if (codec == LfiBitmapCodec.Unknown)
                        {
                            codec = LfiBitmapCodec.TiffLzw;
                        }
                        if (codec == LfiBitmapCodec.FaxGroup3)
                        {
                            codec = LfiBitmapCodec.FaxGroup4;
                        }
                        int rotation;
                        if (!pageOrientations.TryGetValue(pageIndex, out rotation))
                        {
                            rotation = 0;
                        }
                        string pageOutputFilename = outputFilenameFunc(pageIndex + 1);
                        if (rotation == 0)
                        {
                            using (var outStream = File.OpenWrite(pageOutputFilename))
                            {
                                converter.ConvertFile(strm, pageIndex, outStream, LfiContainerFormat.Tiff, codec);
                            }
                        }
                        else
                        {
                            using (var bitmap = DecodeBitmapPage(strm, pageIndex))
                            {
                                using (var rotatedBitmap = new LfiRotatedBitmap(bitmap, rotation, LfiRotationOptions.AllowResize))
                                {
                                    SaveBitmapPage(pageOutputFilename, rotatedBitmap, codec);
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Given the EXIF orientation flag value, return the degrees of rotation
        /// required to normalize the image to the intended orientation.
        /// </summary>
        /// <param name="exifOrientationValue"></param>
        /// <returns></returns>
        static int ExifOrientationToRotationDegrees(int exifOrientationValue)
        {
            switch (exifOrientationValue)
            {
                case 1:
                    return 0;
                case 3:
                    return 180;
                case 6:
                    return 90;
                case 8:
                    return 270;
                default:
                    throw new ArgumentOutOfRangeException(
                        "Invalid EXIF orientation value, or an orientation that is flipped which cannot be corrected with rotation alone.");
            }
        }

        static int? TryMetadataGetQueryConvertAsInt(BitmapMetadata metadata, string query)
        {
            var unknownResult = metadata.GetQuery(query);
            if (unknownResult is IConvertible)
            {
                return (unknownResult as IConvertible).ToInt32(null);
            }
            return null;
        }

        static SortedDictionary<int, int> GetRotationForImagePages(Stream stream)
        {
            stream.Seek(0, SeekOrigin.Begin);
            var result = new SortedDictionary<int, int>();
            BitmapDecoder decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.DelayCreation, BitmapCacheOption.Default);
            string query;
            if (decoder is TiffBitmapDecoder)
            {
                query = "/ifd/{ushort=274}";
            }
            else if (decoder is JpegBitmapDecoder)
            {
                query = "/app1/ifd/exif:{ushort=274}";
            }
            else
            {
                return result;
            }
            int nextFrame = 0;
            foreach (var frame in decoder.Frames)
            {
                int frameIndex = nextFrame;
                nextFrame++;
                var unknownMetadata = frame.Metadata;
                if (unknownMetadata is BitmapMetadata)
                {
                    var metadata = (unknownMetadata as BitmapMetadata);
                    var orientation = TryMetadataGetQueryConvertAsInt(metadata, query);
                    if (orientation.HasValue)
                    {
                        int degrees;
                        try
                        {
                            degrees = ExifOrientationToRotationDegrees(orientation.Value);
                        }
                        catch
                        {
                            continue;
                        }
                        result.Add(frameIndex, degrees);
                    }
                }
            }
            stream.Seek(0, SeekOrigin.Begin);
            return result;
        }

        static bool IsTiffOrJpeg(Stream stream)
        {
            stream.Seek(0, SeekOrigin.Begin);
            LfiBitmapDecoder lfiDecoder = new LfiBitmapDecoder(stream, BitmapCreateOptions.None);
            var container = lfiDecoder.ContainerFormat;
            stream.Seek(0, SeekOrigin.Begin);
            return (container == LfiContainerFormat.Tiff ||
                container == LfiContainerFormat.Jpeg);
        }

        static SortedDictionary<int, LfiBitmapCodec> GetImagePageCodecs(Stream stream)
        {
            stream.Seek(0, SeekOrigin.Begin);
            LfiBitmapDecoder lfiDecoder = new LfiBitmapDecoder(stream, BitmapCreateOptions.None);
            var result = new SortedDictionary<int, LfiBitmapCodec>();
            int pageCount = lfiDecoder.Frames.Count;
            for (int pageIndex = 0; pageIndex < pageCount; ++pageIndex)
            {
                var frame = lfiDecoder.Frames[pageIndex];
                result.Add(pageIndex, frame.Codec);
            }
            stream.Seek(0, SeekOrigin.Begin);
            return result;
        }

        static LfiBitmapSource DecodeBitmapPage(Stream stream, int pageIndex)
        {
            stream.Seek(0, SeekOrigin.Begin);
            LfiBitmapDecoder lfiDecoder = new LfiBitmapDecoder(stream, BitmapCreateOptions.None);
            return lfiDecoder.Frames[pageIndex].DecodeBitmap();
        }

        static void SaveBitmapPage(string outputFilename, LfiBitmapSource bitmap, LfiBitmapCodec codec)
        {
            LfiBitmapEncoder lfiEncoder = new LfiBitmapEncoder(LfiContainerFormat.Tiff);
            lfiEncoder.Overwrite = false;
            lfiEncoder.Append = false;
            lfiEncoder.Frames.Add(new LfiBitmapFrame(bitmap, codec));
            lfiEncoder.Save(outputFilename);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            ExifRotateDemoUtility.ExtractPageWithRotation(@"inputFilename", @"outputFolder");
        }
    }
}

 

0 0
You are not allowed to follow up in this post.

Sign in to reply to this post.