WINXP .NET MFC です。
jpeg画像を表示したり、拡縮することはできるのですが、回転させる方法が全く分かりません。
左右90度、画質の劣化なく回転させられればいいのですが、ネットで検索しても「これぞ」というサイトは見つけることができませんでした。
フリーソフトがいくつもあったりして、かなり難しいのでしょうか。
アトデバイスをお願いいたします。
すみません。回答ではないのですが。。。
つまり,90度回転させた画像を,再びjpeg形式で
*元画像以上の劣化無しで* 保存したい,という事でしょうか?
# 確か,昔,誰かがどこかでその方法を検討してた記憶があるですよ。
# 結論は「可能」だったはずで。。。
簡単なやり方としては、↓みたいなものとか。少し遅いかもしれませんが。
CImage source
source.Load("source.jpg");
int w = source.GetWidth();
int h = source.GetHeight();
CImage rotated;
rotated.Create(h, w, source.GetBPP());
for(int x = 0 ; x < w ; ++x)
for(int y = 0 ; y < h ; ++y)
rotated.SetPixel(y, x, source.GetPixel(x, y));
rotated.Save("rotated.jpg");
速くしたければ、CImage::GetPixelAddressでイメージバッファを取ってきて、
直接操作したほうがいいかもしれません。
申し訳ありません。
いきなりCImage source;でつまずきました。
フォームビューの***View.cppのOnBnClickedButton1() の中に書き込んだところ、
'CImage' : 定義されていない識別子です。
というエラーになりました。
単純データ型クラスと思ったのですが、違うのでしょうか。
過去ログを調べたり、ネット検索したりしましたが、調べ方が悪いのか、参考になるものは見つかりません。
もう一点、手前で煮詰まっているので確認できないのですが、source.Load("source.jpg"); の中の"source.jpg"は、頭にパスをつなげて、ファイルから直接読み出すことはできるのでしょうか。
同様に、Saveは、直接ファイルに書き込むことが出来るのでしょうか。
MSDNをみると、Loadの引数は「読み込むイメージ ファイルの名前を示す文字列へのポインタ。 」となっていますが、junさんのサンプルではファイル名が直接書き込まれています。
もしポインタに置き換えるのであれば、先に読み込む必要があるのでしょうか。
お恥ずかしいのですが、初心者でよく分かりません。
アドバイスをお願いいたします。
> 'CImage' : 定義されていない識別子です。
直接/間接に,atlimage.hをインクルードしていないのではないですか?
> MSDNをみると、Loadの引数は「読み込むイメージ ファイルの名前を示す文字列へのポインタ。 」となっていますが、junさんのサンプルではファイル名が直接書き込まれています。
文字列とは何かを知りましょう。
リテラルの文字列はcharの配列であり,
式中では(一部の例外を除いて)先頭の文字へのポインタに変換されます。
エラーは、#include "atlimage.h" で解決し、90度回転させることができました。
たいへんありがとうございました。
不思議なのは、2度実行すると、180度回転ではなく、元に戻ってしまいます。
これは、こうしたものなのでしょうか。
また、ピクセル単位の処理なので、画質の劣化はないと考えていいのでしょうか。
junさんがおっしゃるとおり、処理速度はかなり遅く、GetPixelAddressも調べてみたのですが、よく分かりません。
あちこち検索しているのですが、まだ、それらしいものは見つかりません。
重ねて勝手なお願いですが、もしご存知のサイト等があればご紹介いただけませんでしょうか。
YuOさん、ありがとうございます。
行き違いになりました。
MSDNの読み方が足りませんでし
手拍子で送信してしまいました。。。。
踏んだり蹴ったりです。(涙)
YuOさん、ありがとうございます。
行き違いになりました。
MSDNの読みこみが足りませんでした。
> 不思議なのは、2度実行すると、180度回転ではなく、元に戻ってしまいます。
junさんの提示されたコードは回転のアルゴリズムではありません。
X-Y座標を入れ替えるアルゴリズムです。
本来の回転のアルゴリズムは
dstX = srcX * cos(rad) - srcY * sin(rad)
dstY = srcX * sin(rad) + srcY * cos(rad)
になります。ただし、ディスプレイの座標系は常に正の範囲なので
回転中心へのオフセットを考慮して上式を適用することになります。
また、90,180,270,360度以外の回転の場合はピクセルとピクセルの間に
隙間ができる場合があり、その隙間を補完する必要があります。
google先生に聞いてみたらこんなサイトがあったので参考までにどうぞ
http://homepage2.nifty.com/tsugu/sotuken/rotation/
kureさんご紹介のサイトを拝見し、まずはべたっとコピペして、エラーを消していったのですが、
pDstImg[y2][x2] = pSrcImg[y1][x1]; の部分で、
error C2676: 二項演算子 '[' : 'HBITMAP__' は、この演算子または定義済の演算子に適切な型への変換の定義を行いません。 (新しい動作; ヘルプを参照)
というエラーが消せません。
これが使えるのはBitmapだけでしょうか。
また、色の処理もよく分かりません。
とりあえず左右に90度倒せればいいのですが。。。
ご指導いただけませんでしょうか。
> まずはべたっとコピペして、エラーを消していったのですが
ロジックを理解せずにとりあえず動けば良いという思想で
プログラムを作るのはよろしくないです。
提示したURLのページには画像回転のためだけの理論と実装が書いてあり、
そのために必要なもろもろの理論や実装方法は書かれていません。
温野菜さんが行いたいことのために
知らなければならないことは他にもあるはずです。
CImageクラスがどういうものか、Windowsが画像をどのように扱うのか
といった知識無しにはプログラムは書けません。
error C2676の部分ですが、
HBITMAPはビットマップハンドルであり、
[]演算子でのアクセスはできません。
実際のピクセルデータにアクセスするには
GetDIBitsなどでピクセルデータを取得する必要があります。
URL先のページでもCreateImageは自作してくださいと書いてあります。
この関数が返すのはHBITMAPではなくピクセルデータのポインタのはずです。
>また、色の処理もよく分かりません。
色の処理とはなにをさしているのかこちらには判りかねます。
具体的なコードなどを挙げて回答者が答えれる質問をしてください。
エラーの解決を依頼する場合は、
エラーの内容およびエラーの起きた箇所のソースを
提示されなければ詳細な回答は得られないものと思ってください。
回答者はエスパーではありませんので。
90度回転は、
rotated.SetPixel(h - y - 1, x, source.GetPixel(x, y));
でしたね、間違ってました。
(ついでにGetPixelAdressはGetBitsの方がよかったかも・・・)
お詫びにバッファ直操作の90度回転を作ってみたので置いときますが、
kureさんのおっしゃるように、なるべく画像処理の仕組みを理解してからやった方がいいと思います。
↓この本なんかとっつきやすいかも?
http://www.amazon.co.jp/exec/obidos/ASIN/479800958X/
#include <atlimage.h>
int main()
{
CImage source;
source.Load("source.jpg");
int w = source.GetWidth();
int h = source.GetHeight();
int bpp = source.GetBPP();
int sourcePitch = source.GetPitch();
unsigned char *sourceBuffer = (unsigned char *)source.GetBits();
if(bpp < 8)
return false; // bpp < 8 のときは略
CImage rotated;
rotated.Create(h, w, bpp);
int rotatedPitch = rotated.GetPitch();
unsigned char *rotatedBuffer = (unsigned char *)rotated.GetBits();
int bytepp = bpp / 8;
for(int x = 0 ; x < w ; ++x){
for(int y = 0 ; y < h ; ++y){
int destX = h - y - 1;
int destY = x;
unsigned char *read = sourceBuffer + x * bytepp + y * sourcePitch;
unsigned char *write = rotatedBuffer + destX * bytepp + destY * rotatedPitch;
for(int c = 0 ; c < bytepp ; ++c)
*write++ = *read++;
}
}
rotated.Save("rotated.jpg");
return 0;
}
junさん、たいへんありがとうございました。
時計回り、反時計回りとも、クリックごとに90度づつ回してゆくことができました。
(回転という表現が正しくないのは理解しますが)
グラフィックのことをもう少し理解したいので、ご紹介の本は購入します。
重ね重ね申し訳ありません。
「解決」にチェックするのを忘れました。