获取EXIF方向标签,将其旋转到正确的方向,处理图像并保存具有适当方向的图像。

6
我的程序批量处理一些图像。我目前需要读取图像的exif方向标签,将其旋转到正确的方向,进行一些处理并保存图像,不带任何EXIF方向标签,但具有正确的旋转(或带有正确方向的EXIF标签)。
我正在使用这个库来读取EXIF并进行旋转:这个库
  var bmp = new Bitmap(pathToImageFile);
    var exif = new EXIFextractor(ref bmp, "n"); // get source from http://www.codeproject.com/KB/graphics/exifextractor.aspx?fid=207371

    if (exif["Orientation"] != null)
    {
    RotateFlipType flip = OrientationToFlipType(exif["Orientation"].ToString());

    if (flip != RotateFlipType.RotateNoneFlipNone) // don't flip of orientation is correct
    {
    bmp.RotateFlip(flip);
    bmp.Save(pathToImageFile, ImageFormat.Jpeg);
    }

    // Match the orientation code to the correct rotation:

    private static RotateFlipType OrientationToFlipType(string orientation)
    {
    switch (int.Parse(orientation))
    {
    case 1:
    return RotateFlipType.RotateNoneFlipNone;

    case 2:
    return RotateFlipType.RotateNoneFlipX;

    case 3:
    return RotateFlipType.Rotate180FlipNone;
    case 4:
    return RotateFlipType.Rotate180FlipX;
    break;
    case 5:
    return RotateFlipType.Rotate90FlipX;
    break;
    case 6:
    return RotateFlipType.Rotate90FlipNone;
    case 7:
    return RotateFlipType.Rotate270FlipX;
    case 8:
    return RotateFlipType.Rotate270FlipNone;
    default:
    return 
}
}

这个方法可以解决问题,但是当保存图片时,EXIF旋转标签仍然存在,导致图片方向错误。 我可以做的是:

       var bmp = new Bitmap(OS.FileName);       
       var exif = new EXIFextractor(ref bmp, "n");
       exif.setTag(0x112, "1");
       bmp.save("strippedexifimage");

但是,当我在循环中使用这段代码时,我的程序速度会减慢约50%。有没有其他的方法?也许可以在应用旋转后编写反向旋转图像的代码,那样行吗?
更新: @Hans Passant您的答案有效,但它产生的结果与库产生的结果相同。当我使用bitmap.save()时,库和您的代码都有效。但是,当我使用以下代码保存我的图像,具体取决于用户选择的格式。Imgformat可以是
private void saveJpeg(string path, Bitmap img, long quality)
        {


            EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);


            ImageCodecInfo Codec = this.getEncoderInfo(imgformat);
             if (Codec == null)
              return;

            EncoderParameters encoderParams = new EncoderParameters(1);
            encoderParams.Param[0] = qualityParam;    
            img.Save(path + ext, Codec, encoderParams);
        }



 private ImageCodecInfo getEncoderInfo(string mimeType)
        {
            // Get image codecs for all image formats
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();

            // Find the correct image codec
            for (int i = 0; i < codecs.Length; i++)
                if (codecs[i].MimeType == mimeType)
                    return codecs[i];
            return null;
        }

看一下ImageMagick。我认为它可以满足你的所有需求。你可以为它编写代码,或者直接使用它。 :) - atlaste
1个回答

7
  var exif = new EXIFextractor(ref bmp, "n")

使用库来实现一个功能可以节省很多时间。但是当库设计不佳或使用困难时,你会被卡住好几天。 ref bmp 是第一个大警报,你试图将该值指定为字符串,结果陷入绝望的深渊。这很吸引人,因为你不必考虑在正确的setTag()重载中"type"可能意味着什么。它是类型3,并且需要一个具有两个元素的byte[]。这根本不可发现,只有当你不需要它时才能正确地使用该库。
放弃这个库,它没有帮助。存储方向的EXIF标签的ID为0x112,编码为16位值。只需直接使用System.Drawing读取该值并将其强制返回为1即可。像这样:
static void FixImageOrientation(Image srce) {
    const int ExifOrientationId = 0x112;
    // Read orientation tag
    if (!srce.PropertyIdList.Contains(ExifOrientationId)) return;
    var prop = srce.GetPropertyItem(ExifOrientationId);
    var orient = BitConverter.ToInt16(prop.Value, 0);
    // Force value to 1
    prop.Value = BitConverter.GetBytes((short)1);
    srce.SetPropertyItem(prop);

    // Rotate/flip image according to <orient>
    switch (orient) {
        // etc...
    }
}

非常感谢。我尝试搜索是否有直接通过代码实现的方法,但没有找到任何相关信息。orient函数是否返回在我的switch case中的值(即1、2、3等)? - techno
1
呵呵,我也记得曾经抓住了那个库...然后把我从规范中需要的部分删除并重新编写。像往常一样,汉斯给出了好建议! - atlaste
@atlaste 我有保存图像为不同格式的代码。由于整个工作流程都依赖于它,我想保留这段代码。当我使用savejpeg方法时出现错误。你能告诉我这里出了什么问题吗? - techno
@techno 我猜缩略图不会查看方向标签,或者这是缓存的影响(缩略图通常被缓存)。在软件中,你唯一可以确定的是通过检查数据与规格来避免错误。第三方软件的行为...好吧,那是你无法控制的。因此,对于你的问题,除了“我会遵循规格”之外,没有更好的答案。 - atlaste
@atlaste 这不是缩略图缓存的问题。有一个叫做 http://www.exiv2.org/ 的实用程序,我通过命令行使用它,并从图像中删除所有exif数据,如 exiv2 rm myimage.jpg。一旦我这样做了,由我的程序生成的错误旋转图像就恢复到原始方向了。我试着把它加入到我的代码中。使用旧版本的程序渲染图像的实际时间大约为34秒,使用exiv2和提取exif库需要1分钟以上。 - techno
@atlaste 我不理解的是,当使用img.save时,Hans Passant的代码和库运行得非常完美。但是当我使用savejpeg时,一些图像会出现问题。 - techno

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接