jpeg画像を90度回転させる

解決


温野菜  2006-04-16 16:47:12  No: 61228  IP: 192.*.*.*

以前同じタイトルで質問して、下記のコードを教わりました。

質疑のURLは、
http://madia.world.coocan.jp/cgi-bin/Vcbbs/wwwlng.cgi?print+200603/06030044.txt
です。

    CImage inPhoto;
    inPhoto.Load(mych1);
    int sWidth = inPhoto.GetWidth();
    int sHeight = inPhoto.GetHeight();
    int bpp = inPhoto.GetBPP();
    int inphotoPitch = inPhoto.GetPitch();
    unsigned char *inphotoBuffer = (unsigned char *)inPhoto.GetBits();
        if(bpp < 8) {return; }
    CImage outPhoto;
    outPhoto.Create(sHeight, sWidth, bpp);
    int outphotoPitch = outPhoto.GetPitch();
    unsigned char *outphotoBuffer = (unsigned char *)outPhoto.GetBits();
    int bytepp = bpp / 8;
    for(int x = 0 ; x < sWidth ; ++x){
        for(int y = 0 ; y < sHeight ; ++y){
                        destX = y; 
                        destY = sWidth - x - 1;
            unsigned char *read = inphotoBuffer + x * bytepp + y * inphotoPitch;
                        unsigned char *write = outphotoBuffer + destX * bytepp + destY * outphotoPitch;
                        for(int c = 0 ; c < bytepp ; ++c) {*write++ = *read++; }
        }
    }
    outPhoto.Save(mych1);

mych1は、写真ファイルのバスです。

これですいすい回転(正確にはX軸とY軸の置換え)させられるのですが、ファイルのバイト数が、1/3くらいになってしまいます。

大きさは変わらず、画質も拡大してみても劣化はしていないようなのですが、バイト数だけ減ります。
圧縮率の問題かとも思うのですが、原因が分からず、改めてお尋ねしました。

WINXP  .NET  MFC  です。

よろしくお願いいたします。

編集 削除
dairygoods  2006-04-17 08:54:54  No: 61229  IP: 192.*.*.*

jpeg画像の圧縮率を表示可能な画像ビューアがあると思います。
まずは、それで圧縮率が変化しているか確認してみては。

編集 削除
a  2006-04-17 09:37:01  No: 61230  IP: 192.*.*.*

タグの情報が減ってるんじゃない?
DSCで撮った写真とかだとExif情報とか切られそうだけど。
サムネイルとかも切られてるんじゃないかなぁ。

編集 削除
温野菜  2006-04-17 14:30:26  No: 61231  IP: 192.*.*.*

画像ビューアは、ペイントとフォトショップがありますが、いずれも圧縮率は表示されないようです。
MSDNでsaveを見ても、どのような条件でファイルに書き込まれるのかは、書いてありません。
圧縮率を指定するようなパラメータもないようです。

サンプルにした写真はサイバーショットで撮ったもので、約1.4メガですが、上記のコードで90度倒してsaveすると、0.4メガくらいになります。
2度目以降の「回転」では、サイズの縮小は起こりません。
マイコンピュータで見ると、撮影日のデータが消えています。

撮影された写真のファイル名は、ご指摘の通りDSCで始まっています。
サムネイルは、マイコンピュータの「縮小版」でフォルダを開いた時、写真が表示されるので、残っているのではないでしょうか。
それ以前に、タグの情報だけで、1メガ弱もあるのでしょうか。

アドバイスをお願いいたします。

編集 削除
PATIO  2006-04-17 15:01:44  No: 61232  IP: 192.*.*.*

> サムネイルは、マイコンピュータの「縮小版」でフォルダを開いた時、
> 写真が表示されるので、残っているのではないでしょうか。

この時に表示されるサムネイルは、各フォルダ内にあるThumbs.dbから
読み込まれていると思うのでそれを判断基準にしても駄目だと思いますよ。
やはり、ファイル内のメタ情報を確認できるようなビューアを用意しないと
駄目でしょう。
しかし、サムネイルよりも元データが大きいとは思えないので
圧縮率が変わったりしているのではないかと思えますけれど。

編集 削除
subaru  2006-04-17 15:36:52  No: 61233  IP: 192.*.*.*

CImageでは品質を指定して保存することはできないと思います。
GDI+のデフォルトで保存されるようです。
JPEGのフォーマットとかよく知らないのですが
もし仮に品質指定できたとして元画像がどの程度の品質かなんて
わからないんじゃないでしょうか・・・

編集 削除
温野菜  2006-04-17 16:12:11  No: 61234  IP: 192.*.*.*

dairygoodsさん、aさん、PATIOさん、subaruさん、ありがとうございます。

アドバイスを頂きながら、自分でもあれこれネット検索をしています。
圧縮率の取得できるビューワは、いろいろ探したのですが、見つかっていません。
圧縮率を指定して保存するフリーソフトはあるのですが。

そもそも「回転」させるとファイルのサイズが極端に変わることが、たいへん気になったのですが、やはり圧縮率の問題かと思いました。

本格的にjpeg画像の回転を勉強するのは、それなりに時間がかかると思うのですが、CImage/saveでjpeg画像を保存する場合のフォーマット等について、関連サイトをご存知の方がいらしたら、お教え頂けませんでしょうか。

編集 削除
subaru  2006-04-17 22:40:36  No: 61235  IP: 192.*.*.*

CImage::Saveは内部でGDI+を利用して保存しているようですが
品質指定で保存したい場合には自分で用意する必要がありそうです。

CImage→Gdiplus::Bitmapへの変換は簡単にできるので
保存時のパラメータのみ注意すればいいでしょう。
とりあえずググってみるとEncoderQualityを指定しているものがありました。
http://www.geocities.jp/i96815/mm/mm02.html
(画像を作り直しているので当然Exif情報は失われます)

画像の回転ならCImage::PlgBltとか、Gdiplus::Graphics::DrawImageとか。。

編集 削除
温野菜  2006-04-18 07:47:54  No: 61236  IP: 192.*.*.*

subaruさん、ありがとうございまなす。

とりあえず、ご紹介のサイトのコードがC++なので、MFCに置き換えてみようと思いました。

フォームビューを使って、GetEncoderClsid()を手書きし、OnBnClickedButton1()をハンドルして、mainの中身をコピペしました。

ここでビルドすると、
Bitmap* pBitmap = new Bitmap ( 207, 156 );  の行で、

'Gdiplus::GdiplusBase::operator new' : 関数に 3 個の引数を指定できません。

というエラーになります。
引数は2個しかないのに、なぜ「3個」なのか、とっても不思議です。
あれこれやってみて、別なところの問題だと思ったのですが、どうしたらいいのか分かりません。


> CImage→Gdiplus::Bitmapへの変換は簡単にできるので

これも分かりません。
MFCのコードができたら、自分でいろいろ試してみようと思ったのですが。。。

アドバイスをお願いいたします。

編集 削除
kure  2006-04-18 10:23:25  No: 61237  IP: 192.*.*.*

> 'Gdiplus::GdiplusBase::operator new' : 関数に 3 個の引数を指定できません。

についてはこちらをどうぞ。

http://support.microsoft.com/default.aspx?scid=kb%3Bja%3B317799

> CImage→Gdiplus::Bitmapへの変換

についてはGdiplus::Bitmap::FromHBITMAPとか。

編集 削除
YuO  2006-04-18 11:58:02  No: 61238  IP: 192.*.*.*

「JPEG 回転」でググって,
http://dobon.net/vb/bbs/log3-6/3371.html
http://www.eggheadcafe.com/articles/20030706.asp
と経由すると,MSDNに"Transforming a JPEG Image Without Loss of Information"という記事がありましたが……。
http://msdn.microsoft.com/library/en-us/gdicpp/GDIPlus/usingGDIPlus/usingimageencodersanddecoders/transformingajpegimagewithoutlossofinformation.asp

編集 削除
温野菜  2006-04-18 16:55:58  No: 61239  IP: 192.*.*.*

subaruさんからご紹介頂きましたコードは、kureさんのアドバイスで動くようになりました。

次は以前junさんから教わりました、画像のX軸とY軸を置き換えるコードとの組み合わせなのですが、型の変換ができません。

CImage outPhoto ⇒ Image* pImgOrg です。

subaruさんは、「簡単にできる」とおっしゃっていますが、全然分かりません。
なんだか、「クグッ」ていると、あちこち「GDI++は簡単だ」と書いてあるのに、私はぼろぼろです。

アドバイス頂けませんでしょうか。

編集 削除
subaru  2006-04-18 17:30:25  No: 61240  IP: 192.*.*.*

>型の変換ができません。
kureさんがすでにフォローしてくれてますが、
こんなかんじで。
Bitmap* pImgOrg = Bitmap::FromHBITMAP(outPhoto, NULL);

使い終わったら必ずdeleteすること。
Bitmapのコンストラクタでもありますが。
#Gdiplus::Bitmap→CImageに比べたらはるかに簡単ということで。。

タイトルの内容からするとYuOさんの紹介してくれたページが
一番適した方法のようですよ。
#EncoderTransformationって何に使えるんだろうって思ってたけど
#元画像がJPEGって前提で使うものだったのですね。

編集 削除
kure  2006-04-18 17:34:07  No: 61241  IP: 192.*.*.*

> CImage outPhoto ⇒ Image* pImgOrg です。

ImageってGdiplus::Imageのことだと仮定して話を進めます。

CImage -> Gdiplus::Bitmapの変換はできましたか?
これができればGdiplus::BitmapはGdiplus::Imageからの派生クラスなので、

Gdiplus::Bitmap* bmp = Gdiplus::FromHBITMAP(...);
Gdiplus::Image* img = bmp;

とできますよ。
ここまできたら以前のコードにこだわらずに、
今回YuOさんが紹介してくださった回転のコードを
適用してみてもいいんじゃないかと思いますよ。

編集 削除
kure  2006-04-18 17:36:34  No: 61242  IP: 192.*.*.*

訂正です。

> Gdiplus::Bitmap* bmp = Gdiplus::FromHBITMAP(...);

Gdiplus::Bitmap* bmp = Gdiplus::Bitmap::FromHBITMAP(...);

編集 削除
温野菜  2006-04-19 13:31:12  No: 61243  IP: 192.*.*.*

subaruさん、kureさん、YuOさん、ありがとうございます。

型の変換はできました。
また、YuOさんからご紹介のサイト(MSDN)のコードも、動きました。

ただ、もうひとつアドバイスをお願いします。

ファイルの読み書きですが、

        Image* image = new Image(L"./\\aaa.JPG");

で読み込むことはできるのですが、"./\\aaa.JPG"を変数に置き換えた場合、Lをどのようにしたらいいのか分かりません。

char ss[20];
strcpy(ss, "./\\aaa.JPG");
Image* image = new Image((const WCHAR *)ss);

では読み込まれませんでした。

読み込みだけなら、教えていただいた形で、CImageから置き換えてもいいと思ったのですが、書き込みでも同じ問題があります。

あと一歩なので、なんとかアドバイスをお願いいたします。

編集 削除
Blue  2006-04-19 13:37:25  No: 61244  IP: 192.*.*.*

>char ss[20];
>strcpy(ss, "./\\aaa.JPG");
>(const WCHAR *)ss

このような危険なキャストはしてはいけません。

WCHAR(wchar_t)とCHAR(char)の違いは理解できていますか?

> new Image(L"./\\aaa.JPG");
引用符の前の L は何なのかわかりますか?


wchar_t ss[ 20 ];
wcscpy( ss, L"./\\aaa.JPG" );// wcs〜で始まる関数を調べてみてください
Image* image = new Image( ss );

編集 削除
温野菜  2006-04-20 12:44:02  No: 61245  IP: 192.*.*.*

たいへんありがとうございました。
解決しました。

char ch[] = "テスト";
wchar_t wch[10];
setlocale(LC_ALL, "ja"); 
mbstowcs(wch, ch, 10);

で、char型文字列をワイド文字列に置き換えることができました。

画像は、スムーズに回転します。
タグ情報も残っていました。

編集 削除