アプリ版:「スタンプのみでお礼する」機能のリリースについて

いつもお世話になってます。
c言語を使った画像処理を学び始めました。
入力画像の白(RGB値255,255,255)以外の画像を黒(RGB値0,0,0)
に変換するプログラムを作成したいと思っているのですが、
入力画像の大きさによっては正常に命令を実行してくれないことがあるので、
改善点をご教示いただきたいと考え投稿させていただきました。ソースプログラムは

#include<stdio.h>
#definenx 100//画像の幅
#defineny 100//画像の高さ

int main(void)
{
FILE *fp,*fp2;
int i,j;
unsigned char header[54];
unsigned char screen[nx][ny][3];

/* ファイルから読む */

fp=fopen("input.bmp","rb"); //ビットマップ形式 ,24ビットカラー
fread(header,1,54,fp);// ヘッダ(54バイト)を飛ばす */
fread(screen,1,nx*ny*3,fp); // 残りはデータ(最下行から順に入る)

//(255,255,255)以外なら黒(0,0,0)に
for(j=0;j<ny;j++)
for(i=0;i<nx;i++)
if(screen[j][i][0]!=255||screen[j][i][1]!=255||screen[j][i][2]!=255){
screen[j][i][0]=0;
screen[j][i][1]=0;
screen[j][i][2]=0;
}


fclose(fp);

/* ファイルに書く */
fp=fopen("output.bmp","wb");
fwrite(header,1,54,fp); /* ヘッダ */
fwrite(screen,1,nx*ny*3,fp); /* データ */
fclose(fp);

return 0;
}

となっています。ここで、画像の高さ、幅を100以下にすると正常に変換できなくなります。
どなたか原因がお分かりでしたらお知らせ願えないでしょうか?

A 回答 (7件)

#3 訂正


自分的に256BMPメインで扱ってたんで一応確認してみましたが
24ビットだとちょっと変わった変化してます

24ビットカラーのとき横4で割り切れないピクセル数だと
データとしては 1Pixl×3バイト(BGR) + 余りバイト数
となっているようです
ここら辺は御自分でペイント使って出来たファイルの
中身調べれば分かると思います

上のような動作ですと#3で書いたコードでは結局動かないはずです
コードを全面的に書き直します(Windows前提のVCコード)
ついでにサイズはファイルからそのまま読んでます
エラー処理などは省いています

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

#include <windows.h>

int main()
{
FILE *fp;

BITMAPFILEHEADERfileheader;
BITMAPINFOHEADERheader;
RGBTRIPLE*rgb;

unsigned char *screen;
intsize;

/* ファイルから読む */
fp=fopen("c:\\aaa.bmp","rb"); //ビットマップ形式 ,24ビットカラー

fread(&fileheader,sizeof(fileheader), 1,fp); // ヘッダ(54バイト)を飛ばす */
fread(&header,sizeof(header), 1,fp); // ヘッダ(54バイト)を飛ばす */

//size = ((header.biWidth % 4) == 0) ? (header.biWidth * header.biHeight * 3) : (header.biWidth * header.biHeight * 3) + header.biWidth % 4;
size = fileheader.bfSize - (sizeof(fileheader) + sizeof(header));

screen = (unsigned char *)malloc(size);

fread(screen,size,1,fp); // ヘッダ(54バイト)を飛ばす */
fclose(fp);

rgb = (RGBTRIPLE *)screen;

//(255,255,255)以外なら黒(0,0,0)に
for(int y = 0 ; y < header.biHeight ; y ++) {
for(int x = 0 ; x < header.biWidth ; x ++, rgb ++) {
if((rgb->rgbtRed != 255) || (rgb->rgbtGreen != 255) || (rgb->rgbtBlue != 255)) {
rgb->rgbtRed = 0;
rgb->rgbtGreen = 0;
rgb->rgbtBlue = 0;
}
}

char *addr = ((char *)rgb) + (header.biWidth % 4);
rgb = (RGBTRIPLE *)addr;
}

free(screen);


//別ファイルに書く分は手抜き
}
    • good
    • 0

#6です。



>>添字の順番が逆になっています
>とのことですが、どうやら画像のデータは(y,x)という風に書かれている>ようなので、
>これで正しいと思っています。

もう一度よくみてください
screenの宣言は
> unsigned char screen[nx][ny][3];
で、添字の範囲は[0~(nx-1)][0~(ny-1)][0~2]となっています。

対して
> for(j=0;j<ny;j++)
> for(i=0;i<nx;i++)
> if(screen[j][i][0]!=255||screen[j][i][1]!=255||screen[j][i][2]!=255){
では[j=0~(ny-1)]i=[0~(nx-1)][0~2]を使おうとしています。

最初のプログラムではnx=100,ny=100なので入れ替わっても同じでしたが、これを
nx=200,ny=100
としたら
[0~(200-1)][0~(100-1)][0~2]と宣言して
[0~(100-1)][0~(200-1)][0~2]とで使おうとすることになります。

Cでは添字の範囲外のチェックを行いません。「添字が範囲外」というエラーは出ません。その添字で指定された領域をアクセスしようとします。
結果、たまたまそこにあった関係ない変数が書き変わったり、そもそもアクセス制限にかかって致命的はエラーとなったりします。

今回は最終的に動的確保で落ちつきそうなので、直接関係ないところではありますが、指摘されていただきました。
    • good
    • 0
この回答へのお礼

お返事ありがとうございます。
ご指摘いただいた内容をよく理解せず返答してしまいました。申し訳ございません。

確かに変数宣言時に
unsigned char screen[nx][ny][3];
となっていますね。
これでは添え字が間違っていると認めざるを得ません。
再度ご指摘いただかなければ誤った認識のままでいるところでした。

ご指摘、本当にありがとうございます

お礼日時:2010/02/02 23:35

100x100のときも結果としてうまく読めてるみたいですが


> unsigned char screen[nx][ny][3];

と宣言しておいて
> for(j=0;j<ny;j++)
> for(i=0;i<nx;i++)
> if(screen[j][i][0]!=255||screen[j][i][1]!=255||screen[j][i][2]!=255){
と添字の順番が逆になっています。

この回答への補足

失礼しました。
御礼を送る相手を間違えてしまいました。

ご回答ありがとうございます。

>添字の順番が逆になっています
とのことですが、どうやら画像のデータは(y,x)という風に書かれているようなので、
これで正しいと思っています。

この認識は間違いなのでしょうか?

補足日時:2010/02/02 02:39
    • good
    • 0
この回答へのお礼

なるほど、そうなっていたのですね!
おかげさまで無事問題が解決しました。
たびたび質問に答えてくださり、本当にありがとうございます。

お礼日時:2010/02/02 02:38

#4補足


#4に書いてあるコメントは元々のソースのコピーで残っててめちゃくちゃです
(直し忘れたのをそのまま乗せてしまいました環境はVC6++のコンソールアプリ)

一応データサイズ自分で求めましたがめんどくさいので結局
ファイル情報に記載してあるサイズ分を読むようにしています
(計算方法としてコメント行で記載してあるだけだと思ってください)

>char *addr = ((char *)rgb) + (header.biWidth % 4);
>rgb = (RGBTRIPLE *)addr;
ここでデータポインタを次の行の位置に合わせています

本来はエラー処理含めファイルの中身がBMPなのか
24ビットカラーなのか等もチェックする方がより正確なものになります
    • good
    • 0
この回答へのお礼

なるほど、そうなっていたのですね!
おかげさまで無事問題が解決しました。
たびたび質問に答えてくださり、本当にありがとうございます。

お礼日時:2010/02/02 02:38

>自分のプログラムにどう組み込めばいいのか、ご教示願えませんでしょうか?


とっても安直な方法です

>#define nx 100 //画像の幅

#define nx_org 100 //画像の幅

int nx;

int main(void)
{
nx = (nx_org % 4) 0 ? nx_org : (nx_org / 4 + 1) * 4;

>unsigned char screen[nx][ny][3];
コンパイラ次第でエラーになったりならなかったりするので
エラーの場合
unsigned char *screen;
screen = memalloc(sizeof(unsigned char), nx * ny * 3);
などと変えてください
アロケーションした場合 free もお忘れなく

それ以外もともとの御自分のコードで動くはずです
(保管部分のデータに対しては一切操作してません)

コンパイル・確認して無くてここに直に書いたんで間違ってるかも
(イメージだけつたわりゃ良いかなと)
    • good
    • 0
この回答へのお礼

そのようにすればよかったのですか。
さっそくご教示いただいた部分をプログラムに取り込むと

>unsigned char screen[nx][ny][3];

の部分でエラー「定数式が必要です」
のエラーがでてしまいます。

unsigned char *screen;
screen = memalloc(sizeof(unsigned char), nx * ny * 3);
としてみましたが、こちらのプログラムの

>//(255,255,255)以外なら黒(0,0,0)に

以下がエラー(配列または、ポインタでない変数に添字が使われました)
となり、コンパイルできませんでした。
ポインタは扱ったことがないのでどのように変更すればいいのか分かりません。
恐縮ですが、この部分の対処法をご教示願えないでしょうか?

お礼日時:2010/02/01 01:34

ビットマップは1ライン4バイト境界で作成しないといけませんが


その辺りの問題ではないですか?

それ以外はプログラム的に間違ってないと思います

4バイト境界になってないBMPを作るためには不足分のバイトを自分で
補ってファイルを作る必要があります
(その部分のデータは何でも良かったと思いますが0にしておいた方がいいとは思います)

例)
横 2pixl のBMP作りたいときは 横 4pixlのデータを作る
(2pxl分のデータは0にしておく)
ということです

BMP構造としては以下参照
http://www.kk.iij4u.or.jp/~kondo/bmp/
    • good
    • 0
この回答へのお礼

ご回答いただきありがとうございます。

おかげさまでbmpの内容について少し理解できました。対処する方針もわかりました。
しかしながら、実際に直接画像データを操作するとなると、勝手がわからず

>>4バイト境界になってないBMPを作るためには不足分のバイトを自分で
補ってファイルを作る必要があります

とのことですが、具体的にはどのように操作すればよいのでしょうか?
お示しいただいたサイトのプログラム

line = (Width * BitCount) / 8; // 1 ラインあたりのデータ数
if ((line % 4) != 0) {
line = ((line / 4) + 1) * 4; // 4byte 境界にあわせる
}

for(i = 0; i < Height; i++) {
position = OffBits + line * (Height - (i + 1)); // 行の先頭位置
fseek(fp, position, SEEK_SET);
fread(buf, line, 1, fp); // 値の読み込み
}
を自分のプログラムにどう組み込めばいいのか、ご教示願えませんでしょうか?

お礼日時:2010/02/01 00:07

あなたのいう「正常に変換できない」がどのようなことをさしているのかさっぱりわかりませんが, 画像の大きさを画像自身からとるようにす

ればいいのでは?

この回答への補足

ご回答ありがとうございます。

>>「正常に変換できない」がどのようなことをさしているのかさっぱりわかりません

とのことですので、内容をお伝えします。
具体的には黒色への変換が途中で終わってにしまいます。
赤い四角形だけ描いてある画像に対しこのプログラムを実行すると
四角の下半分だけ黒くなるのですが、上は赤のままなのです。
ペイントでキャンパスの大きさを100*100にしてから実行すると
この場合、四角はすべて黒となります。
拙い説明で申し訳ありませんが、これでお分かりいただけたでしょうか?

>>画像の大きさを画像自身からとるようにすればいいのでは?

汎用性を考え、当然その考えは持ちました。
しかしながら、画像処理に関する知識がないため、やりたくてもできないのが現状です。
よろしければ画像の大きさを画像自身から取得する方法をご教示ください

補足日時:2010/01/31 23:56
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!