Repository navigation
#12 Use exif reader for tiff tags reading - #1494
Conversation
d608688 to
f9b1a27
Compare
f9b1a27 to
acf5f3b
Compare
brianpopow
left a comment
There was a problem hiding this comment.
@IldarKhayrutdinov thanks once again for your help.
| return false; | ||
| // sometimes duplicates appear, | ||
| // can compare val.Tag == exif.Tag | ||
| if (val == exif) |
There was a problem hiding this comment.
this is comparing by reference, this seems like a mistake.
There was a problem hiding this comment.
yeah, this is suspicious, but it used to work like this:
| @@ -31,7 +31,7 @@ public static T[] GetArray<T>(this TiffFrameMetadata meta, ExifTag tag, bool opt | |||
| public static bool TryGetArray<T>(this TiffFrameMetadata meta, ExifTag tag, out T[] result) | |||
There was a problem hiding this comment.
I'm not a fan of all these extension methods. ExifProfile has strong type (Get/Set)Value methods already.
There was a problem hiding this comment.
right, now can remove or simplify these methods
There was a problem hiding this comment.
@JimBobSquarePants I got rid of some methods, but most remained, they are useful as wrappers for ExifProfile methods - allow you to cast types for properties
There was a problem hiding this comment.
allow you to cast types for properties
I don't understand. The values returned by the existing method are already strong typed to the correct type without the need for internal calls and casting.
For example. This method is called upstream by the following in TiffDecoderMetadataCreator.
byte[] buf = frame.GetArray<byte>(ExifTag.XMP, true);
if (buf != null)
{
tiffMetadata.XmpProfile = buf;
}However, this could easily have been.
IExifValue<byte[]> xmp = frame.ExifProfile.GetValue(ExifTag.XMP);
if (xmp != null)
{
tiffMetadata.XmpProfile = xmp.Value;
}which uses the existing methods removing any code duplication and is better for someone reading the code since it indicates exactly where the metadata is coming from.
I do wish that ExifProfile.GetValue was ExifProfile.TryGetValue but we can't fix that without introducing a breaking change (I could be convinced to do that though).
There was a problem hiding this comment.
The same duplication is happening in the resolution getter/setter code.
We have existing methods in jpeg GetExifResolutionValue that demonstrate a much more simple approach using existing APIs.
There was a problem hiding this comment.
@JimBobSquarePants yes, you are right, it can be simplified even more, I did it
There was a problem hiding this comment.
Ace... I'll give it another review tonight. 👍
|
|
||
| private uint gpsOffset = 0; | ||
|
|
||
| public ExifReader(bool isBigEndian, Stream stream) |
There was a problem hiding this comment.
I think these parameters should be reversed.
There was a problem hiding this comment.
yeah, it looks more logical
| } | ||
|
|
||
| public ExifReader(byte[] exifData) => | ||
| this.data = new MemoryStream(exifData ?? throw new ArgumentNullException(nameof(exifData))); |
There was a problem hiding this comment.
What about endianness?
There was a problem hiding this comment.
endianness will readed later:
the class turned out to be a little universal, it can make 2 inheritor for tiff and exif, what do you think?
There was a problem hiding this comment.
@JimBobSquarePants it seems, worth to make 2 inheritor for tiff and exif, it will look better
There was a problem hiding this comment.
@JimBobSquarePants please review again, I split the classes and they seems to be looking better now
|
|
||
| public override IExifValue DeepClone() => new ExifNumberArray(this); | ||
|
|
||
| private bool SetSingle<T>(T value, Func<T, Number> converter) |
There was a problem hiding this comment.
TrySetSingle
TrySetArray
There was a problem hiding this comment.
These methods return the value for simplicity case construction, they will always return true:
|
@JimBobSquarePants @brianpopow I can't reply to your comments, maybe it's because of Assignees |
i dont know why that is. I have removed myself from assignees. Can you try again @IldarKhayrutdinov ? |
|
strange, it doesn't work |
| /// <summary> | ||
| /// Gets the RowsPerStrip exif tag. | ||
| /// </summary> | ||
| public static ExifTag<Number> RowsPerStrip { get; } = new ExifTag<Number>(ExifTagValue.RowsPerStrip); |
There was a problem hiding this comment.
RowsPerStrip dataType is short or long - https://www.awaresystems.be/imaging/tiff/tifftags/rowsperstrip.html
|
|
||
| if (coreMetadata.ExifProfile == null) | ||
| { | ||
| byte[] buf = frame.GetArray<byte>(ExifTag.SubIFDOffset, true); |
There was a problem hiding this comment.
now SubIFD and subGps are read to common TiffFrameMetaData.FrameTags list
|
|
||
| // used for sequential read big values (actual for multiframe big files) | ||
| // todo: different tags can link to the same data (stream offset) - investigate | ||
| private readonly SortedList<uint, Action> lazyLoaders = new SortedList<uint, Action>(new DuplicateKeyComparer<uint>()); |
There was a problem hiding this comment.
DuplicateKeyComparer used for possiblity add a duplicate offsets (but tags don't duplicate)
| foreach (IExifValue val in values) | ||
| { | ||
| return false; | ||
| // sometimes duplicates appear, |
There was a problem hiding this comment.
note, I added a reproduction of the problem with duplicates, these tests work successfully:
| Assert.Equal(expectedLatitude, latitude.Value); | ||
|
|
||
| // todo: duplicate tags | ||
| Assert.Equal(2, image.Metadata.ExifProfile.Values.Count(v => (ushort)v.Tag == 59932)); |
There was a problem hiding this comment.
note: this PR doesn't affected, it was earlier
happened! |
|
Did you mean to close this? I thought it just needed a little work. |
sorry, I myself did not understand how it happened |
|
@JimBobSquarePants @brianpopow I pushed some small improvements, please review. Are you changes requested? |
| private static ByteOrder ReadByteOrder(Stream stream) | ||
| { | ||
| if (this.ReadHeader()) | ||
| var headerBytes = new byte[2]; |
There was a problem hiding this comment.
sorry, do you mean doing new Span(stackalloc byte[2]) ?
| return null; | ||
| } | ||
| } | ||
| protected override void RegisterExtLoader(uint offset, Action reader) => throw new NotImplementedException(); |
There was a problem hiding this comment.
I'm assuming this is a feature waiting to be implemented?
There was a problem hiding this comment.
The HeaderReader class use one time to read only header. I changed exception type to NotSupported.
I tried to make fewer changes within the PR.
It was possible to move the stream reading methods into separate classes (as was in tiff), this would remove HeaderReader class and slightly simplify the exif/tiff readers.
brianpopow
left a comment
There was a problem hiding this comment.
I think this can be merged now, but i leave the final verdict to @JimBobSquarePants
|
@brianpopow if you’re happy, I’m happy. |
|
Thanks @IldarKhayrutdinov !! |
|
It was crazy PR 🙃 |
#12 Use exif reader for tiff tags reading
#12 Use exif reader for tiff tags reading
Prerequisites
Description
I was afraid to make a lot of changes to exif classes, so it turned out a little clumsy. Don't hesitate to write comments 😉
Tests are works correctly and successful.
The logic of Exif reading has not changed.
The difficulty was in supporting multi-page Tiff files, where IDF tags and their extended data (> 4 bytes) can be scattered throughout the file in no particular order, and file can be very large.