我正在尝试创建一个PNG文件来保存我们捕获的图像数据,但我真的很痛苦,试图设置像公司,CameraManufacturer,Brightness等元数据。
Google PNGCS
库会这样做,但我必须先写入文件,然后重新加载并重新保存。
我们正在使用WPF,所以我认为我可以使用BitmapMetadata
,PngBitmapEncoder
,JpegBitmapEncoder
等更通用的类来实现。
然而,我一直反对threw an exception of type "System.NotSupportedException"
我想要使用的属性。
您可以在变量watch
中看到这一点,我在此帖子的底部放置了创建的元数据。
Image类实际上是一个包含宽度高度的数据结构,文件类型如png或gif等。
应该直接从相机获取图像数据,添加我们想要的标签并将其保存到文件中。
与负载相同 - 我们应该能够得到它们。
代码:
/// <summary>
/// Handles the load, save, and export of images
/// </summary>
public interface IImageProvider
{
string GetPath(string fileNameWithoutExtension, ImageVersion version);
string GetPath(Plate plate);
Image Load(string path);
Image Load(string path, int width);
Image Load(Plate plate);
Image LoadThumb(Plate plate);
Task<Image> LoadAsync(Plate plate);
Task<Image> LoadAsync(Plate plate, ImageVersion version);
void Save(Image image, string path, Resolution resolution);
void Save(Image image, string name, ImageCategories category, Resolution resolution);
void Save(Plate plate, Image image, Resolution resolution);
Task SaveAsync(Image image, string path, Resolution resolution);
void Export(Image image, string name, ExportFormats format, string directory, Resolution resolution);
void ExportAsync(Image image, string path, ExportFormats format, string directory, double increment, Resolution resolution);
Task<Image> Import(string fileName, Project project);
void Delete(Plate plate);
Image Load(Plate plate, IStage stage);
}
class ImageProvider
{
private void Save(Image image, string name, ExportFormats format, string directory, Resolution resolution)
{
BitmapEncoder bitmapEncoder = null;
//Determine the type of the export
switch (format)
{
case ExportFormats.bmp:
bitmapEncoder = new BmpBitmapEncoder();
break;
case ExportFormats.jpg:
bitmapEncoder = new JpegBitmapEncoder();
break;
case ExportFormats.png:
bitmapEncoder = new PngBitmapEncoder();
break;
case ExportFormats.tiff:
bitmapEncoder = new TiffBitmapEncoder();
break;
}
if (bitmapEncoder != null)
{
var source = ResizeToResolutionUniform(image, resolution);
ReadOnlyCollection<ColorContext> colorContexts = null;
BitmapMetadata metadata = new BitmapMetadata("png");
BitmapSource thumbnail = null;
BitmapFrame bitmapFrame = BitmapFrame.Create(source, thumbnail, metadata, colorContexts);
bitmapEncoder.Frames.Add(bitmapFrame);
//Get the folder and extension
var extension = Enum.GetName(typeof(ExportFormats), format);
var path = Path.Combine(directory, name + "." + extension);
//Create the directory if needed
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
Directory.CreateDirectory(directory);
if (image.ProcessingSettings != null)
{
BitmapMetadata bmp = bitmapEncoder.Frames[0].Metadata as BitmapMetadata;
BitmapMetadata bitmapMetadata = (BitmapMetadata)bitmapEncoder.Frames[0].Metadata;//bitmapEncoder.Metadata;
if (!AddMetatDataTags(bitmapMetadata, image.ProcessingSettings))
LogW($"Could not to {path}");
}
using (FileStream fileStream = new FileStream(path, FileMode.Create))
{
//Save the image
bitmapEncoder.Save(fileStream);
}
}
}
public bool AddMetatDataTags(BitmapMetadata bitmapMetadata, IImageProcessingSettings settings)
{
CultureInfo culture = CultureInfo.InvariantCulture;
var ret = false;
if (bitmapMetadata != null)
{
// 1 off invariant information
// Note: the PNG specification does not support many of these metadata properties (which are based on EXIF, which is NOT by the PNG spec)
bitmapMetadata.SetQuery("/tEXt/Author", Environment.UserName); //Environment.UserName;
object obj = bitmapMetadata.GetQuery("/tEXt/Author");
string s = obj.ToString();
AddKey(bitmapMetadata, 1, "CameraManufacturer", "Singer Instrument Company Limited");
AddKey(bitmapMetadata, 2, "CameraModel", "Phenobooth");
AddKey(bitmapMetadata, 0, "ApplicationName", "PhenoSuite");
AddKey(bitmapMetadata, 3, "Brightness", Convert.ToString(settings.Brightness, culture)); // Capture specific data
AddKey(bitmapMetadata, 4, "Exposure", Convert.ToString(settings.Exposure, culture));
AddKey(bitmapMetadata, 5, "Gain", Convert.ToString(settings.Gain, culture));
//...
ret = true;
}
return ret;
}
/*
* The tag dictionary in the bitmap properties has a strange implementation - based on a separated key value pair
* if n = 0 entry like: /iTXt/Keyword = key /iTXt/TextEntry = val
* if n > 0 entry like: /[n]iTXt/Keyword = key /[n]iTXt/TextEntry = val
*/
private void AddKey(BitmapMetadata metaData, int n, string key, string val)
{
var _key = string.Format($"iTXt/{0}{1}{2}", (n > 0) ? "[" : "", (n > 0) ? n.ToString() : "", (n > 0) ? "]" : "");
try
{
metaData.SetQuery(_key + "Keyword", key.ToCharArray()); // need to convert using ToCharArray as internal representation is based on the LPSTR C type
metaData.SetQuery(_key + "TextEntry", val.ToCharArray());
}
catch (Exception e)
{
LogE($"Could not add metadata key:{key} index: {n} {e.Message}");
throw;
}
}
public class Image
{
#region Constructors
/// <summary>
/// Creates an image optionally copying the metadata
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="format"></param>
/// <param name="_imageMetadata"></param>
public Image(int width, int height, PixelFormat format, ImageMetadata _imageMetadata) : this()
{
Width = width;
Height = height;
Format = format;
ImageMetadata = _imageMetadata;
}
...
}
当我查看创建的元数据时,有许多不受支持的属性 - 事实上,我已经尝试了各种方法来解决这个问题,我确信这已经过去了,但是我无法解决所发生的变化。
新元数据对象上watch
的示例:Meta Data
答案 0 :(得分:0)
以下是我用来从iTXt png元数据中获取键值对的代码
public static class ImageUtils
{
/// <summary>
/// Captures all or part of the raw png metadata.
/// Can use this to capture PhonoBooth metadata by setting the filter to "iTXt"
///
/// throws: ArgumentException if not a png file
/// </summary>
/// <param name="imageFilePath"></param>
/// <param name="itemMap"></param>
/// <param name="filter"> optional filter on the key (contains)</param>
/// <returns>true if successful, false otherwise.</returns>
public static bool GetMetaDataItems(string imageFilePath, ref Dictionary<string, string> itemMap, string filter=null)
{
Assertion<ArgumentException>(imageFilePath.ToLower().EndsWith(".png"), "Expected png file");
var ret = false;
var query = string.Empty;
itemMap.Clear();
try
{
using (Stream fileStream = File.Open(imageFilePath, FileMode.Open))
{
var decoder = BitmapDecoder.Create(fileStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
GetMetaDataItems(decoder.Frames[0].Metadata as BitmapMetadata, ref itemMap, filter);
}
ret = true;
}
catch (Exception e)
{
ret = false;
LogE(e.Message);
}
return ret;
}
/// <summary>
/// Used to get the meta data from png file metadata
/// Can use this to capture PhonoBooth metadata by setting the filter to "iTXt"
/// </summary>
/// <param name="bitmapMetadata"></param>
/// <param name="itemMap"></param>
/// <param name="filter">set this to iTXt for Phenosuite image data</param>
/// <param name="query">initally null, used in recursive calls to get the child data</param>
public static void GetMetaDataItems(BitmapMetadata bitmapMetadata , ref Dictionary<string, string> itemMap, string filter= null, string query = null )
{
if (query == null)
query = string.Empty;
if (bitmapMetadata != null)
{
var key = string.Empty;
foreach (string relativeQuery in bitmapMetadata)
{
var fullQuery = query + relativeQuery;
// GetQuery returns an object: either a string or child metadata
// If a string then it is one of 4 values: ["Keyword", "Translated", "Compression", "Language Tag", "TextEntry"]
// We want the Keyword and the subsequent TextEntry items, the tags are a sequence in the order specified above
var metadata = bitmapMetadata.GetQuery(relativeQuery);
var innerBitmapMetadata = metadata as BitmapMetadata;
if (innerBitmapMetadata == null)
AddToMap(ref key, fullQuery, metadata?.ToString(), ref itemMap, filter); // Not a metadata structure so it is data - therefore check and Add to map
else
GetMetaDataItems(innerBitmapMetadata, ref itemMap, filter, fullQuery); // Recursive call
}
}
}
/// <summary>
/// Suitable for Png iTXt metadata
/// This is used to buld the item map from the metadata
/// </summary>
/// <param name="key">key like "Application" or "Lighting Mode"</param>
/// <param name="fullQuery">metadata query</param>
/// <param name="metadata">image metadata</param>
/// <param name="itemMap">map being populated from the metadata</param>
/// <param name="filter">we dont want all the meta data - so this filters on the "sub folder" of the meta data -Phenosuite uses "iTXt" </param>
private static void AddToMap(ref string key, string fullQuery, string metadata, ref Dictionary<string, string> itemMap, string filter)
{
if (metadata != null)
{
if (!fullQuery.Contains("Translated"))
{
if ((filter == null) || ((fullQuery.Contains(filter))))
{
if (fullQuery.Contains("Keyword"))
key = metadata;
if (fullQuery.Contains("TextEntry") && (key != null))
itemMap[key] = metadata?.ToString();
}
}
}
}
}
希望这有助于某人 - 因为我发现它很难理解!