获取EXIF方向标记,旋转到正确方向,处理图像并以正确的方向保存图像

时间:2016-01-12 10:11:43

标签: c# .net gdi+ exif system.drawing

我的程序批量处理一些图像。我目前需要读取图像的exif方向标记,将其旋转到正确的方向,进行一些处理并保存图像而不使用任何EXIF方向标记,但是需要适当的旋转。(或者带有正确方向的EXIF标签)

我正在阅读EXIF并使用this library旋转:

  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可以是imgformat = "image/png";,imgformat = "image/jpeg";等,一些图像仍然保存有错误的exif方向标记。即:我在Windows资源管理器中看到错误的图像预览,当我用MS Paint打开图像时,图像具有正确的方向。我做错了什么?请帮忙。

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;
        }

1 个答案:

答案 0 :(得分:5)

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

使用库实现功能可以节省大量时间。或者,当图书馆设计不当或难以使用时,您会陷入困境。 ref bmp是第一个响亮的警报,你试图将值指定为字符串,从而使它成为绝望的陷阱。这很有吸引力,因为您不必考虑正确的setTag()重载中“类型”可能意味着什么。它是类型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...
    }
}