Reputation: 11
We are trying to inject a TimestampAnnotation into the StructuredAnnotations section of an OME-Tiff file. I'm getting the annotation in there correctly, but when I try to reload the tiff, all the images are damaged. Is there an easier way to do this?
OME is a list of classes generated from the OME-tiff schema that we obtained (then generated our own) from
https://github.com/boonepeter/OME-Tiff-Csharp
private bool InjectTimeStampIntoSlide()
{
bool success = false;
try
{
using (Tiff image = Tiff.Open(SlideName, "a"))
{
// Image 0 must be the highest res image of the pyramid
image.SetDirectory(0);
// Get the Image Header for this image
TaggedImageHeader header = new TaggedImageHeader(image);
bool hasSubIfds = header.SubIfd > 0;
bool hasTiles = image.IsTiled();
if (hasSubIfds && hasTiles)
{
OME omeMetadata;
var fieldValueArray = image.GetField(TiffTag.IMAGEDESCRIPTION);
string omeMetadataStr = fieldValueArray[0].ToString();
XmlSerializer serializer = new XmlSerializer(typeof(OME));
using (TextReader tr = new StringReader(omeMetadataStr))
{
omeMetadata = (OME)serializer.Deserialize(tr);
}
// Convert to List
List<object> lstItems = new List<object>(omeMetadata.Items.ToList());
// Load StructuredAnnotations
StructuredAnnotations structuredAnnotations =
(StructuredAnnotations)lstItems.FirstOrDefault(
i => i.GetType() == typeof(StructuredAnnotations));
// We need the Index, so we can delete / replace it later
int structuralAnnotationsIdx =
lstItems.FindIndex(i => i.GetType() == typeof(StructuredAnnotations));
if (structuredAnnotations == null)
structuredAnnotations = new StructuredAnnotations();
// Get and / or create list of Annotations
List<Annotation> lstAnnotations = new List<Annotation>();
if (structuredAnnotations.Items?.Length > 0)
lstAnnotations = structuredAnnotations.Items.ToList();
// Add TimestampAnnotation
lstAnnotations.Add(new TimestampAnnotation()
{ Description = $"UserName: {Environment.UserName}", Value = DateTime.Now });
// Delete old Structured Annotations
if (structuralAnnotationsIdx >= 0)
lstItems.RemoveAt(structuralAnnotationsIdx);
// Add then add new one back
structuredAnnotations.Items = lstAnnotations.ToArray();
lstItems.Add((object)structuredAnnotations);
// Now update Metadata Items
omeMetadata.Items = lstItems.ToArray();
// And convert it to XML string
omeMetadataStr = string.Empty;
// Using StringWriter makes Encoding utf-16, and we need utf-8.
// So created the class ExtendedStringWriter, and now using
// StringBuilder to assist
StringBuilder sb = new StringBuilder();
using (ExtendedStringWriter sw = new ExtendedStringWriter(sb, Encoding.UTF8))
{
serializer.Serialize(sw, omeMetadata);
omeMetadataStr = sb.ToString();
}
image.SetField(TiffTag.IMAGEDESCRIPTION, omeMetadataStr);
success = image.WriteDirectory();
}
}
success = true;
}
catch (Exception ex)
{
Log4NetHelper.WriteExceptionToLog(Log, ex, GetType(), MethodBase.GetCurrentMethod().Name);
}
return success;
}
Upvotes: 1
Views: 212
Reputation: 14236
You are updating an existing directory with WriteDirectory
. This causes the data to be written in the file starting at the same location/offset. If the new size of the directory is larger than the previous one, the following data will be corrupted.
The proper way is to use RewriteDirectory
instead of WriteDirectory
. As stated in the documentation for this method:
The
RewriteDirectory
operates similarly toWriteDirectory
, but can be called with directories previously read or written that already have an established location in the file. It will rewrite the directory, but instead of place it at it's old location (as would) it will place them at the end of the file, correcting the pointer from the preceeding directory or file header to point to it's new location. This is particularly important in cases where the size of the directory and pointed to data has grown, so it won’t fit in the space available at the old location. Note that this will result in the loss of the previously used directory space.
Upvotes: 1