おたすけ~

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
初心者

おたすけ~

#1

投稿記事 by 初心者 » 15年前

C言語C++プログラム初心者です。質問です。
モノクロのBMP画像をブルートーン画像に変換する
簡単?なプログラムを作ろうと思ってます。
コンパイルは通って実行ファイルはできたのですが、
実行しても何も起こらない場合があります。
画像の読み込みのための配列 out[/url] のサイズによって、
実行できたりします。
たとえば512×512のBMP画像を扱う場合、
unsigned char out[512*512*3]; //3はRGBの3種の意味
と書いて、実行できてちゃんと画像も出力できたのですが、
unsigned char out[800*800*3]; 
・・・などとサイズを大きくすると途端に実行できなくなります。
これって配列用のメモリが大きくて使えないってことなのでしょうか?
初心者なのでよくわからないのですが、ご指摘お願いします。

やっくん

Re:おたすけ~

#2

投稿記事 by やっくん » 15年前

> unsigned char out[800*800*3];
これは画像512×512に対してこの大きさを確保しているのでしょうか?

良ければソースコードを示していただけ無いでしょうか。

スレ主

Re:おたすけ~

#3

投稿記事 by スレ主 » 15年前

“やっくん”さんレスありがとう。

え~と、
例えば横512縦512のBMP画像を扱うときは、
unsigned char out[512*512*3];
と書いています。(3はRGBの3です)
ちなみにこの場合はちゃんと実行できて、画像が出力されます。
256*256などのもっと小さな画像でも上手くいきます。

問題は、もっと大きな画像で、例えば横800縦800のBMP画像にした時は
unsigned char out[800*800*3]; とちゃんと書き直していますが、
これだと実行ファイルは作成されますが、実行しても画像が出力されません。
コンパイルもできているので、ソースの文法的には間違ってないはずです。

メモリ不足なのでしょうか?
たぶん使っているPCのメモリスペックが低すぎて、大きな画像にすると
メモリを割り当てられないのではないかと予想してますが、
こういう事ってありえますか?
使っているPCは古いですし、常駐ソフトもあるのでメモリの使用量は酷い状態です。
物理メモリが448MBなのに対し、かなりオーバーしてます。(おそらく仮想メモリ使用)

こういう状態では実行してもメモリが確保できずに実行アプリが終了してしまうのでしょうか?

Justy

Re:おたすけ~

#4

投稿記事 by Justy » 15年前

 開発環境は何ですか?
 ひょっとしてその配列はローカル変数でしょうか?

ROM

Re:おたすけ~

#5

投稿記事 by ROM » 15年前

スレ主さん
とりあえず規約を読んでから投稿しましょう。
(名前は何かに統一して下さい)

初心者

Re:おたすけ~

#6

投稿記事 by 初心者 » 15年前

すみません。名前は初心者に固定します。

コンピュータのOSはwindowsXPです。
メモリは448MB(実際は512MBだけど残りはビデオメモリ用?)
メモリは常駐ソフトなどが多いため物理メモリをオーバーしていて、
おそらくハードディスクでの仮想メモリが使われている状態です。
そのため、たまにPCの動作が遅くなります。

C言語、C++の開発ツールですが、
使ってるコンパイラはボーランド?ってやつです。
メモ帳などにソースを書いて"test.cpp"で保存、
コマンドプロンプトでディレクトリを指定して
bcc32 test.cpp などと打ち込むとコンパイルされるやつです。

さっき確認してみましたが、
640×480の画像では実行できたけど、出力画像が変なことに...orz
本当なら青くなるはずが、ノイズみたくなってる・・・orz
なぜか512×512以下のサイズならちゃんと青くなるのです。

↓以下はソースです(//☆☆☆☆☆☆のついてる4箇所のサイズを書き換えてください)

#include<stdio.h>

int main(void)
{
FILE *fp;

unsigned char head[54];
unsigned char out[480*640*3]; //☆☆☆☆☆☆☆☆☆☆
unsigned char name[40];
unsigned char in[20];


int n,i;
printf("ファイル名と拡張子の入力(例 test.bmp):");
scanf("%s",in);
fp=fopen(in,"rb");
if(fp == NULL)
{
printf("ファイル %s が存在しません。",in);
return 1;
}
else
{
fread(head,1,54,fp);
fread(out,1,480*640*3,fp);//☆☆☆☆☆☆☆☆☆☆
}
fclose(fp);

fp=fopen("head.dat","wb");
fwrite(head,1,54,fp);
fclose(fp);

//ここはモノクロからブルートーンへの変換処理

for(n=0;n<=480*640;n++)//☆☆☆☆☆☆☆☆☆☆(ここは*3不要)
{
for(i=0;i<=2;i++)
{
if(i==0)//blue
{
if(out[n*3+i]>=128)
{
out[n*3+i]=255;
}
else
{
out[n*3+i]=out[n*3+i]*2;
}
}
else if(i==1)//green
{
out[n*3+i]=out[n*3+i];
}
else//red
{
if(out[n*3+i]<=127)
{
out[n*3+i]=0;
}
else
{
out[n*3+i]=(out[n*3+i]-128)*2;
}
}
}
}

fp=fopen("head.dat","rb");
fread(head,1,54,fp);
fclose(fp);
sprintf(name,"out_%s",in);
fp=fopen(name,"wb");
fwrite(head,1,54,fp);
fwrite(out,1,480*640*3,fp); //☆☆☆☆☆☆☆☆☆☆
fclose(fp);
return 0;
}


初心者なんでまだ効率が悪く汚い書き方をしてます・・・悪しからず。

釣り師

Re:おたすけ~

#7

投稿記事 by 釣り師 » 15年前

同一人物だとしたら申し訳ない。
他の人とハンドルネームかぶってませんでしょうか?

初心者

Re:おたすけ~

#8

投稿記事 by 初心者 » 15年前

DecentLoveさん

そうです。初心者、スレ主、両方とも同一人物です。
スレ主と書いた方がわかりやすいと思って変えてしまいました。すみません。
以降、名前を“初心者”に統一します。

ROM

Re:おたすけ~

#9

投稿記事 by ROM » 15年前

回答ではなくてすみませんが、規約を読みましょう・・・。
初心者という名前は使わないで下さいと書いてあります。

Justy

Re:おたすけ~

#10

投稿記事 by Justy » 15年前

 何点か。

 まず、画像データを格納するバッファ outはローカル変数だとhttp://ja.wikipedia.org/wiki/%E3%82%B9% ... D%E3%83%BCする可能性があります。
 staticをつけるなど静的領域にとるか、mallocでアロケートして下さい。

 次に for(n=0;n<=480*640;n++) はループ回数が多いかと。

 それから書き間違えを防ぐために、ソース内の 640とか 480は
[color=#d0d0ff" face="monospace]
#define IMAGE_SIZE_X 640
#define IMAGE_SIZE_Y 480
[/color]

のようにマクロ定義し、main関数内ではこのマクロを使用するようにしてみて下さい。

 最後に見た感じこのソースだと最初の段階で head変数にヘッダ情報が入っているわけですし
head.datは書き出す必要も読み出す必要も無いような気がします。

ピッチャー

Re:おたすけ~

#11

投稿記事 by ピッチャー » 15年前

>>ROMさん
すみません。
規約には初心者のような簡単な名前はダメみたいなので、
たぶんスレ主とかもダメっぽいですね...
一番最初の投稿で初心者と書いてしまいましたし...

投稿名前はピッチャーに統一します。何度も失礼しました。
質問投稿者のピッチャーです。

>>Justyさん

ご指摘ありがとうございます。

え~と、とりあえずout[/url]の部分を静的ローカル変数(?)にする、
staticをつけるということでしょうか...

>>次に for(n=0;n<=480*640;n++) はループ回数が多いかと。

回数が多いとおかしなことになるのでしょうか...
もっと大きなサイズの画像を扱う場合はどう書くべきでしょうか...

>>マクロ定義し、main関数内ではこのマクロを使用するようにしてみて下さい。

なるほど。ぜひ試してみます。

>>最後に見た感じこのソースだと最初の段階で head変数にヘッダ情報が入っているわけですし
head.datは書き出す必要も読み出す必要も無いような気がします。

最初はおっしゃる通りにhead.datなしで書いていたんですが、
なぜかヘッダの内容(特にBMPフォーマットを決めるデータ部)が変わって出力されてしまう現象
が起こったため、原因がわからずとりあえず応急処置でこう書いておいたのです。

ピッチャー

Re:おたすけ~

#12

投稿記事 by ピッチャー » 15年前

やはり640×480などの大きな画像になると変になります。
512×512とか512×256とかのサイズだと上手くいきます。

添付は512×512の上手くいった出力画像の圧縮jpgバージョンです。
256階調のモノクロ画像がブルー調画像になっていれば成功です。

↓ソースの更新(マクロ定義とstaticだけ書き変えました)

#include<stdio.h>

#define IMAGE_SIZE_X 640
#define IMAGE_SIZE_Y 480

int main(void)
{
FILE *fp;
unsigned char head[54];
static unsigned char out[IMAGE_SIZE_X * IMAGE_SIZE_Y *3]; //☆☆☆☆☆☆☆☆☆☆
unsigned char name[40];
unsigned char in[20];


int n,i;
printf("ファイル名と拡張子の入力(例 test.bmp):");
scanf("%s",in);
fp=fopen(in,"rb");
if(fp == NULL)
{
printf("ファイル %s が存在しません。",in);
return 1;
}
else
{
fread(head,1,54,fp);
fread(out,1,IMAGE_SIZE_X * IMAGE_SIZE_Y *3,fp);//☆☆☆☆☆☆☆☆☆☆
}
fclose(fp);

fp=fopen("head.dat","wb");
fwrite(head,1,54,fp);
fclose(fp);

//ここはモノクロからブルートーンへの変換処理

for(n=0;n<=IMAGE_SIZE_X * IMAGE_SIZE_Y;n++)//☆☆☆☆☆☆☆☆☆☆ここは*3不要
{
for(i=0;i<=2;i++)
{
if(i==0)//blue
{
if(out[n*3+i]>=128)
{
out[n*3+i]=255;
}
else
{
out[n*3+i]=out[n*3+i]*2;
}
}
else if(i==1)//green
{
out[n*3+i]=out[n*3+i];
}
else//red
{
if(out[n*3+i]<=127)
{
out[n*3+i]=0;
}
else
{
out[n*3+i]=(out[n*3+i]-128)*2;
}
}
}
}

fp=fopen("head.dat","rb");
fread(head,1,54,fp);
fclose(fp);
sprintf(name,"out_%s",in);
fp=fopen(name,"wb");
fwrite(head,1,54,fp);
fwrite(out,1,IMAGE_SIZE_X * IMAGE_SIZE_Y *3,fp); //☆☆☆☆☆☆☆☆☆☆
fclose(fp);
return 0;
}

Justy

Re:おたすけ~

#13

投稿記事 by Justy » 15年前

>staticをつけるということでしょうか
 OKです。

>回数が多いとおかしなことになるのでしょうか
 最後のループの nは 640*480で 307200となり、最大 out[307200*3+2 = 921602]に対して
アクセスするわけですが、実際の配列は 640*480*3 = 921600しかありません。
 今のプログラムだと配列の領域を越えてメモリにアクセスすることになります。


>なぜかヘッダの内容(特にBMPフォーマットを決めるデータ部)が変わって出力されてしまう現象
 多分ですが、その forの回数が多かった為、配列のサイズを超えてメモリに対し書き込みを行った結果
head等がの中身が破壊されたのでしょう。
 forの回数を正しく設定し、配列のサイズを超えて書き込みをしないようにして下さい。


>もっと大きなサイズの画像を扱う場合はどう書くべきでしょうか
 そうですね、BMPのヘッダを見て画像の縦横サイズを取得し、そのサイズに合わせてメモリを
動的にアロケート(malloc)、処理をするのが普通ではないかと。
 これなら環境が許す限り大きなファイルでも開けますので。


>やはり640×480などの大きな画像になると変になります
 どう変になりますか?

 先に挙げた点を修正すればうまくいくかと思うのですが。
 んーと、IMAGE_SIZEの X, Yを 640、480に設定しておきながら実際に読み込んだ画像は
512×512ということはないですか?
 或いはその逆とか。
画像

へろりくしょん

Re:おたすけ~

#14

投稿記事 by へろりくしょん » 15年前

ざらっと見ただけですが。

ビットマップの場合ピクセルデータは1列あたり4の倍数バイトで整列されてなければならない。 というルールがあります。

このあたりが考慮されてないような気がしますが。

ピッチャー

Re:おたすけ~

#15

投稿記事 by ピッチャー » 15年前

>>Justy...さん

>最後のループの nは 640*480で 307200となり、最大 out[307200*3+2 = 921602]に対して
アクセスするわけですが、実際の配列は 640*480*3 = 921600しかありません。

お~なるほど!ご指摘ありがとうごうざいます。
ヘッダの不具合もそれが原因でしたか...間抜けでした(笑)

>そうですね、BMPのヘッダを見て画像の縦横サイズを取得し、そのサイズに合わせてメモリを
動的にアロケート(malloc)、処理をするのが普通ではないかと。

なるほど。ヘッダからサイズを取得するのはなんとかできそうです。
malloc関数はまだ使ったことがないんですが、調べてみてできれば試してみます。

>へろり...さん

あ~!そういえばそんな制約もありました。すっかり忘れてました。
今まで試した画像サイズだと偶然にも行データが4の倍数になっていたので、
ギリギリセーフだったみたいです。
でも横サイズ202とか199みたいな画像だったら今のままでは駄目ですね。
対策としては、横サイズが4の倍数でない画像を例外処理で除外して仕様にするか、
埋め合わせを考慮して作るかのどっちかですね。

みなさんご指摘どうもです。
コードを書き直して動作チェックをしたらまた近々投稿します。

ピッチャー

Re:おたすけ~

#16

投稿記事 by ピッチャー » 15年前

Justyさん、へろりさん、皆様方、

おかげさまで上手くできたっぽいです。
マクロ定義で固定せずに、ヘッダから読み取った画像サイズ情報を基に
out配列をmalloc関数で動的にメモリ確保をしてみました。
malloc関数は初めて使ってみましたが、便利そうですね。
最後のfreeの使い方がこれで合ってるのかは気になりますが...

これで対応画像であれば大きなサイズでもたぶん上手くブルー調画像に変換できそうです。
ただし8ビット(256パレット)のグレースケールには対応してないのが痛いですが...

何か他にご指摘があればお伝えください。

添付は成功した640×480の画像です。
↓できあがったソースです。メイン関数だけなので汚いですがご勘弁を!

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
FILE *fp;
unsigned char head[54];
unsigned char *out; //☆☆☆☆☆
int IMAGE_SIZE_X;
int IMAGE_SIZE_Y;
int size;
unsigned char name[40];
unsigned char in[20];
int gazo_type, info , color_type;

int n,i;
printf("ファイル名と拡張子の入力(例 test.bmp):");
scanf("%s",in);
fp=fopen(in,"rb");
if(fp == NULL)
{
printf("ファイル %s が存在しません。",in);
return 1;
}
else
{
fread(head,1,54,fp);

IMAGE_SIZE_X=head[18]+head[19]*16*16;
IMAGE_SIZE_Y=head[22]+head[23]*16*16;
gazo_type=head[0]+head[1]*16*16;
info=head[14];
color_type=head[28];

printf("横サイズ:%d\n\n",IMAGE_SIZE_X);
printf("縦サイズ:%d\n\n",IMAGE_SIZE_Y);

if(gazo_type != 19778)
{
printf("ビットマップ以外の画像には対応しておりません\n\n");
return 1;
}
else if(IMAGE_SIZE_X % 4 != 0)
{
printf("このサイズには対応しておりません...orz\n\n");
printf("横サイズが4の倍数の画像を使用してください\n\n");
return 1;
}
else if(info != 40)
{
printf("このタイプのビットマップ画像には対応しておりません\n\n");
return 1;
}
else if(color_type != 24)
{
printf("24ビットグレースケール画像以外は対応しておりません\n\n");
return 1;
}

size=IMAGE_SIZE_X*IMAGE_SIZE_Y;

out = (unsigned char*)malloc(sizeof(int)*size) ;

fread(out,1,size *3,fp);//☆☆☆☆☆☆☆☆☆☆
}
fclose(fp);

//ここはモノクロからブルートーンへの変換処理

for(n=0;n<=size-1;n++)//☆☆☆☆☆☆☆☆☆☆ここは*3不要
{
for(i=0;i<=2;i++)
{
if(i==0)//blue
{
if(out[n*3+i]>=128)
{
out[n*3+i]=255;
}
else
{
out[n*3+i]=out[n*3+i]*2;
}
}
else if(i==1)//green
{
out[n*3+i]=out[n*3+i];
}
else//red
{
if(out[n*3+i]<=127)
{
out[n*3+i]=0;
}
else
{
out[n*3+i]=(out[n*3+i]-128)*2;
}
}
}
}

sprintf(name,"out_%s",in);
fp=fopen(name,"wb");
fwrite(head,1,54,fp);
fwrite(out,1,size *3,fp); //☆☆☆☆☆☆☆☆☆☆
fclose(fp);
free(out);
return 0;
}

Justy

Re:おたすけ~

#17

投稿記事 by Justy » 15年前

>最後のfreeの使い方がこれで合ってるのかは気になりますが
 大丈夫ですよ。合ってます。


>何か他にご指摘があればお伝えください

・ scanf("%s",in);
 inは 19文字分しか領域がないので、scanf("%19s",in)としておいた方が無難です。

・ fread(head,1,54,fp);
 このファイルは実は20バイトで、54バイトもないかもしれません。
 その場合 head配列内の足りない部分はゴミが残り、以後の各種チェックがうまく働かない可能性があります。
 ちゃんと 54バイト分読めたかどうか確認しておいた方がいいでしょう。
 
・ IMAGE_SIZE_X=head[18]+head[19]*16*16
 出来れば head[18] | (head[19] << 8) のようにビットシフトで組み立てて欲しいところです。

・ gazo_type != 19778
 (char)head[0] != 'B' || (char)head[1] != 'M'とした方が意図は判りやすいかと。

・ fp=fopen(name,"wb");
 書き込み時も fopenの戻り値はチェックしましょう。

ピッチャー

Re:おたすけ~

#18

投稿記事 by ピッチャー » 15年前

Justy...さん

お早い返答ありがとうございます。

>scanf("%19s",in)

こういう書き方は初めて知りました。

>ちゃんと 54バイト分読めたかどうか確認しておいた方がいいでしょう。

ほほう。そこまで想定できませんでした。プログラムは油断ならぬ、ですね...

>ビットシフトで組み立てて欲しいところです。
>(char)head[0] != 'B' || (char)head[1] != 'M'とした方が

なるほどその手がありましたか。確かに僕のだと何やってるか分かりづらいですね。

ご指摘どうもです。ちゃんと書き直せたらまた近々レスします。

Justy

Re:おたすけ~

#19

投稿記事 by Justy » 15年前

>最後のfreeの使い方がこれで合ってるのかは気になりますが
> 大丈夫ですよ。合ってます。
 あ、1点。

・ out = (unsigned char*)malloc(sizeof(int)*size)
 mallocは失敗することがありますので、このチェックも必要ですね。

ピッチャー

Re:おたすけ~

#20

投稿記事 by ピッチャー » 15年前

>Justyさん...

改良できました~。

>ちゃんと 54バイト分読めたかどうか確認しておいた方がいいでしょう。

ここはfeof関数で対応しました。これも初めて使ってみました。

まだまだダメ出しはあると思いますが、
いろいろ勉強になりました。ありがとうございました。

ところで、せっかく画像を扱うこともできたので、C言語でCUIではなく
いわゆるGUIアプリのようなものを作ってみたいのですが、
GUIを扱うのはウィンドウズAPIやウィンドウズOSを知らなければならず、
ウィンドウを表示するだけでもかなりたくさんコードを書く必要があるらしいので、
初心者には難しくめんどくさいらしいのですが、
何かもっと簡単に端折って作成できる方法はありますでしょうか?
楽に作れるということは自由度が下がると思いますが、自由度は下がっても構いません。
でもさすがにペタペタ貼り付けるフレームワーク?みたいなのはできるだけ避けたいのです。
ちゃんとテキスト形式でコードを書いて翻訳するものならC言語以外でも構いません。

何を作るかですが、とりあえず最初は、実行ファイルをクリックすると窓が開き、
画像を表示し、ボタンやキーを押すと画像がスライドして次の画像に変わる
ような画像ビューアーです。


↓ソースです

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
FILE *fp;
unsigned char head[54];
unsigned char *out; //☆☆☆☆☆
int IMAGE_SIZE_X;
int IMAGE_SIZE_Y;
int size;
unsigned char name[40];
unsigned char in[20];
int info , color_type;


int n,i;
printf("ファイル名と拡張子の入力(例 test.bmp):");
scanf("%19s",in);
fp=fopen(in,"rb");
if(fp == NULL)
{
printf("\n\nファイル %s が存在しません\n\n",in);
return 1;
}
else
{
fread(head,1,54,fp); //ヘッダの読み込み

if(feof(fp))
{
printf("画像のヘッダ情報が54バイト分ありません\n\n");
return 1;
}

IMAGE_SIZE_X=head[18] | (head[19] << 8) ;
IMAGE_SIZE_Y=head[22] | (head[23] << 8) ;
info=head[14];
color_type=head[28];

printf("横サイズ:%d\n\n",IMAGE_SIZE_X);
printf("縦サイズ:%d\n\n",IMAGE_SIZE_Y);

if((char)head[0] != 'B' || (char)head[1] != 'M')
{
printf("ビットマップ以外の画像には対応しておりません\n\n");
return 1;
}
else if(IMAGE_SIZE_X % 4 != 0)
{
printf("このサイズには対応しておりません...orz\n\n");
printf("横サイズが4の倍数の画像を使用してください\n\n");
return 1;
}
else if(info != 40)
{
printf("このタイプのビットマップ画像には対応しておりません\n\n");
return 1;
}
else if(color_type != 24)
{
printf("24ビットグレースケール画像以外は対応しておりません\n\n");
return 1;
}


size=IMAGE_SIZE_X*IMAGE_SIZE_Y;

out = (unsigned char*)malloc(sizeof(int)*size) ;

if(out==NULL)
{
printf("メモリの確保に失敗しました\n\n");
return 1;
}

fread(out,1,size *3,fp);//☆☆☆☆☆☆☆☆☆☆
}
fclose(fp);




//ここはモノクロからブルートーンへの変換処理

for(n=0;n<=size-1;n++)//☆☆☆☆☆☆☆☆☆☆ここは*3不要
{
for(i=0;i<=2;i++)
{
if(i==0)//blue
{
if(out[n*3+i]>=128)
{
out[n*3+i]=255;
}
else
{
out[n*3+i]=out[n*3+i]*2;
}
}
else if(i==1)//green
{
out[n*3+i]=out[n*3+i];
}
else//red
{
if(out[n*3+i]<=127)
{
out[n*3+i]=0;
}
else
{
out[n*3+i]=(out[n*3+i]-128)*2;
}
}
}
}

sprintf(name,"out_%s",in);
fp=fopen(name,"wb");
if(fp == NULL)
{
printf("ファイルの作成に失敗しました");
return 1;
}
else{
fwrite(head,1,54,fp);
fwrite(out,1,size *3,fp); //☆☆☆☆☆☆☆☆☆☆
}
fclose(fp);
free(out);
return 0;

}

ピッチャー

Re:おたすけ~

#21

投稿記事 by ピッチャー » 15年前

このスレは解決済みにします。

閉鎖

“C言語何でも質問掲示板” へ戻る