動的な三次元配列で画像のRGBを取得

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

動的な三次元配列で画像のRGBを取得

#1

投稿記事 by 六氏 » 14年前

画像(bmp)を読み込み、そのRGBデータを配列に入れる処理を以下のように作りました。
xとyにはint型で数字が入っています。

unsigned char header[54],rgb[x][y][3];
fr=fopen("gazou.bmp","rb");
fread(header,1,54,fr);
fread(rgb,1,x*y*3,fr);

rgb[0][0][1]などでRGBの取得が成功したので、rgbを動的な三次元配列として確保したあとにfreadしたいと思っています。
そこで以下のように作ってみたところ、メイクは成功するのですが※の部分で止まってしまいます。

unsigned char header[54];
unsigned char*** rgb;
rgb=(unsigned char***)malloc(sizeof(unsigned char**)*x);
for(i=0;i<x;++i){
rgb=(unsigned char**)malloc(sizeof(unsigned char*)*y);
for(j=0;j<y;++j){
rgb[j]=(unsigned char*)malloc(sizeof(unsigned char)*3);
}
}

fr=fopen("gazou.bmp","rb");
fread(header,1,54,fr);
fread(rgb,1,x*y*3,fr);

for (i=0;i<x;i++) {
for (j=0;j<y;j++) {
free(rgb[j]);※
}
free(rgb);
}
free(rgb);

本やHPにあった二次元配列の作り方を参考にして作ってみたのですが何か根本的な勘違いをしているのでしょうか?
お教えいただけると幸いです。

アバター
h2so5
副管理人
記事: 2212
登録日時: 14年前
住所: 東京
連絡を取る:

Re: 動的な三次元配列で画像のRGBを取得

#2

投稿記事 by h2so5 » 14年前

この動的確保の方法ですと、三次元配列のメモリ上の位置が1次元的に連続していないので
fread(rgb,1,x*y*3,fr);
をした時点でデータが壊れています。

rgb=(unsigned char*)malloc(sizeof(unsigned char)*x*y*3);
のように確保するべきだと思います。

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: 動的な三次元配列で画像のRGBを取得

#3

投稿記事 by bitter_fox » 14年前

六氏 さんが書きました:rgbを動的な三次元配列として確保したあとにfreadしたいと思っています。
そこで以下のように作ってみたところ、メイクは成功するのですが※の部分で止まってしまいます。

コード:

fr=fopen("gazou.bmp","rb");
fread(header,1,54,fr);
fread(rgb,1,x*y*3,fr);

for (i=0;i<x;i++) {
       	for (j=0;j<y;j++) {
       	    	free(rgb[i][j]);※
       	}
       	free(rgb[i]);
}
free(rgb);
本当にfreeで止まってしまっていますか?
個人的にはfreadの方が問題がありそうな気がするのですが・・・

[hr][追記]
それからコードを載せる際にはcodeタグで囲っていただきますようにお願いします。

六氏

Re: 動的な三次元配列で画像のRGBを取得

#4

投稿記事 by 六氏 » 14年前

>>h2so5さん
rgb=(unsigned char*)malloc(sizeof(unsigned char)*x*y*3);
だとx*y*3に相当する一次元の配列が出来るということだと思うのですが、取得したいものは[0,255,0],[255,0,0],[0,0,255]のようになっているので・・・

>>bitter_fox
間違いなく最初のfreeまでは処理が進んでいます。
freadをコメントアウトすると止まららないので、freadでデータが壊れても無視して進み、freeで解放するときに壊れたデータで止まっているのだと思います。
codeタグのご指摘ありがとうございます。次回から必ず使うようにします。

アバター
kimuchi
記事: 163
登録日時: 14年前
住所: 東京

Re: 動的な三次元配列で画像のRGBを取得

#5

投稿記事 by kimuchi » 14年前

コード:

unsigned char header[54];
unsigned char* rgb;
rgb=(unsigned char*)calloc(x*y,sizeof(unsigned char*)*3);

fr=fopen("gazou.bmp","rb");
fread(header,1,54,fr);
for(i=0;i<x*y;++i){
    fread(rgb,1,3,fr);
    rgb++;
}

free(rgb);
取得したいものは[0,255,0],[255,0,0],[0,0,255]のようになっているので・・・
上のサンプルは、
callocを使って3バイトのメモリを「x*y」個確保してファイルから3バイトずつ読み込ませています。
これでうまくいきそうですが・・・いかがでしょう?

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: 動的な三次元配列で画像のRGBを取得

#6

投稿記事 by bitter_fox » 14年前

六氏 さんが書きました: 間違いなく最初のfreeまでは処理が進んでいます。
freadをコメントアウトすると止まららないので、freadでデータが壊れても無視して進み、freeで解放するときに壊れたデータで止まっているのだと思います。
確かにh2so5さんが仰られた
h2so5 さんが書きました: この動的確保の方法ですと、三次元配列のメモリ上の位置が1次元的に連続していないので
fread(rgb,1,x*y*3,fr);
をした時点でデータが壊れています。
が原因のようですね。

細かく見ると

コード:

rgb=(unsigned char***)malloc(sizeof(unsigned char**)*x);
を実行したときのrgb[0]の値を0x00004000として。(注:以下の値はすべて仮定の値です)
その次の

コード:

for(i=0;i<x;++i){
       rgb[i]=(unsigned char**)malloc(sizeof(unsigned char*)*y);
で、
rgb[0]の時に0x00005000が入ったとします。

そこで

コード:

fread(rgb,1,x*y*3,fr);
とした場合は、
rgb[0]の0x00004000を先頭にしてファイルの内容をx*y*3バイト分書き込んでしまいます。(この時にx*y*3バイトは最初に確保した範囲を当然超えているのでヒープ領域を破壊する恐れもあります。)
で、書き込んだ結果
*(0x00004000) = 0xff (←ここは画像データの値
...
となり、次のfreeのところはどうなるかというと
free(*(0x00004000))がfree(0xff)となって解放してはいけない領域を解放しようとしてエラーになってしまったのだと思います。

ですので、freadのところを各ピクセルごとに読み込むように変えて実行してみてください。

コード:

fr=fopen("gazou.bmp","rb");
fread(header,1,54,fr);
for (i = 0; i < x; i++) // xが縦というのは少々おかしいですが縦
{
        for (j = 0; j < y; j++) // yが横というのも同じく変ですが横
        {
                fread(rgb[i][j],1,3,fr);
        }
}
 
for (i=0;i<x;i++) {
        for (j=0;j<y;j++) {
                free(rgb[i][j]);※
        }
        free(rgb[i]);
}
free(rgb);

六氏

Re: 動的な三次元配列で画像のRGBを取得

#7

投稿記事 by 六氏 » 14年前

kimuchiさんとbitter_foxさんのコードを見ながら考えてみたところ、上手く出来ました。
止まってしまう原因も理解でき、何を勘違いしていたのかもわかったのでとても勉強になりました。
ありがとうございました。

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 15年前
住所: 北海道札幌市
連絡を取る:

Re: 動的な三次元配列で画像のRGBを取得

#8

投稿記事 by Dixq (管理人) » 14年前

> rgb=(unsigned char*)malloc(sizeof(unsigned char)*x*y*3);
> だとx*y*3に相当する一次元の配列が出来るということだと思うのですが、取得したいものは[0,255,0],[255,0,0],[0,0,255]のようになっているので・・・

解決されたようですが、補足です。
二次元配列も結局は一次元配列と同じことですよ。
例えば

int arr[2][3]; 

と確保すると

* * * * * *

このように横6つの連続した領域が確保され、例えばarr[1][0]と示せば4つめの要素を示せるだけです。
逆に言えば配列要素が連続していない二次元配列において、arr[1][0]を示すと要素ではないものを指してしまいます。

今回、

コード:

    rgb=(unsigned char***)malloc(sizeof(unsigned char**)*x);
    for(i=0;i<x;++i){
        rgb[i]=(unsigned char**)malloc(sizeof(unsigned char*)*y);
        for(j=0;j<y;++j){
            rgb[i][j]=(unsigned char*)malloc(sizeof(unsigned char)*3);
        }
    }
このように要素が全て別々に作られているので、アドレスが連続しているとは限りません。
よって、期待しない要素を示してしまっていたのだと思います。

閉鎖

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