如何使用 Winforms C# 或 C++ .Net 在不更改文件中的任何其他内容的情况下修改

我正在编写一个程序来帮助我整理多年来拍摄的数以千计的数码照片。我想要的一个功能是能够通过修改 Orientation EXIF 标签来旋转图像,而无需更改文件中的任何其他内容。我知道这是可能的,因为如果您在 Windows 资源管理器中右键单击文件并选择向左/向右旋转,那么就会发生这种情况 - 修改一个字节以匹配新的方向值。我特别不想修改图片本身。


然而,我尝试过的一切要么没有效果,要么显着改变了文件(例如,将文件减少了 14k 字节,大概是通过重新编码)。我在几个网站上阅读了很多帖子,似乎没有人对我的具体问题有答案——他们大多谈论添加额外标签,以及添加填充的需要,但如果我只是需要添加填充,我肯定不需要添加填充试图修改一个现有字节(尤其是我知道 Windows 资源管理器可以做到)。


我正在使用在 Windows 10 专业版下运行 Framework 4.5.2 的 C# Windows 窗体应用程序。还尝试从 C++ 中做到这一点。感谢所有贡献者,他们的例子是我建立的。


以下是 5 个基本的控制台应用程序示例:


使用 System.Drawing.Image 类的基本 C#。这将 Orientation 标签设置为 OK,但会减小尺寸,即重新编码图片。


 static void Main(string[] args)

 {

     const int EXIF_ORIENTATION = 0x0112;


     try

     {

         using (Image image = Image.FromFile("Test.jpg"))

         {

             System.Drawing.Imaging.PropertyItem orientation = image.GetPropertyItem(EXIF_ORIENTATION);


             byte o = 6; // Rotate 90 degrees clockwise


             orientation.Value[0] = o;


             image.SetPropertyItem(orientation);


             image.Save("Test2.jpg");

         }

     }

     catch (Exception ex)

     {

     }

InPlaceBitMapEditor 类看起来正是我所需要的,调试行表明这是修改 EXIF 标记,但文件未修改,即更改未写出。


 static void Main(string[] args)

 {

     try

     {

         Stream stream = new System.IO.FileStream("Test.JPG", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);


         JpegBitmapDecoder pngDecoder = new JpegBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);


         BitmapFrame frame = pngDecoder.Frames[0];


         InPlaceBitmapMetadataWriter inplace = frame.CreateInPlaceBitmapMetadataWriter();


         ushort u = 6; // Rotate 90 degrees clockwise


         object i1 = inplace.GetQuery("/app1/ifd/{ushort=274}"); // DEBUG - this is what it was before - 1


         if (inplace.TrySave() == true)

         {

             inplace.SetQuery("/app1/ifd/{ushort=274}", u);

         }


         object i2 = inplace.GetQuery("/app1/ifd/{ushort=274}"); // DEBUG - this is what it is after - 6


         stream.Close();

     }

     catch (Exception ex)

     {

     }


温温酱
浏览 194回答 2
2回答

眼眸繁星

除非jpeg的宽度和高度都是16的倍数,否则无法这样做。如果在GDI+中进行此操作,并且宽度和高度不是16的倍数,GDI+将尽最大努力保持压缩质量相同。在.net中也是一样请注意,您的 GDI+ 代码只会旋转缩略图。要旋转图像,请使用以下代码:void RotateImage(){    //new/delete operator is not necessary, unless     //Gdiplus startup/shutdown is in the same scope    Gdiplus::Image image(L"source.jpg");    if((image.GetWidth() % 16) != 0 || (image.GetHeight() % 16) != 0)        wprintf(L"Lossless compression is not possible\n");    Gdiplus::EncoderParameters encoder_params;    encoder_params.Count = 1;    encoder_params.Parameter[0].Guid = Gdiplus::EncoderTransformation;    encoder_params.Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong;    encoder_params.Parameter[0].NumberOfValues = 1;    //rotate    ULONG transformation = Gdiplus::EncoderValueTransformRotate90;    encoder_params.Parameter[0].Value = &transformation;    CLSID clsid;    GetEncoderClsid(L"image/jpeg", &clsid);    auto stat = image.Save(L"destination.jpg", &clsid, &encoder_params);    wprintf(L"Save %s\n", (stat == Gdiplus::Ok) ? L"succeeded" : L"failed");}int main(){    Gdiplus::GdiplusStartupInput gdiplusStartupInput;    ULONG_PTR gdiplusToken;    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);    RotateImage();    Gdiplus::GdiplusShutdown(gdiplusToken);    return 0;}

一只甜甜圈

以编程方式执行此操作的方法是读取 SOS 市场之后应出现的 APP1 标记。获取标记结构的 JPEG 文档。拥有 APP1 标记后,您需要根据需要更改方向。然后将 SOS 标记、修改后的 APP1 标记以及 APP1 标记之后的其余 JPEG 流写入新文件。这就是他们的全部。唯一的复杂性是导航 EXIF 文档以进行方向设置。
打开App,查看更多内容
随时随地看视频慕课网APP