• ベストアンサー

OpenCVによる4値化について

OpenCVを使って画像を4値化したいのですが、アルゴリズムはわかっているのですがどうプログラムを組めばいいのかわかりません。 アルゴリズムはまず濃度ヒストグラムを作って (1)適当な閾値を3つ(t1,t2,t3),決める。 (2)4つの領域で平均濃度を計算し、それらをa1,a2,a3,a4とする。(4つの領域ってどこ? (3)新閾値t1,t2,t3とt1=(a1+a2)/2,t2=(a2+a3)/2,t3=(a3+a4)/2として計算する。 (4)Σ(j=1,3) |tj-tj|<εならば停止。そうでなければ(2)に戻る。 なのですが、教えて下さい。

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

  • ベストアンサー
回答No.2

 こんにちは。  数学としてのN値化は分からないのですが、画像処理としてのN値化でお話しすると、  (1)RGB[0-255, 0-255, 0-255]の画素をY[0-255]のグレースケールに変換  (2)Y[0-255]に対して、[N-1]個の閾値を境界線として0~[N-1]を出力する  が多いように思います。  (2)は、例えば2値化の場合、1個の閾値を境界線にして、画像フォーマットが1ビットであれば、0~1を出力します。24ビットカラーなどであれば、RGB(0,0,0)又はRGB(255,255,255)を出力します。  此れを4値化で考えると、3個の閾値で0~3を出力しますが、フォーマットが1ビットでは入りきらない為、4ビット以上の画像フォーマットにセーブすると言う事です(大抵24ビットを使用する)。  0→RGB(0,0,0)  1→RGB(85,85,85)  2→RGB(170,170,170)  3→RGB(255,255,255)  アラも在るのですが、以下参考になれば。 //配列の個数を数える #define ArrayCount(a) (sizeof(a)/sizeof(a[0])) //グレースケール変換[0-255]を出力する int RGBToY(int R, int G, int B) { return ((54 * R) + (183 * G) + (18 * B)) >> 8; } //閾値と比較して[0~count]を決定する int GetLevel(int color, const int array[], int count) { for(int i = 0; i < count; ++i) if(color < array[i]) break; return i; } //テスト void test() { //IPLに読み込む(24ビットカラー) IplImage* img = ::cvLoadImage("testimage.bmp", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR); //閾値の配列 const int thresholds[] = {64, 128, 192}; //閾値の数 const int count = ArrayCount(thresholds); //count x band = 255にする為 const int band = 255 / count; for(int y = 0; y < img->height; y++) { for(int x = 0; x < img->width; x++) { const int pos = (y * img->widthStep) + (x * img->nChannels); const int R = (unsigned char)img->imageData[pos]; const int G = (unsigned char)img->imageData[pos + 1]; const int B = (unsigned char)img->imageData[pos + 2]; const int Y = ::RGBToY(R, G, B); const int result = ::GetLevel(Y, thresholds, count) * band; for(int i = 0; i < img->nChannels; ++i) img->imageData[pos + i] = result; } } //4値化した結果を24ビットカラーでセーブする ::cvSaveImage("yontika.bmp", img); //IPLの解放 ::cvReleaseImage(&img); }

avenew
質問者

補足

プログラムしてみたところできました! 1つ質問ですが、この部分がどういう処理なのか教えていただけますでしょうか。 for(int y = 0; y < img->height; y++) { for(int x = 0; x < img->width; x++) { const int pos = (y * img->widthStep) + (x * img->nChannels); const int R = (unsigned char)img->imageData[pos]; const int G = (unsigned char)img->imageData[pos + 1]; const int B = (unsigned char)img->imageData[pos + 2]; const int Y = ::RGBToY(R, G, B); const int result = ::GetLevel(Y, thresholds, count) * band; for(int i = 0; i < img->nChannels; ++i) img->imageData[pos + i] = result; } }

その他の回答 (3)

回答No.4

 こんにちは。補足頂きました。  グレースケールの公式のつもりだったのですが、一般的ではないのかもしれません。  以下を使用しています(LibPNGと言う、PNG画像を処理する有名なライブラリのヘルプドキュメント内に紹介されているグレースケールの公式です)。  http://dencha.ojaru.jp/programs_07/pg_graphic_10_libpng_txt.html  出力画素 = 0.212671 * R成分 + 0.715160 * G成分 + 0.072169 * B成分  グレースケールの公式や手段に関しては、其の他にも幾つか有る様です。  http://ofo.jp/osakana/cgtips/grayscale.phtml  話を戻すと、以下の様な関数に成ります。  int RGBToY(int R, int G, int B)  {   return (0.212671 * R) + (0.715160 * G) + (0.072169 * B);  }  小数を含む計算は速度が落ちる為、各係数を256倍して整数だけの計算にしています。  int RGBToY(int R, int G, int B)  {   return ((54 * R) + (183 * G) + (18 * B)) / 256;  }  >> 8は8ビット右にシフトする演算です。/ 256と同じ結果が出せます。  / 256とするよりも>> 8とした方がCPUは早く計算出来るので、  int RGBToY(int R, int G, int B)  {   return ((54 * R) + (183 * G) + (18 * B)) >> 8;  }  としています。  グレースケール変換については、以下が非常に参考になります。  http://cell.fixstars.com/ps3linux/index.php/2.9%E3%80%80%E6%BC%94%E7%BF%92%E5%95%8F%E9%A1%8C_(2-4)_%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%AB%E5%A4%89%E6%8F%9B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0

avenew
質問者

お礼

いろいろ教えていただきありがとうございました! 大変参考になりました!

回答No.3

 こんにちは。補足頂きました。  この手の事は中々簡単に説明出来るモノではないのですが、手っ取り早く解説すると、 //(1) for(int y = 0; y < img->height; y++) { //(2) for(int x = 0; x < img->width; x++) { //(3) const int pos = (y * img->widthStep) + (x * img->nChannels); //(4) const int R = (unsigned char)img->imageData[pos]; //(5) const int G = (unsigned char)img->imageData[pos + 1]; //(6) const int B = (unsigned char)img->imageData[pos + 2]; //(7) const int Y = ::RGBToY(R, G, B); //(8) const int result = ::GetLevel(Y, thresholds, count) * band; //(9) for(int i = 0; i < img->nChannels; ++i) img->imageData[pos + i] = result; } } imgはファイルから読み込んできたカラービットマップを示しています(読み込んでくるファイルは24bitを前提としています)。 (1)imgの縦幅の分だけ回ります (2)imgの横幅の分だけ回ります (3)imgから処理対象の画素の位置を計算しています。img->widthStepは次の行に改行する為に必要な正確なバイト数を示しています。  img->nChannelsは、画素一つ辺りに幾つの色素があるかを示しています。  24bit に限定しているので img->nChannelsは 3 で RGB(赤、緑、青)です(其れ以外は表示が可笑しくなる)。  この場合、次の画素へ移動するには3バイトずつ移動する事になります。 (4)赤の色素を取り出しています[0-255]。pos = 0の時 赤の位置は pos (5)緑の色素を取り出しています[0-255]。pos = 0の時 緑の位置は pos + 1 (6)青の色素を取り出しています[0-255]。pos = 0の時 青の位置は pos + 2 次の画素に移動してpos = 3になる (7)赤、緑、青の色素を使ってグレースケールに変換します[0-255]。 (8)グレースケールされた数字を[0-255]を閾値と比較して[0-3]へ4値化します。  しかし[0-3]ではディスプレイ出来ませんので、band を掛け合わせることで resultが  0→0  1→85  2→170  3→255  に成る様に調整します。ですので、band = 85です。 (9)赤、緑、青のデータに書き込みます(例えばresult = 170であった場合、赤170 緑170 青170 にしている)。  上記の手順を画像が持つ全ての画素に行う事で、4値化されたグレースケールになります。  此れでよろしいでしょうか。

avenew
質問者

補足

ありがとうございます! 大変わかりやすいです。 何度も申し訳ないのですが、もう一つ質問させてください。 //グレースケール変換[0-255]を出力する int RGBToY(int R, int G, int B) { return ((54 * R) + (183 * G) + (18 * B)) >> 8; } の関数で{54,183,18}というのは適当な値と解釈してよろしいでしょうか。 また「>>8」というのはどういう意味なのでしょうか。 よろしくお願いします。

  • chie65535
  • ベストアンサー率43% (8528/19386)
回答No.1

>4つの領域ってどこ? min~t1、t1~t2、t2~t3、t3~maxの4領域。 例えば、最初に0~63、64~127、128~191、192~255の4つの領域に4分し、それぞれの範囲での平均濃度を求める。 それぞれの平均が1、82、130、254だった場合、t1=(1+82)÷2=41、t2=(82+130)÷2=106、t3=(130+254)÷2=192となる。 次に、0~40、41~105、106~191、192~255の4つの領域に4分し、それぞれの範囲での平均濃度を求める。 以下、終了条件に達する(何回繰り返してもt1、t2、t3が3つとも、ある程度以上は変化しなくなる)まで繰り返し。上記の例では、t3が2回目で変化しなくなっている。 但し「ある周期で、いくつかの値を繰り返す」と言う「発振」を起こす可能性があるので、終了判定に注意する事。例えば、t1が「40⇒43⇒40⇒43⇒…」と延々と繰り返す可能性がある。

avenew
質問者

補足

平均濃度はヒストグラムを4つの領域にわけるのでしょうか? また終了条件に達したらその後はどうすればいいのでしょうか?

関連するQ&A

  • OpenCVで平均濃度の求め方

    OpenCVを使って画像の平均濃度を求めたいのですが、どうやるのでしょうか。 0~256レベルで、例えば0~85の間での平均濃度の求め方です。 濃度ヒストグラムと同じなのでしょうか?

  • ヒストグラム平均化のアルゴリズム

    画像をヒストグラム平均化処理するプログラムを 作成したいのですが,具体的なアルゴリズムが わかりません.どなたか,教えてください.

  • ヒストグラム類似度による画像のマッチング処理

    現在、C言語で画像のマッチング処理をして、画像の類似度を調べる必要が出てきました。 そこで、とりあえずヒストグラムの最大値を1として平均化して、ヒストグラム値を取得した二つの画像を用意。 その同じ画素値どうしのヒストグラムの値の差分(絶対値)をとり、それをRGBの1~256まで繰り返して全部合計。 その合計値が一番低かった画像が似た画像である。 という適当なアルゴリズムを作ってみたのですが、上手くいきませんでした。 何か良いアルゴリズムをご存知の方はいないでしょうか? 類似度といっても、ぱっと見の全体的な色が似てさえいればいいので、画像の詳細が似ている必要はありません。 どちらかといえば、処理速度は速いほうがいいです。

  • 画像処理(高精度な輪郭検出)について

    今年新卒で入社し、医療機器関連のエンジニアとしてスタートしたものです。素人ながら質問させていただきたいと思います。 現在、Visual Studio 2010(言語はC#)とOpenCVを用いて、画像中の輪郭、物体をできるだけ高精度に検出するアルゴリズムの研究を行っています。 基本である差分計算やOpenCVの関数を用いたラプラシアンフィルタやCannyアルゴリズム、輪郭線抽出、RGBからHSV,Lab色空間(Labについては現在も調査中です。)へ変換し、閾値を変えてみるなど試してみました。また、PhotoShopを用いて、コントラストや明るさ、色調変換をかけて、同様に試してみたりもしました。 しかし、画像によっては満足いく輪郭、物体の検出ができない状態です(フリーソフトの輪郭検出器2.10を使ってみても同じでした)。勿論、明るさやレンズの特質等の影響もあるとは思いますが・・・。 そこでお聞きしたいのですが、上記の他にもできるだけ高精度に輪郭や特徴点を検出する手法がありましたら、教えていただける範囲で教えていただけないしょうか。 漠然とした質問で申し訳ありません。どうぞよろしくお願い申し上げます。 今のところ検討中なのは、対象画像に何らかのフィルタをかけて、それから差分や大津の2値化閾値判定を使う、特徴点抽出には、OpenCVのコーナー検出アルゴリズム(Harris等)やSURF等を使うことを考えています。 勿論、上記の事項に応用的に工夫を加える独自のアルゴリズムも検討中です。

  • プログラムを教えてください!

    レポートの質問です。 以下の問題のプログラムを作らなければいけないのですが、いろいろと調べてみてもまったくわかりません。 わかる方プログラム教えてください! 課題1 合同式法を用いて,区間[0,1] (区間[0,1)でも良い) の 一様乱数を100 万個生成し,以下を報告せよ.ただし,一様乱数の生成にはC 言語で自作したプログラムを用いること. (1a) 平均の数値計算結果 (1b) 標準偏差の数値計算結果 (1c) 一様乱数のヒストグラム.ヒストグラムは区間[0,1] を100 分割して 作成すること. 課題2 中心極限定理に基づく「間便法」を用いて,区間[0,1] (区 間[0,1)でも良い) の一様乱数から標準正規分布N(0,1)に従う正規乱数を100 万 個生成し,以下を報告せよ.ただし,正規乱数の生成にはC言語で自作したプ ログラムを用いること. (2a) 平均の数値計算結果 (2b) 標準偏差の数値計算結果 (2c) 正規乱数のヒストグラム.ヒストグラムは区間[-5, 5] を100 分割して 作成すること.

  • OpenCVを用いたヒストグラムの比較について

     現在画像の類似度を求めるために、OpenCV1.0を使って色のヒストグラムの距離を求めるということを行っております。OpenCVで公開されているヒストグラム間の距離のサンプルプログラムを使って行っているのですが、結果があまり芳しくないためいろいろ調べなおしたところ、減色処理、量子化というワードにたどり着きました。  しかし調べてみてもいまいち分からず、行き詰っているのが現状です。どなたか減色処理や量子化について詳しい人がおりましたら、是非回答のほうよろしくお願いします。以下がOpenCVのコードなのですが、どこの部分をどのように変えたらそれが実現できるのかも是非回答お願いいたします。 #include <cv.h> #include <highgui.h> #include <math.h> #include <stdio.h> int main (int argc, char **argv) { char text[16]; int i, hist_size = 256, sch = 0; float range_0[] = { 0, 256 }; float *ranges[] = { range_0 }; double tmp, dist = 0; IplImage *src_img1 = 0, *src_img2 = 0, *dst_img1[4] = { 0, 0, 0, 0 }, *dst_img2[4] = { 0, 0, 0, 0}; CvHistogram *hist1, *hist2; CvFont font; CvSize text_size; // (1)二枚の画像を読み込む.チャンネル数が等しくない場合は,終了 if (argc >= 3) { src_img1 = cvLoadImage (argv[1], CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR); src_img2 = cvLoadImage (argv[2], CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR); } if (src_img1 == 0 || src_img2 == 0) return -1; if (src_img1->nChannels != src_img2->nChannels) return -1; // (2)入力画像のチャンネル数分の画像領域を確保 sch = src_img1->nChannels; for (i = 0; i < sch; i++) { dst_img1[i] = cvCreateImage (cvSize (src_img1->width, src_img1->height), src_img1->depth, 1); dst_img2[i] = cvCreateImage (cvSize (src_img2->width, src_img2->height), src_img2->depth, 1); } // (3)ヒストグラム構造体を確保 hist1 = cvCreateHist (1, &hist_size, CV_HIST_ARRAY, ranges, 1); hist2 = cvCreateHist (1, &hist_size, CV_HIST_ARRAY, ranges, 1); // (4)入力画像がマルチチャンネルの場合,画像をチャンネル毎に分割 if (sch == 1) { cvCopy (src_img1, dst_img1[0], NULL); cvCopy (src_img2, dst_img2[0], NULL); } else { cvSplit (src_img1, dst_img1[0], dst_img1[1], dst_img1[2], dst_img1[3]); cvSplit (src_img2, dst_img2[0], dst_img2[1], dst_img2[2], dst_img2[3]); } // (5)ヒストグラムを計算,正規化して,距離を求める for (i = 0; i < sch; i++) { cvCalcHist (&dst_img1[i], hist1, 0, NULL); cvCalcHist (&dst_img2[i], hist2, 0, NULL); cvNormalizeHis

  • フィックの第二法則の刻み時間(フォートラン)

    第二法則について数値解析を行い、 フォートランによって dt=1.0 dx=1e-4 d=2e-12 a=d*dt/(dx)**2 do 300 j=0,3600 c(j,0)=0.0 c(j,20)=2.0 do 400 i=1,19 c(j+1,i)=c(j,i)+a*(c(j,i+1)-2.0*c(j,i)+c(j,i-1)) 400 continue 300 continue として一秒ごとに計算し、一時間後までの各時間、各位置の濃度を求めています。 (jは時間、iは位置を表しています。) このとき、刻み時間t=1として計算しているのですが、これを0.1秒で計算したいとき、 do 300 j=0,3600 を do 300 j=0,3600,0.1 c(j+1,i) は c(j+0.1,i) としなくてはいけないのでしょうか? それとも1のままでよいのでしょうか。 どなたか、どうか教えてください。 ちなみに、上のようにかえてもプログラムが通らないことはわかっています。 聞きたいのは、「刻み時間を変えると濃度計算の中身と計算のステップも変えなくてはいけないのか」ということです。 わかりにくくて申し訳ありません。 どうかお願い致します。

  • OpenCV webカメラの歪み補正

    現在「OpenCV プログラミングブック」で勉強を始めました。 こちらの本です↓ http://www.amazon.co.jp/gp/product/483992354X/ref=s9_simz_gw_s0_p14_i1?pf_rd_m=AN1VRQENFRJN5&pf_rd_s=center-1&pf_rd_r=1FXA1WZ0DZDCWB92J6T9&pf_rd_t=101&pf_rd_p=463376736&pf_rd_i=489986 この本を参考にして魚眼レンズを取り付けたwebカメラの歪み補正をやっています。 本のP287ページのwebカメラの歪みを補正するプログラムを実行させてwebカメラの映像の歪みをとることができました。 しかし、キャリブレーション手法を用いてるので、毎回チェックパターンを撮影しないと歪み補正をすることができません。 どうにかしてチェックパターンを撮影する手間を省いてwebカメラを起動した時に既に歪みがとれている状態にしたいのですがどうすればいいでしょうか? この本を持っている人しかわからないかもしれませんが回答お願いします。 プログラムはこちらのサイトで配布されていたので貼っておきます。 書籍掲載のコード集:cv_prog.zip (2.1MB) というファイルのs411というフォルダに入っています。 http://book.mycom.co.jp/support/bookmook/opencv/ プログラム初心者なので専門用語などわかりやすくしてもらえると助かります。 わかりずらい説明になってしまいましたが、宜しくお願いします。

  • i,j,kは互いに直交する単位ベクトルである。

    i,j,kは互いに直交する単位ベクトルである。 1)a=i+2tj+2t^2k b=2t^2i+tj+k 外積axb=? 2)a=yzi-zxj+xyk 発散diva=? 回転rota=? わかる方いらっしゃれば お願いします(>_<)

  • VBAで特定範囲のセルの平均値を順次出力したい

    VBAで特定範囲のセルの平均値を順次出力したい 前回の質問で誤りがあったため再投稿しました。 図のように各行列に値が出力されており,VBAで下記のように処理をしたいと考えています。 (1) 平均値を出すセルの範囲(例えば50X16)をVBAで指定できるようにし、その範囲の平均値を求める (2) (1)で求めた平均値を新しいSheetのあるセルに出力 (3) 上記の処理をある範囲(プログラムの中で変更)の列方向に適用していき,順次(2)のSheetに平均値を出力していく イメージとしては図のように黄(A2~J10)、青(A11~J20)、赤(A21~J30)の順に列方向の平均値を新たなSheetに出力したいのです。図では黄、青、赤の3領域しかありませんが、ここもプログラムで範囲指定をできるようにし、領域の数がいくらになってもよいようにしたいです。 どなたか上記の処理ができるプログラムを教えてください。よろしくお願いします。