Как записать данные EXIF ​​для изображения в C #? - PullRequest
2 голосов
/ 19 мая 2019

У меня большие проблемы с записью данных EXIF ​​в изображение. До сих пор мне удалось записать данные, которые требуют ввода в качестве строки. Однако я не знаю, как это сделать для элементов, требующих другого типа (например, время выдержки, выдержка).

Я следую этому руководству, но нет примера для чего-то, кроме строки. Этот веб-сайт объясняет, какой тип данных мне нужно использовать, а в документации Microsoft содержится соответствующее числовое значение. К сожалению, я не могу использовать это, чтобы решить мою проблему. Чтобы выяснить, какой идентификатор соответствует какому значению, я использую этот список и официальную документацию .

System.Drawing.Image imgEXIF = System.Drawing.Image.FromFile("D:/def.jpg");
System.Drawing.Image imgDummy = System.Drawing.Image.FromFile("D:/IMG_3214.jpg");
System.Drawing.Imaging.PropertyItem item = imgDummy.PropertyItems[0];
item.Id = 0x9286;
item.Type = 2; //String
item.Value = System.Text.Encoding.UTF8.GetBytes("Hello World\r\nthis is a test\0");
item.Len = item.Value.Length;
imgEXIF.SetPropertyItem(item);
imgEXIF.Save("D:/ghi.jpg");

Буду очень признателен за помощь в написании EXIF-данных, которые не являются строкой !!

1 Ответ

2 голосов
/ 19 мая 2019

То, что входит в Value на PropertyItem, - это байтовый массив, основанный на соответствующем Type для этого поля. Для текстовых полей, которые вы уже можете сделать, этот байтовый массив принимает форму байтового массива ASCII, который завершается нулем. Другие типы, которые я прокомментировал ниже под TagTypes. Для поля «Время экспонирования» это 8-байтовый массив, состоящий из двух 32-разрядных целых чисел без знака - числителя, за которым следует знаменатель. Мы можем использовать метод BitConverter.GetBytes() для преобразования uint (32-разрядное целое число без знака) в 4-байтовое представление, а затем просто объединить его с другим байтовым массивом, чтобы получить пару числителя и знаменателя.

Вот некоторые расширения, которые показывают, как использовать поле Short / type 3 и поле Rational / type 5 в дополнение к строковому полю Type 2:

public static class ImageMetaExtensions
{
    public static void SetMaxAperture(this Image image, uint numerator, uint denominator)
    {
        SetMetaDataItem(image, MAX_APERTURE, (short)TagTypes.RATIONAL, GetPairUnsigned32Integer(numerator, denominator));
    }

    public static void SetExposureTime(this Image image, uint numerator, uint denominator)
    {
        SetMetaDataItem(image, EXPOSURE_TIME, (short)TagTypes.RATIONAL, GetPairUnsigned32Integer(numerator, denominator));
    }

    public static void SetUserComment(this Image image, string text)
    {
        SetMetaDataItem(image, USER_COMMENT, (short)TagTypes.ASCII, GetNullTerminatedString(text));
    }

    public static void Set35mmFocalLength(this Image image, short focalLength)
    {
        SetMetaDataItem(image, FOCALLENGTH_35MM, (short)TagTypes.SHORT, BitConverter.GetBytes(focalLength));
    }

    public enum TagTypes : short
    {
        BYTE = 1, // 8 bit unsigned integer
        ASCII = 2,
        SHORT = 3, // 16-bit unsigned integer
        LONG = 4, // 32-bit unsigned integer
        RATIONAL = 5, // two unsigned longs - first numerator, second denominator
        UNDEFINED = 6, // any value depending on field definition
        SLONG = 7, // signed 32-bit
        SRATIONAL = 10 // signed pair of 32-bit numerator/denominator
    }

    private static void SetMetaDataItem(Image image, int id, short type, byte[] data)
    {
        PropertyItem anyItem = image.PropertyItems[0];
        anyItem.Id = id;
        anyItem.Len = data.Length;
        anyItem.Type = type;
        anyItem.Value = data;
        image.SetPropertyItem(anyItem);
    }

    private static byte[] GetPairUnsigned32Integer(uint numerator, uint denominator)
    {
        return BitConverter.GetBytes(numerator).Concat(BitConverter.GetBytes(denominator)).ToArray();
    }

    private static byte[] GetNullTerminatedString(string text)
    {
        return Encoding.ASCII.GetBytes(text + "\0");
    }

    private const int EXPOSURE_TIME = 0x829A;      
    private const int USER_COMMENT = 0x9286;
    private const int MAX_APERTURE = 0x9205;
    private const int FOCALLENGTH_35MM = 0xA405;
}

Использование:

System.Drawing.Image myImage = System.Drawing.Image.FromFile(@"c:\temp\someimage.jpg");
myImage.SetExposureTime(1, 30); // 1/30sec
myImage.SetUserComment("Hello, world");
myImage.Set35mmFocalLength(5);
myImage.Save(@"c:\temp\someotherimage.jpg"); // save somewhere else
...