【问题标题】:TWICImage, how to set jpeg compression quality?TWICImage,如何设置 jpeg 压缩质量?
【发布时间】:2017-02-14 22:19:58
【问题描述】:

我正在使用 Delphi XE 和 TWICImage 类进行图像处理。 我想知道是否有任何方法可以使用 TWICImage 设置 jpeg 压缩质量?

procedure TfrmMain.Button2Click(Sender: TObject);
var
  wic: TWICImage;
begin
  wic := TWICImage.Create;
  try
    wic.LoadFromFile('sample-BMP.bmp');
    wic.ImageFormat := wifJpeg;
    // ... before saving I want to set low compression quality
    wic.SaveToFile('sample-JPG.jpg');
  finally
    wic.Free;
  end;
end;

【问题讨论】:

  • 我认为您需要创建一个TJPegImage 并为其分配TWICImage,使用CompressionQuality 属性设置压缩质量,最后保存您的图像TJPegImage.SaveToFile('sample-JPG.jpg');
  • 我非常怀疑这一点。我希望这个想法是完全避免 TJpegImage。

标签: delphi


【解决方案1】:

WIC 的 VCL 包装器有些受限。它没有为您提供任何指定图像质量的方法。我将对该代码中完全没有错误检查视而不见。呃!

我认为您将需要使用原始 COM API 编写自己的代码。它可能看起来像这样:

uses
  System.SysUtils,
  System.Variants,
  System.Win.ComObj,
  Winapi.Windows,
  Winapi.Wincodec,
  Winapi.ActiveX,
  Vcl.Graphics;

procedure SaveBitmapAsJpeg(Bitmap: TBitmap; ImageQuality: Single; FileName: string);
const
  PROPBAG2_TYPE_DATA = 1;
var
  ImagingFactory: IWICImagingFactory;
  Width, Height: Integer;
  Stream: IWICStream;
  Encoder: IWICBitmapEncoder;
  Frame: IWICBitmapFrameEncode;
  PropBag: IPropertyBag2;
  PropBagOptions: TPropBag2;
  V: Variant;
  PixelFormat: TGUID;
  Buffer: TBytes;
  BitmapInfo: TBitmapInfo;
  hBmp: HBITMAP;
  WICBitmap: IWICBitmap;
  Rect: WICRect;
begin
  Width := Bitmap.Width;
  Height := Bitmap.Height;

  OleCheck(
    CoCreateInstance(CLSID_WICImagingFactory, nil, CLSCTX_INPROC_SERVER 
      or CLSCTX_LOCAL_SERVER, IUnknown, ImagingFactory)
  );

  OleCheck(ImagingFactory.CreateStream(Stream));
  OleCheck(Stream.InitializeFromFilename(PChar(FileName), GENERIC_WRITE));
  OleCheck(ImagingFactory.CreateEncoder(GUID_ContainerFormatJpeg, GUID_NULL, Encoder));
  OleCheck(Encoder.Initialize(Stream, WICBitmapEncoderNoCache));
  OleCheck(Encoder.CreateNewFrame(Frame, PropBag));

  PropBagOptions := Default(TPropBag2);
  PropBagOptions.pstrName := 'ImageQuality';
  PropBagOptions.dwType := PROPBAG2_TYPE_DATA;
  PropBagOptions.vt := VT_R4;
  V := VarAsType(ImageQuality, varSingle);
  OleCheck(PropBag.Write(1, @PropBagOptions, @V));
  OleCheck(Frame.Initialize(PropBag));
  OleCheck(Frame.SetSize(Width, Height));
  if Bitmap.AlphaFormat=afDefined then begin
    PixelFormat := GUID_WICPixelFormat32bppBGRA
  end else begin
    PixelFormat := GUID_WICPixelFormat32bppBGR;
  end;
  Bitmap.PixelFormat := pf32bit;
  SetLength(Buffer, 4*Width*Height);
  BitmapInfo := Default(TBitmapInfo);
  BitmapInfo.bmiHeader.biSize := SizeOf(BitmapInfo);
  BitmapInfo.bmiHeader.biWidth := Width;
  BitmapInfo.bmiHeader.biHeight := -Height;
  BitmapInfo.bmiHeader.biPlanes := 1;
  BitmapInfo.bmiHeader.biBitCount := 32;
  hBmp := Bitmap.Handle;
  GetDIBits(Bitmap.Canvas.Handle, hBmp, 0, Height, @Buffer[0], BitmapInfo, 
    DIB_RGB_COLORS);
  OleCheck(ImagingFactory.CreateBitmapFromMemory(Width, Height, PixelFormat, 
    4*Width, Length(Buffer), @Buffer[0], WICBitmap));
  Rect.X := 0;
  Rect.Y := 0;
  Rect.Width := Width;
  Rect.Height := Height;
  OleCheck(Frame.WriteSource(WICBitmap, @Rect));
  OleCheck(Frame.Commit);
  OleCheck(Encoder.Commit);
end;

传递一个介于 0 和 1 之间的图像质量值,其中 0 是最低质量(最高压缩),1 是最高质量(最低压缩)。

我已广泛使用此处找到的问题和答案:How to create a lossless jpg using WIC in Delphi

我还大量借用了 VCL 源代码来创建 IWICBitmap。如果您希望继续使用TWICBitmap,您可以这样做并使用其Handle 属性来获取IWICBitmap。这将产生如下代码:

uses
  System.Variants,
  System.Win.ComObj,
  Winapi.Windows,
  Winapi.Wincodec,
  Winapi.ActiveX,
  Vcl.Graphics;

procedure SaveWICImageAsJpeg(WICImage: TWICImage; ImageQuality: Single; 
  FileName: string);
const
  PROPBAG2_TYPE_DATA = 1;
var
  ImagingFactory: IWICImagingFactory;
  Width, Height: Integer;
  Stream: IWICStream;
  Encoder: IWICBitmapEncoder;
  Frame: IWICBitmapFrameEncode;
  PropBag: IPropertyBag2;
  PropBagOptions: TPropBag2;
  V: Variant;
  PixelFormat: TGUID;
  Rect: WICRect;
begin
  Width := WICImage.Width;
  Height := WICImage.Height;
  ImagingFactory := WICImage.ImagingFactory;
  OleCheck(ImagingFactory.CreateStream(Stream));
  OleCheck(Stream.InitializeFromFilename(PChar(FileName), GENERIC_WRITE));
  OleCheck(ImagingFactory.CreateEncoder(GUID_ContainerFormatJpeg, GUID_NULL, Encoder));
  OleCheck(Encoder.Initialize(Stream, WICBitmapEncoderNoCache));
  OleCheck(Encoder.CreateNewFrame(Frame, PropBag));
  PropBagOptions := Default(TPropBag2);
  PropBagOptions.pstrName := 'ImageQuality';
  PropBagOptions.dwType := PROPBAG2_TYPE_DATA;
  PropBagOptions.vt := VT_R4;
  V := VarAsType(ImageQuality, varSingle);
  OleCheck(PropBag.Write(1, @PropBagOptions, @V));
  OleCheck(Frame.Initialize(PropBag));
  OleCheck(Frame.SetSize(Width, Height));
  Rect.X := 0;
  Rect.Y := 0;
  Rect.Width := Width;
  Rect.Height := Height;
  OleCheck(Frame.WriteSource(WICImage.Handle, @Rect));
  OleCheck(Frame.Commit);
  OleCheck(Encoder.Commit);
end;

【讨论】:

  • 是的,这几乎涵盖了它。我刚刚尝试了一种使用 TWICImage 和 IWICBitmapEncoder 的方法,但是冰从那里开始变薄,并且 Delphi 没有提供安全线。最好像 David 所展示的那样直接加入 COM。
  • 天哪!这不是我想的那样一行代码 :) 大卫,你是最伟大的!