• ベストアンサー

困りました。

現在SDKにて画像処理ソフトを作成しています。 そして最近フィルタリングの機能をつけてみようと思いました。 知識として3×3の配列を利用するフィルタ処理で、ラプラシアン、ノイズ除去などを行うやりかた(アルゴリズム)は知っているのですが、配列の確保に問題が出てきました。 プログラムの仕様として、入力画像の輝度情報は入力時にその情報量分のメモリを確保し、ポインタとして格納しています。つまり配列として扱うには1次元配列となってしまいます。もちろん配列をあやつる変数部分を変えるだけで、2次元的な表現はできるのですが、フィルタリング処理をする際複雑になってしまい、コーディングするのが困難になってしまっています。 そこでポインタとしての1次元配列を2次元配列に格納したいのですが、単に格納だけならできるのですが、格納する2次元配列も動的にメモリ確保を行いたいのです。 色々とごちゃごちゃした言い回しになってしまいましたが、第一の問題は2次元配列として扱えるよう動的にメモリを確保したいということです。つまり src[i][j] このように扱えるようメモリを確保したいということです。 以前メモリ確保についてここで質問させてもらいましたが、2次元配列の場合、宣言として (*src)[256] とした場合少なくとも縦が256の画像を読み込むことができます。しかしこの場合は”256”と決まった量を与えているため記述できるのですが、自分が行いたいのは関数の引数で画像サイズを取得し、その量のメモリを動的に確保したいため、画像サイズに応じて確保するメモリの量が変わってきてしまいます。そのため(*src)[256]と書くことができませんが、しかし自分では確保したメモリ配列を2次元配列として扱いたいため(*src)[256]と書かなければなりません。 つまりこれが壁になっています。 関数の引数で受け取った画像サイズ分のメモリ確保を行い、かつそのメモリ空間をを2次元配列として扱いたい場合、どのように記述すればよいでしょうか? ひとつ考えたことが **src としてポインタのポインタを使用し、 src = (unsigend char **)GlobalAlloc(GHND, sizeof(unsigned char *) * height); for(i=0; i<height; i++)   src[i] = (unsigned char *)GlobalAlloc(GHND, sizeof(unsigned char) * width); for(i=0; i<height; i++){     for(j=0; j<width; j++){ src[i][j] = Input[width * i + j];     } } このように定義してみたんですけど、src[i][j]と記述はできたのですが実行時にエラーが出てしまいました。 なにか効率の良い方法はないでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.7

★inthefloi さんご指摘ありがとう。 ・GlobalAlloc( GHND, sizeof(unsigned char *) * height ) は  GHND 定数ではなくて GPTR 定数が正しいですね。  GHND ならば GlobalLock() 関数でロックしてから使います。 ・GPTR定数⇒GMEM_ZEROINIT と GMEM_FIXED の組み合わせ(固定メモリ用)  GHND定数⇒GMEM_ZEROINIT と GMEM_MOVEABLE の組み合わせ(移動可能メモリ用)  確保したメモリをポインタに直接キャストで代入できるのは GMEM_FIXED 定数の場合だけです。  移動可能メモリで確保した場合は一度メモリをロックしてから使います。 使い方1:固定メモリ unsigned char **src; if ( (src = (unsigned char **)GlobalAlloc(GPTR,sizeof(unsigned char *) * height)) != NULL ){  :  処理  :  GlobalFree( src ); } 使い方2:移動可能メモリ unsigned char **src; HGLOBAL hMem; if ( (hMem = GlobalAlloc(GHND,sizeof(unsigned char *) * height)) != NULL ){  if ( (src = (unsigned char **)GlobalLock(hMem)) != NULL ){ ←ロックしてから代入   :   処理   :   GlobalUnlock( hMem ); ←そく解放するなら必要ない  }  GlobalFree( hMem ); } その他: ・GlobalAlloc() 関数よりは HeapAlloc() 関数の方が高速に処理されます。  また、HeapAlloc() 関数以外に C 言語の malloc()、calloc()、free() や  C++ 言語の new、delete の演算子を利用した方が移植性が高くなり良い。 ・Windows 環境の限定のソフトならば HeapAlloc() 関数などを利用すれば一番効率がよくなります。  なお、メモリ容量が 2M バイトを超える場合は VirtualAlloc() 系の関数を利用します。  GlobalAlloc()、HeapAlloc() 関数は 4MB 以内のメモリ量を扱うように設計されていると MSDN の  マニュアルに載っています。単純にメモリを確保するときには malloc()、calloc() が便利かも  しれませんよ。 ・あるいは、最大の height 値の配列を宣言する方法もあります。  ただし、スタックサイズには注意して下さい。→初期値では最大スタックが 1MB までかな。  height が最大で 1000 ピクセルならば unsigned char *src[ 1000 ]; と宣言してから  for ( i = 0 ; i < height ; i++ ){   src[ i ] = &Input[ width * i ];  }  としてもいいかも。 ・まぁ、いろいろと工夫して下さい。 // 1次元配列⇒2次元配列に変換する関数 unsigned char **FuncSetArray( unsigned char Input[], int width, int height ) {  static unsigned char *src[ 1000 ]; ←height が最大 1000 ピクセルとする  int i;    for ( i = 0 ; i < height ; i++ ){   src[ i ] = &Input[ width * i ];  }  return src; } // 使い方 unsigned char **src = FuncSetArray( Input, width, height ); int x, y; // 2次元配列 src でアクセスすると1次元配列 Input を取得/代入できる for ( y = 0 ; y < height ; y++ ){  for ( x = 0 ; x < width ; x++ ){   src[ y ][ x ] = (unsigned char)(src[y][x] ^ 0xFF);  } } ※下の『参考URL』に GlobalAlloc() 系の使い方が載っています。こちらも参考に!

参考URL:
http://wisdom.sakura.ne.jp/system/winapi/win32/win90.html
noconan
質問者

補足

回答ありがとうございました。 メモリ確保のための各関数ごと丁寧に解説を頂きありがとうございました。GlobalAllocやHeapAllocなどは4MB以内の確保を行うという条件は知りませんでした。 とても大きいサイズの画像を扱う場合はもしかしたらオーバーフローするかもしれませんね・・・・ しかし自分ではmallocよりもGlobalAllocを使ってみたかったというのもあります。別に特別な理由があるというわけではなくw それにまだSDKをはじめたばかりですので、HeapAllocも今後使用してみたいと思いますが、まずはGlobalAllocの使用に慣れたいと思い、今回GlobalAllocのみ使用してみようと思いました。 そしてサンプルプログラムまで載せていただきありがとうございます。 unsigned char **src = FuncSetArray( Input, width, height ); 確かに効率がよいです。 この表現はJavaにも出てきますが、目から鱗がはがれましたw 今後参考にしたいと思います。 ありがとうございました。

その他の回答 (6)

  • dekopa-
  • ベストアンサー率42% (161/378)
回答No.6

既に1次元配列でコードを組んでいるのでしょうか?それを2次元にするのですか? 単に、1次元配列を読む際、2次元に変換するマクロを組めば良いと思いますが。つまり、格納位置はそのままで値だけを取り出すのです。 value(x, y, w) array[y * w + x] // wはwidth さらに、画像サイズ(width, height)や配列arrayをメンバに持って、輝度情報を返すメンバをもったクラスにしてしまえば、マクロより柔軟に操作できます。

noconan
質問者

お礼

回答ありがとうございました。 予想されたとおり、すでに入力画像は1次元配列に格納済みであり、その1次元配列を2次元配列のように操作を行いたいというのが今回の目的です。 教えていただいたマクロについてですが、自分はまだ未熟でマクロまで知識が及んでいないため、どのように操作してよいか想像もつかない現状です。 せっかくアドバイスをしていただいたにもかかわらず、こんな返答しかできないことをお許しください。 しかし、今後マクロ関係まで学んでいきたいので、そのときには参考にさせてもらいます。 ありがとうございました。

noname#30727
noname#30727
回答No.5

GlobalAllocにGHNDを指定したらアドレスは返りません。

noconan
質問者

お礼

俺はあほですね。 おそらく質問文に書いたプログラムにエラーが生じた理由は完全にその間違いからくるものでしょう。 ご指摘ありがとうございました。

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.4

★修正。 ・int i, j; ではなく  int i, x, y; の宣言でした。 ・以上。

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.3

★ポインタへの配列にセットすれば良い。 ・unsigned char src[ height ][ width ] とアクセスしたい場合は、  unsigned char *src[ height ] を確保して、これに  unsigned char Input[ height * width ] のアドレスを  width バイト毎にポインタ値をセットします。 ・つまり下のようになります。 サンプル: void func( unsigned char Input[], int width, int height ) {  unsigned char **src; ←unsigned char *src[height] とするためのポインタ  int i, j;    if ( (src = (unsigned char **)GlobalAlloc(GHND,sizeof(unsigned char *) * height)) != NULL ){   // 1次元配列 Input を2次元配列 src でアクセスできるようにポインタをセット   for ( i = 0 ; i < height ; i++ ){    src[ i ] = &Input[ width * i ];   }      // 2次元配列 src でアクセスすると1次元配列 Input を取得/代入できる   for ( y = 0 ; y < height ; y++ ){    for ( x = 0 ; x < width ; x++ ){     src[ y ][ x ] = (unsigned char)(src[y][x] ^ 0xFF);    }   }   GlobalFree( src );  } } その他: ・上記の『src[ y ][ x ] = (unsigned char)(src[y][x] ^ 0xFF);』処理を行うと  写真で言うところのポジティブ→ネガティブ変換になります。  この辺の処理はサンプルですので。 ・あと GlobalAlloc() 関数よりは、HeapAlloc() 関数の方が高速になります。  また、上記のサンプルでは Input[] 配列を2二元のポインタ配列 src にマッピング  しているので src[y][x] で代入などすると Input[] にそく反映します。  src はポインタ配列ですので Input[] の内容をコピーしなくても良い。  1次元配列を2次元配列でアクセスできるようにしただけですので。 ・以上。おわり。

参考URL:
http://msdn.microsoft.com/library/ja/default.asp?url=/library/ja/jpmemory/html/_win32_heapalloc.asp
noconan
質問者

お礼

回答ありがとうございました。 方法としてはNo2さんと同じですね。 自分ももう少し熟考しなければいけませんね。 反省です。 しかもHeapAlloc関数まで教えていただきありがとうございました。

回答No.2

src = (unsigend char **)GlobalAlloc(GHND, sizeof(unsigned char *) * height); この sizeofは char* では なく void* (アドレスのサイズ)です src[i] = (unsigned char *)GlobalAlloc(GHND, sizeof(unsigned char) * width); この部分は実エリアを確保する必要はなく、Inputの中のポジションをもって くれば良いです プログラムは以下のようになります #include <stdio.h> #include <stdlib.h> #define HEIGHT 10 #define WIDTH 16 int main(int argc, char **argv) { int i, j; char Input[HEIGHT * WIDTH]; char **src; for (i = 0;i < HEIGHT*WIDTH; i++) { Input[i] = (i % WIDTH) + 0x61; // 一次元配列の a..p の文字を繰り返しいれる } src = malloc(sizeof(void *) * HEIGHT); // 根っこの領域を確保しポインターをセット for (i = 0; i < HEIGHT; i++) { src[i] = Input + WIDTH * i; // 折り返す部分のポインターをセット } for (i = 0; i < HEIGHT; i++) { for (j = 0; j < WIDTH; j++) { printf("%c",src[i][j]); } printf("\n"); } }

noconan
質問者

補足

回答ありがとうございました。 なるほど・・・・ポインターの先頭アドレス部部のみ最初に確保し、それぞれの先頭アドレス部分に、入力画像の各行の先頭アドレスを与える方法ですか・・・・・・ 正直考え付きませんでしたw 目から鱗です。 たしかにこの方法なら入力画像を格納した配列のアドレスをそのまま渡せるので、2次元配列として処理した結果をダイレクトに伝えることができますね。 ありがとうございました。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.1

MinGWやDigital MarsのようなC99対応の処理系であれば、 void func(int m, int n, unsigned char array[m][n]) {  int value = array[1][2];  ... } のようにするのが一番簡単です。 SDKといわれても何のSDKか断定できませんし(GlobalAllocとか出てきているので多分Windowsでしょうが)、コンパイラもわかりませんので...。

noconan
質問者

お礼

回答ありがとうございました。 説明不足で申し訳なかったです。 開発環境は Visual Studio 2005.net です。

関連するQ&A

専門家に質問してみよう