BMPを選択した範囲内で保存する機能の追加

[戻る]
KM 2003/12/12(金) 14:19:50
はじめまして。KMと申します。
実は現在2つのBMPを使ってピクセルの減算処理をしていまして、その処理した画像
をBMPで保存する機能をつけたいのですがどのようにして保存すればよいかわかりま
せん。保存の仕方としましては、メニューのところに従来ある「名前をつけて保存」と
は別に「画像を保存」という項目を設け、そこをクリックすると画面全体ではなく処理
後の画像の部分だけを保存するという形にしたいのですが、画像を保存する関数?など
があるのでしょうか?
お願いします。

開発環境はwindows XPで、Visual C++6.0です。

KM 2003/12/12(金) 14:20:51
すみません。
開発環境の追加でMFCでプログラミングしています。

n [E-Mail] 2003/12/12(金) 14:57:06
その2つのBMPはプログラム中でどのように保持しているのですか?

KM 2003/12/12(金) 15:08:28
nさん返信ありがとうございます。

メニューからA画像指定、B画像指定として各画像をメニューのファイルを開くから読
み込めるようになっています。格納の仕方は、各ピクセル値を読み込んでA画像とB画
像それぞれの格納先を作っておき、実行時に指定されたらそこへ格納されるようになっ
ています。そして表示ボタンを押すとA画像とB画像を並べて2枚表示し、その下にC画
像として処理後の画像が表示されるようになっています。そのC画像だけを保存したい
のです。

よろしくお願いします。

n [E-Mail] 2003/12/12(金) 15:26:01
それぞれの格納方法は具体的にわかりませんか?

KM 2003/12/12(金) 15:34:32
// CHosei01Doc シリアライゼーション

void CHosei01Doc::Serialize(CArchive& ar)
{
      DWORD dwDummy;
         WORD wDummy;
      int i,j,k;
      int nTuika,nHasuu;

    if (ar.IsStoring())
    {
        // TODO: この位置に保存用のコードを追加してください。
    }
    else
    {
        // TODO: この位置に読み込み用のコードを追加してください。
      

      nHasuu=lWidth*3 % 4; //4バイトの倍数に対する端数を計算する
        if(nHasuu!=0) nTuika=4-nHasuu; //端数があれば、追加すべきバイ
トを求める
        else nTuika=0;


        for(i=0;i<13;i++)
            ar>>dwDummy;
            ar>>wDummy;

            k=bImageType;
            for(j=239;j>=0;j--)    //上下を逆にする
                for(i=0;i<320;i++)
            ar>>bBlue[k][i][j]>>bGreen[k][i][j]>>bRed[k][i][j];
            
            
                            
            
    }
}

//////////////////////////////////////////////////////////////////////////////

// CHosei01Doc コマンド


void CHosei01Doc::OnMenuImageA() 
{
    // TODO: この位置にコマンド ハンドラ用のコードを追加してください
    bImageType=0;
}

void CHosei01Doc::OnMenuImageB() 
{
    // TODO: この位置にコマンド ハンドラ用のコードを追加してください
    bImageType=1;
}


ソースをのせてみましたが、お分かりいただけるでしょうか?
bimageTypeに0か1が入ることでAかBかを切り替えています。
そして格納は主に上側のDoc.cppで行っています。

n [E-Mail] 2003/12/12(金) 15:41:49
C画像はどのように保持されていますか?

KM 2003/12/12(金) 15:50:06
A画像とB画像の減算した結果を表示しているだけなので、どこかへ格納ということは
していません。

n [E-Mail] 2003/12/12(金) 15:53:34
bBlue,bGreen,bRedはそれぞれどのように宣言されていますか?

KM 2003/12/12(金) 16:08:22
void CHosei01View::OnMenuHyouji(CCmdUI* pCmdUI) 
{    
unsigned char r0,r1,r2,g0,g1,g2,b0,b1,b2;


        for(j=0;j<240;j++)
        for(i=0;i<320;i++){
                r0=pDoc->bRed[0][i][j];
                g0=pDoc->bGreen[0][i][j];
                b0=pDoc->bBlue[0][i][j];
                pDC->SetPixel(x0+i,y0+j,RGB(r0,g0,b0)); //A
画像の表示

                r1=pDoc->bRed[1][i][j];
                g1=pDoc->bGreen[1][i][j];
                b1=pDoc->bBlue[1][i][j];
                pDC->SetPixel(x1+i,y0+j,RGB(r1,g1,b1)); //B
画像の表示

                r2=fabs(r1-r0);
                g2=fabs(g1-g0);
                b2=fabs(b1-b0);

                  pDC->SetPixel(x1+i,y0+j,RGB(r2,g2,b2)); //C
画像の表示
}

といった感じです。

n [E-Mail] 2003/12/12(金) 16:11:58
いや、どう使われているかではなくどう宣言されているかなんだけど。

「unsigned char bBlue[2][320][240];」かな?

KM 2003/12/12(金) 16:16:18
すみません。

unsigned char bRed[2][512][512];
unsigned char bGreen[2][512][512];
unsigned char bBlue[2][512][512];

です。

n [E-Mail] 2003/12/12(金) 16:44:58
ではまず、
bBlue,bGreen,bRedを宣言しているのと同じ場所に、

 BYTE header[13*sizeof(DWORD)+sizeof(WORD)];

と宣言して下さい。
次にCHosei01Doc::Serialize()の

 for(i=0;i<13;i++)
  ar>>dwDummy;
  ar>>wDummy;

を、

 for(i=0;i<sizeof(header)/sizeof(BYTE);i++)
  ar>>header[i];

と変えて下さい。
で次にSerialize()のif(ar.IsStoring()){}の{}の間に、

 for(i=0;i<sizeof(header)/sizeof(BYTE);i++)
  ar<<header[i];

 for(j=0;j<240;j++){
  for(i=0;i<320;i++){
   ar<<fabs(pDoc->bBlue[1][i][j]-pDoc->bBlue[0][i][j]);
   ar<<fabs(pDoc->bGreen[1][i][j]-pDoc->bGreen[0][i][j]);
   ar<<fabs(pDoc->bRed[1][i][j]-pDoc->bRed[0][i][j]);
  }
 }

を入れて下さい。
とりあえずこれで全領域がファイルに保存されるか確かめてみて下さい。

n [E-Mail] 2003/12/12(金) 16:49:18
ちょっと間違い。
最後のfor文のところを修正します。

 for(j=0;j<240;j++){
  for(i=0;i<320;i++){
   unsigned char b=fabs(pDoc->bBlue[1][i][j]-pDoc->bBlue[0][i][j]);
   unsigned char g=fabs(pDoc->bGreen[1][i][j]-pDoc->bGreen[0][i][j]);
   unsigned char r=fabs(pDoc->bRed[1][i][j]-pDoc->bRed[0][i][j]);
   ar<<b<<g<<r;
  }
 }

KM 2003/12/12(金) 17:01:48
pDocが定義されていないというエラーが出たのですが、定義はどう書けばいいのでしょ
うか?

n [E-Mail] 2003/12/12(金) 17:03:16
ごめんなさい!
「pDoc->」は不要です。

KM 2003/12/12(金) 17:06:06
はい、できました。

n [E-Mail] 2003/12/12(金) 17:24:50
え?マジでうまく保存できた?

では次に保存する部分の指定だけれど、
さっきのheaderを宣言した場所に、

 CRect saverect;

を付け足して下さい。
次にIsStoring()のところで、最初のfor文の前に

 BITMAPINFOHEADER* pbi=
  reinterpret_cast<BITMAPINFOHEADER*>(header+sizeof(BITMAPFILEHEADER));
 pbi->biWidth=saverect.Width();
 pbi->biHeight=saverect.Height();

を入れて下さい。次に最後のfor文を

 for(j=saverect.top;j<saverect.bottom;j++){
  for(i=saverect.left;i<saverect.right;i++){
   (略)
  }
 }

と変えて下さい。
ここで問題がsaverectの内容の指定ですが、とりあえずIsStoring(){}の最初に

 saverect.SetRect(10,10,20,20);

とでも入れて、実行してみて下さい。

KM 2003/12/12(金) 17:34:05
プログラムは実行できましたが、保存をしても保存した画像を見ることができない状態
です。

n [E-Mail] 2003/12/12(金) 17:47:00
pbiの所にこれも入れて下さい。

 BITMAPFILEHEADER* pfh=reinterpret_cast<BITMAPFILEHEADER*>(header);
 pfh->bfSize=((saverect.Width()*3+3)&~3)*saverect.Height();

あと何か抜けがあったかな…??

KM 2003/12/12(金) 17:49:40
はい、入れました。

n [E-Mail] 2003/12/12(金) 17:54:27
また間違い:
これを先に書いて、
 pbi->biSizeImage=((saverect.Width()*3+3)&~3)*saverect.Height();

これを修正。
 pfh->bfSize=pbi->biSizeImage+13*sizeof(DWORD)+sizeof(WORD);

KM 2003/12/12(金) 18:01:03
ちょっと混乱してきたのですが、現在このようになっていますが合ってますでしょう
か?

  saverect.SetRect(10,10,20,20);
  for(i=0;i<sizeof(header)/sizeof(BYTE);i++)
   ar<<header[i];
  BITMAPINFOHEADER* pbi=
     reinterpret_cast<BITMAPINFOHEADER*>(header+sizeof(BITMAPFILEHEADER));
    pbi->biWidth=saverect.Width();
    pbi->biHeight=saverect.Height();

  BITMAPFILEHEADER* pfh=reinterpret_cast<BITMAPFILEHEADER*>(header);
  pbi->biSizeImage=((saverect.Width()*3+3)&~3)*saverect.Height();
  pfh->bfSize=pbi->biSizeImage+13*sizeof(DWORD)+sizeof(WORD);

n [E-Mail] 2003/12/12(金) 18:05:50
いやこんな感じに。

  saverect.SetRect(10,10,20,20);

  BITMAPINFOHEADER* pbi=…;
    pbi->biWidth=…;
    pbi->biHeight=…;
  pbi->biSizeImage=((saverect.Width()*3+3)&~3)*saverect.Height();

  BITMAPFILEHEADER* pfh=…;
  pfh->bfSize=pbi->biSizeImage+13*sizeof(DWORD)+sizeof(WORD);

  for(i=0;i<sizeof(header)/sizeof(BYTE);i++)
    ar<<header[i];

KM 2003/12/12(金) 18:08:10
すいません。できました。

n [E-Mail] 2003/12/12(金) 18:27:19
保存も表示もできましたか?

ところで私は時間切れなので次は明日になります悪しからず。

KM 2003/12/12(金) 18:32:34
ほんとにありがとうございますm(__)m

表示はまだできません。
ほんとうに申し訳ないのですが明日、明後日と私の都合上、進めることができないの
で、もしご都合がよろしければ月曜日からでもまた教えてください。

今日は長時間にわたり、ありがとうございました。

n [E-Mail] 2003/12/13(土) 16:36:30
これでうまくいくハズです。

//Doc.hに追加するもの
enum { HEADERSIZE=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER) };
unsigned char header[HEADERSIZE];//BYTEもunsigned charも同じ型


//Doc.cppのar.IsStoring()のとこ
saverect.SetRect(10,10,20,20);

BITMAPFILEHEADER* pbfh=reinterpret_cast<BITMAPFILEHEADER*>(header);
BITMAPINFOHEADER* pbih=reinterpret_cast<BITMAPINFOHEADER*>(header+sizeof
(BITMAPFILEHEADER));
pbih->biWidth=saverect.Width();
pbih->biHeight=saverect.Height();
pbih->biSizeImage=((pbih->biWidth*3+3)&~3)*pbih->biHeight;
pbfh->bfSize=HEADERSIZE+pbih->biSizeImage;
for(int i=0;i<HEADERSIZE;++i) ar<<header[i];

const int rest=((pbih->biWidth*3+3)&~3)-pbih->biWidth*3;//詰め物の長さの計算
for(int j=saverect.bottom-1;j>=saverect.top;--j)
{
    for(int i=saverect.left;i<saverect.right;++i)
    {
        unsigned char b=fabs(bBlue[1][i][j]-bBlue[0][i][j]);
        unsigned char g=fabs(bGreen[1][i][j]-bGreen[0][i][j]);
        unsigned char r=fabs(bRed[1][i][j]-bRed[0][i][j]);
        ar<<b<<g<<r;
    }
    for(int i=0;i<rest;++i) ar<<(unsigned char)0;//詰め物
}

KM 2003/12/15(月) 13:36:58
ありがとうございます。
最後のところ、iが再定義されてしまってますが、なにか他の変数でも問題ないですか?
一応kにすることでエラーを失くしましたが。


ファイルから「名前をつけて保存」には保存できるのですが、表示することができませ
ん。
新しくメニュー上に保存機能をつけて、他のソフトでもBMPとして表示できるように
したいのですがそういうことは可能なんでしょうか?
できるとしたら、Doc.cppかView.cppどっちにかけばいいんでしょうか?

n [E-Mail] 2003/12/15(月) 15:15:22
>最後のところ、iが再定義されてしまってますが
間違いました。kにしてOKです。

このコードで保存したファイルは、私の方では
Windows XP付属のWindows Picture and Fax ViewerとPaintで正常に表示できたので、
間違ってはいないと思います。


>新しくメニュー上に保存機能をつけて、他のソフトでもBMPとして表示できるように
>したいのですがそういうことは可能なんでしょうか?
これが保存するコードなので可能では??? 意味違うかな??

>できるとしたら、Doc.cppかView.cppどっちにかけばいいんでしょうか?
保存コードはDoc.cppに書くのが普通です。

KM 2003/12/15(月) 15:39:18
メニューに保存という項目を追加したのですが、classwizardの設定をDocクラスでオブ
ジェクトIDはMENU_Hozonnにして新しくできたところに何かコードを入力するのでしょ
うか?教えていただいたコードはDoc.cppのシリアライゼーションに書いてあるので、新
しく作るのはちょっと違うかなぁと思っているのですがそこらへんの設定がよくわかり
ません。ほんとに初心者でこういう基本的なことがわかってなくてすみません。教えて
いただければ幸いです。

n [E-Mail] 2003/12/15(月) 16:17:00
>新しくできたところに何かコードを入力するのでしょうか?

普通の「上書き保存」「名前をつけて保存」とは違うようにしたいのであれば、
それでいいと思います。

KM 2003/12/15(月) 17:04:50
つまり教えていただいたコードは「名前をつけて保存」を使って保存ができるというこ
とですよね?
「名前をつけて保存」でやっているのですが、どうも保存したファイル形式がBMPじ
ゃなくてプロパティでファイルの種類を見ると“ファイル”って書いてあるだけで、そ
のせいもあってかファイルを開くことができません。コードはあってるはずなんですけ
ど、どうしてでしょうか??

n [E-Mail] 2003/12/15(月) 17:07:31
保存するときに「名前.bmp」という風に、末尾に.bmpと付けていないのでは?

KM 2003/12/15(月) 17:18:04
できました!
本当にありがとうございました。

おかげで大きく前進することができました。
この方法を参考に次のステップに役立てたいと思います。

KM 2003/12/15(月) 17:18:50
[[解決]]
解決ボタンを忘れてました。
解決です。

古いスレッドには追加発言できません。