JPEG画像を無劣化で回転するには

解決


operon  2017-10-26 21:03:22  No: 48824

JPEG画像ファイルを読み込み、90度単位で回転させ、別JPEG画像ファイルとして保存したいと思っています。
GDI+(GDIPAPI, GDIPOBJ)を使うことで比較的容易に実現できるらしいと聞いたのですが、具体的にどうすればいいのか、どなたかご教授願えませんでしょうか。

過去記事でサンプルコードの掲載サイトが紹介されていましたが、現在はなくなってしまっているようで、参照できませんでした。
https://www.petitmonte.com/bbs/answers?question_id=2793


通りすがり  2017-10-26 22:44:05  No: 48825

とりあえず delphi jpeg rotate lossless をキーワードに検索してみてはいかがでしょう。


operon  2017-10-27 00:47:35  No: 48826

通りすがり様ありがとうございます。

検索してみたところ、
Delphiについてはアプリの紹介サイトくらいしか見つかりませんでした。
 
他には、 
http://www.ijg.org/files/ 
で jpegTranなるC言語らしきソースコードがありましたが、
難解すぎて私にはとても手を出せそうにありませんでした。

msdnのサイト 
https://msdn.microsoft.com/en-us/library/windows/desktop/ms533845(v=vs.85).aspx
に、英語でよく分かりませんが多分目的に合致するだろうサンプルコードがありました。
GDI+を使ったシンプルなコードのようなので、おそらくこれをDelphiに翻訳するのが近道
なのだろうと思いますが、C言語系は触ったことがないので頭を抱えています。

helper functionて何でしょうか?
ところどころに「->」の記述が見られますが(image->GetWidth() 等)、Delphiではどんな記述になるのでしょうか?
ど素人丸出しで恥ずかしい限りですが、ご教示願えますでしょうか。


ウォレス  2017-10-27 02:19:59  No: 48827

StackOverFlowのコピペ。
Delphi10.2.1 で確認

https://stackoverflow.com/questions/10633400/rotate-bitmap-by-real-angle

uses
  GDIPAPI,  GDIPOBJ,  GDIPUTIL;

procedure TForm1.Button1Click(Sender: TObject);
var
  encoderClsid: TGUID;
  encoderParameters: TEncoderParameters;
  transformation: TEncoderValue;
  Image: TGPImage;
  path: string;
begin
  Image := TGPImage.Create('input.jpg'); //適当に
  GetEncoderClsid('image/jpeg', encoderClsid);
  encoderParameters.Count := 1;
  encoderParameters.Parameter[0].Guid := EncoderTransformation;
  encoderParameters.Parameter[0].Type_:= EncoderParameterValueTypeLong;
  encoderParameters.Parameter[0].NumberOfValues := 1;
  transformation := EncoderValueTransformRotate270;
  encoderParameters.Parameter[0].Value := @transformation;

  path:= extractfilepatH(Application.ExeName);

  Image.Save(path + 'imageOUT.jpg', encoderClsid, @encoderParameters);
  Image.Free;
end;


operon  2017-10-27 02:26:03  No: 48828

msdnのサンプルコードをよくわからないなりに翻訳してみました。

uses GDIPAPI, GDIPOBJ, GDIPUTIL

procedure TForm1.buttonJpegRotate;
var
  GdiplusStartupInput : TGdiplusStartupInput;
  GdiplusStartupOutput: TGdiplusStartupOutput;
  gdiplusToken       : cardinal;
  encoderClsid       : TGUID;
  encoderParameters  : TEncoderParameters;
  transformation     : TEncoderValue;
  stat               : TStatus;
begin
  // Initialize GDI+.
  GdiplusStartup(gdiplusToken, @GdiplusStartupInput, @GdiplusStartupOutput);

  //① Get a JPEG image from the disk.
  //Image* image = new Image(L"test1.jpg");

  // Get the CLSID of the JPEG encoder.
  GetEncoderClsid('image/jpeg', &encoderClsid);

  // Rotate the image.
  encoderParameters.Count := 1;
  encoderParameters.Parameter[0].Guid := EncoderTransformation;
  encoderParameters.Parameter[0].Type_ := EncoderParameterValueTypeLong;
  encoderParameters.Parameter[0].NumberOfValues := 1;
  transformation := EncoderValueTransformRotate90;
  encoderParameters.Parameter[0].Value := @transformation;

  //② Save the image.
  //stat = image->Save(L"ShapesR90.jpg", &encoderClsid, &encoderParameters);

  GdiplusShutdown(gdiplusToken);
end;

①②で imageクラスを使っているようなのですが、
Delphiで同じことをするにはどのようにしたらよいのでしょうか。

ところで、当該サイトの英文を翻訳してみたところ、
「画像の幅と高さが両方とも16の倍数の場合、画像の回転と保存のプロセスによって情報が失われることはありません。」
とありました。
これは単に当該コードの制限事項なのか、あるいはJpegの特性でひょっとして
画像の幅と高さが共に16の倍数の場合は、BitMapからJpegへ変換しても劣化しないということなのか
ご存じの方はいらっしゃいますでしょうか。


operon  2017-10-27 02:28:38  No: 48829

↑を書いてる間に ウォレスさんが答えてくださっていました。
ありがとうございます!


ウォレス  2017-10-27 02:38:26  No: 48830

JpegはDCTを用いて8X8ブロック毎に圧縮していますので画像の縦および横が8の倍数とき、ブロック内のエンコード値を軸回転することで、無劣化で回転できるはずです。
16の倍数なのはなぜでしょうか。。。恐らくGDIの実装上の制約ではないかと。

「BitMapからJpegへ変換しても劣化しない」というのはかなり難しいのではないかと。。。
論理的には必ず劣化しますが、DCT後の間引きをやめる、YUVではなくRGBで圧縮する、などのオプションを設定できれば、劣化は極めてすくなくなります。無劣化になるかどうかは数学者の出番ですね。。。


ウォレス  2017-10-27 02:52:33  No: 48831

あれれリンク先が間違ってるよ
https://stackoverflow.com/questions/41619757/why-is-timage-rotating-my-image


ウォレス  2017-10-27 03:01:38  No: 48832

おかしいな。。。ココです。
http://delphikingdom.ru/asp/answer.asp?IDAnswer=49892


operon  2017-10-27 03:26:14  No: 48833

ウォレス様ありがとうございます。
やっぱりそうですよね。。。

試しにループ実行してみたところ、
16の倍数(688×880 ピクセル)の画像では 1000回実行しても劣化せず、
非倍数(673×870 ピクセル)の画像では じわじわ劣化していきました。

これ以上はおそらく無理だろうと思われますので、
未解決ではありますが、クローズとさせていただきます。
ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加