【问题标题】:How can I find out when a picture was actually taken in C# running on Vista?我怎样才能知道一张照片是在 Vista 上运行的 C# 中实际拍摄的?
【发布时间】:2020-01-27 19:25:16
【问题描述】:

在 Windows XP 中,“FileInfo.LastWriteTime”将返回照片的拍摄日期 - 无论文件在文件系统中移动了多少次。

在 Vista 中,它返回的是从相机复制照片的日期。

如何知道照片是在 Vista 中拍摄的?在 Windows 资源管理器中,此字段称为“拍摄日期”。

【问题讨论】:

    标签: c#


    【解决方案1】:

    这里尽可能快速和干净。通过使用 FileStream,您可以告诉 GDI+ 不要加载整个图像进行验证。它在我的机器上运行速度超过 10 倍。

    //we init this once so that if the function is repeatedly called
    //it isn't stressing the garbage man
    private static Regex r = new Regex(":");
    
    //retrieves the datetime WITHOUT loading the whole image
    public static DateTime GetDateTakenFromImage(string path)
    {
        using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
        using (Image myImage = Image.FromStream(fs, false, false))
        {
            PropertyItem propItem = myImage.GetPropertyItem(36867);
            string dateTaken = r.Replace(Encoding.UTF8.GetString(propItem.Value), "-", 2);
            return DateTime.Parse(dateTaken);
        }
    }
    

    是的,正确的 id 是 36867,而不是 306。

    下面的其他开源项目应注意这一点。在处理数千个文件时,这对性能造成了巨大的损失。

    【讨论】:

    • 经过一些测试,我发现你的答案是最好的。谢谢。
    • 现在,前提是您先检查它是否存在。这个属性总是有可能没有。
    • 很好的解决方案!属性检查很重要。如果您添加 if (myImage.PropertyIdList.Any(x => x == 36867)) 作为您的检查,效果很好!
    • 稍微揭开这个答案的神秘面纱。 PropertyItem 36867(或十六进制的 0x9003)是“PropertyTagExifDTOrig”,如此列表中的 MSDN msdn.microsoft.com/en-us/library/… 中所述
    • 它显示错误“找不到属性”。我正在尝试处理 .PNG 文件
    【解决方案2】:
    Image myImage = Image.FromFile(@"C:\temp\IMG_0325.JPG");
    PropertyItem propItem = myImage.GetPropertyItem(306);
    DateTime dtaken;
    
    //Convert date taken metadata to a DateTime object
    string sdate = Encoding.UTF8.GetString(propItem.Value).Trim();
    string secondhalf = sdate.Substring(sdate.IndexOf(" "), (sdate.Length - sdate.IndexOf(" ")));
    string firsthalf = sdate.Substring(0, 10);
    firsthalf = firsthalf.Replace(":", "-");
    sdate = firsthalf + secondhalf;
    dtaken = DateTime.Parse(sdate);
    

    【讨论】:

    • 实际上,306 是最后修改的日期标识符...我试过了,它非常接近...但是,查看所有属性 ID,并转储到文本文件,我发现了该 ID 36867 是拍摄日期(虽然 36868 也有相同的拍摄日期值,所以我不肯定哪个是哪个)
    • 上面的答案应该改成答案,只要EXIFG存在的速度。此方法有效,但速度较慢 - 尤其是批量处理!
    【解决方案3】:

    自 2002 年以来,我维护了一个 simple open-source library,用于从图像/视频文件中提取元数据。

    // Read all metadata from the image
    var directories = ImageMetadataReader.ReadMetadata(stream);
    
    // Find the so-called Exif "SubIFD" (which may be null)
    var subIfdDirectory = directories.OfType<ExifSubIfdDirectory>().FirstOrDefault();
    
    // Read the DateTime tag value
    var dateTime = subIfdDirectory?.GetDateTime(ExifDirectoryBase.TagDateTimeOriginal);
    

    在我的基准测试中,此代码的运行速度比 Image.GetPropertyItem 快 12 倍以上,比 WPF JpegBitmapDecoder/BitmapMetadata API 快约 17 倍。

    库中提供了大量额外信息,例如相机设置(F-stop、ISO、快门速度、闪光模式、焦距……)、图像属性(尺寸、像素配置)和其他信息如 GPS 位置、关键字、版权信息等。

    如果您只对元数据感兴趣,那么使用这个库非常快,因为它不会解码图像(即位图)。如果您有足够快的存储空间,您可以在几秒钟内扫描数千张图像。

    ImageMetadataReader 可以识别多种文件类型(JPEG、PNG、GIF、BMP、TIFF、PCX、WebP、ICO 等)。如果您知道您正在处理 JPEG,并且您需要 Exif 数据,那么您可以通过以下方式加快处理速度:

    var directories = JpegMetadataReader.ReadMetadata(stream, new[] { new ExifReader() });
    

    元数据提取器库可通过NuGetcode's on GitHub 获得。感谢多年来改进库和提交测试图像的所有出色的贡献者。

    【讨论】:

    • 德鲁,我回来了!我现在有将 RAW 转换为 Tiff 的代码,但我的 Image.GetPropertyItem 没有使用 36867 拍摄的日期。您的代码会执行 Tiff 和 Raws 吗?
    • 还有。 . . .击鼓。 . . .它按照您的 Nugetter 完美安装,并使用上面的代码在您的回答中返回了 .CR2 文件(佳能)的照片拍摄日期!当然,必须单击几个潜在的修复程序才能将使用放在顶部,但 VS 2017 会照顾你。
    • 只是为了确认:这个库不能用于写入元数据,对吗?
    • @rory.ap 还没有。您可以订阅github.com/drewnoakes/metadata-extractor-dotnet/issues/23 以跟踪此功能请求。
    • 谢谢,这在 macOS 环境中运行良好,与 System.Drawing 不同。稍后将在项目中在 Ubuntu 环境中进行测试。
    【解决方案4】:

    使用 WPF 和 C#,您可以使用 BitmapMetadata 类获取 Date Taken 属性:

    MSDN - BitmapMetada

    WPF and BitmapMetadata

    【讨论】:

    • 我可以将 BitmapMetadata 与 Window.Forms 一起使用还是仅 WPF? (从 Visual Studio 2008 收到奇怪的错误消息)
    • 或者使用my library,它的 API 更简单,运行速度比这些 WPF API 快 17 倍左右(如果您只需要 Exif,则速度快 30 倍)。
    【解决方案5】:

    在 Windows XP 中“FileInfo.LastWriteTime” 将返回图片的日期 采取 - 无论多少次 文件在 文件系统。

    我非常怀疑 XP 是否真的这样做了。您用于将图像从相机复制到硬盘的工具更有可能将文件修改日期重置为图像的拍摄日期。

    【讨论】:

    • 工具是windows explorer。
    • 啊..这可以解释。相机是一台带有文件系统的小型计算机。拍照时将其“写入磁盘”。资源管理器在复制时维护它们。
    【解决方案6】:

    您必须检查图片中的 EXIF 信息。我认为使用常规的 .Net 函数您不会知道照片的拍摄时间。

    可能有点复杂...

    【讨论】:

      【解决方案7】:

      图像中会嵌入 EXIF 数据。如果您搜索 EXIF 和 C#,网络上有大量示例。

      【讨论】:

        【解决方案8】:
            //retrieves the datetime WITHOUT loading the whole image
            public static DateTime GetDateTakenFromImage(string path)
            {
                using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
                using (Image myImage = Image.FromStream(fs, false, false))
                {
                    PropertyItem propItem = null;
                    try
                    {
                        propItem = myImage.GetPropertyItem(36867);
                    }
                    catch { }
                    if (propItem != null)
                    {
                        string dateTaken = r.Replace(Encoding.UTF8.GetString(propItem.Value), "-", 2);
                        return DateTime.Parse(dateTaken);
                    }
                    else
                        return new FileInfo(path).LastWriteTime;
                }
            }
        

        【讨论】:

        • 这实际上是@kDar 答案的副本,带有一点错误处理...
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-06
        • 1970-01-01
        相关资源
        最近更新 更多