質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

4344閲覧

Pタイル法でヒストグラムを作り二値化

seriko

総合スコア29

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2020/06/16 08:02

C++です。Pタイル法で画像を二値化します。(P=10%)
対象画像の輝度ヒストグラムは求めることができたと思うのですが、ここからどう書けばいいのかわからなくなってしましました。何かご教示いただければ幸いです。

c++

1#include<opencv2/opencv.hpp> 2 3#define FILE_NAME "../Debug/a_img.jpg" 4 5//ウィンドウ名 6#define WINDOW_NAME_INPUT "input" 7#define WINDOW_NAME_OUTPUT "output" 8 9int main(int argc, const char * argv[]){ 10 int i,hist[256],x,y; 11 12 //画像をグレースケールで入力 13 cv::Mat src_img=cv::imread(FILE_NAME,0); 14 if(src_img.empty()){//ファイルが開けなかった場合 15 fprintf(stderr,"File is not opened\n"); 16 return(-1); 17 } 18 19 //ヒストグラム用の配列の初期化 20 for(i=0;i<256;i++){ 21 hist[i]=0; 22 } 23 //出力画像のメモリ確保 24 cv::Mat dst_img = cv::Mat(src_img.size(),CV_8UC1); 25 26 //線形変換(入力画像、出力画像) 27 for(y=0;y<src_img.rows;y++){ 28 for(x=0;x<src_img.cols;x++){ 29 //画素値の取得 30 unsigned char s = src_img.at<unsigned char>(y,x); 31 hist[(int)s]++;//画素値の計算 32 33 34} 35 } 36 37 //出力 38 for(i=0;i<256;i++){ 39 printf("%d\n",hist[i]); 40 } 41 42 //画像表示 43 cv::imshow(WINDOW_NAME_INPUT,src_img); 44 cv::imshow(WINDOW_NAME_OUTPUT,dst_img); 45 cv::waitKey(); 46 47 return 0; 48 49 } 50

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

「Pタイル法」自体でググるなりすれば,相応に丁寧な説明が見つかると思うのですが……

P=10%

とのことなので,画像の全画素数の10%がいくつになるかを計算しておきます.
int nP = (全画素数の10%);

で,hist[]の要素を順に足していったときに,どこまで足すとnP以上になるのか? を調べます.
例えば,hist[0]~hist[T]までの総和がnP以上となったならば,Tを二値化の閾値とします.


取り組み方針の案:

(1)まず,以下のような2つの関数を作り,それぞれが妥当に動作することを適当なテストデータを用いる等して十分に確認する.

//Pタイル法により,2値化用の閾値を決定する unsigned char Decide_Threshold_by_PercentileMethod( const cv::Mat &SrcGrayImg, //入力画像.グレースケール(8bit1ch)であること int Percentage //Pタイル法で使うパーセンテージの値(※別にこの形でなくとも,例えば「0.0~1.0の範囲のfloat型」とかでもよい.やりやすい形で.) ) { //入力画像のサイズと第2引数とから,nPの値を決めて… //入力画像のヒストグラムhist[]をつくり… //hist[]とnPを用いて敷地を決めて,その値をreturnで返す } //画像の2値化処理 void Binarize( const cv::Mat &SrcGrayImg, //入力画像.グレースケール(8bit1ch)であること cv::Mat &DstImg, //結果出力先.入力画像と同フォーマット,同サイズであること unsigned char Threshold //2値化閾値 ) { //DstImgの画素値を適切な値にする }

(2)上記の2種の機能実装が完成したら,組み合わせて用いてみる.

投稿2020/06/16 08:57

編集2020/06/17 04:49
fana

総合スコア11996

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

seriko

2020/06/16 09:38

回答ありがとうございました。回答を元にプログラムを修正してみたのですが、実行するとdst_imgが真っ黒になってしまいました。どこが間違っているのか教えてくだされば嬉しいです。 #include<opencv2/opencv.hpp> #include <iostream> #define FILE_NAME "../Debug/papersample1.jpg" #define COLOR_MAX 256 #define COLOR_MIN 0 //ウィンドウ名 #define WINDOW_NAME_INPUT "input" #define WINDOW_NAME_OUTPUT "output" int main(int argc, const char * argv[]){ int i,hist[256],x,y,sum=0,nP; //画像をグレースケールで入力 cv::Mat src_img=cv::imread(FILE_NAME,0); if(src_img.empty()){//ファイルが開けなかった場合 fprintf(stderr,"File is not opened\n"); return(-1); } //ヒストグラム用の配列の初期化 for(i=0;i<256;i++){ hist[i]=0; } //出力画像のメモリ確保 cv::Mat dst_img = cv::Mat(src_img.size(),CV_8UC1); nP=(src_img.rows*src_img.cols)*0.1;//全画素数の10% //線形変換(入力画像、出力画像) for(y=0;y<src_img.rows;y++){ for(x=0;x<src_img.cols;x++){ //画素値の取得 unsigned char s = src_img.at<unsigned char>(y,x); hist[(int)s]++;//画素値の計算 sum=sum+s;//要素の総和 if(sum>nP){ dst_img.at<unsigned char>(y,x)=COLOR_MIN; }else{ dst_img.at<unsigned char>(y,x)=COLOR_MAX-1; } } } //出力 for(i=0;i<256;i++){ printf("%d\n",hist[i]); } //画像表示 cv::imshow(WINDOW_NAME_INPUT,src_img); cv::imshow(WINDOW_NAME_OUTPUT,dst_img); cv::waitKey(); return 0; }
fana

2020/06/16 10:55

落ち着いてください.単一の2重ループ内で全てをやるのは無理です. 1. hist[]の値を求める処理 2. hist[]の値から,2値化の閾値Tを求める処理 3. 閾値Tを用いた2値化処理 の記述を完全に分けてください.きっと,それぞれがループ処理になるでしょう. (そして,上記の順に実行される形に処理を並べてください.) 1.に関しては,元々のコードに存在していたので(その時点で妥当だったのであれば)そのまま使えばよいでしょう. 元々の質問コードにおいて,「//出力」と「//画像表示」の間あたりに,2.と3.を追加する形とすればよいかと思います.
seriko

2020/06/16 11:22

コメントありがとうございます。さらにプログラムを書き直してみたのですがまだうまくいきません。僕的には2のhist[]の値から二値化の閾値Tを求める処理が違うのかなと思っているのですが・・・ 何回も聞いてしまって申し訳ないです。 //1. hist[]の値を求める処理 for(y=0;y<src_img.rows;y++){ for(x=0;x<src_img.cols;x++){ //画素値の取得 unsigned char s = src_img.at<unsigned char>(y,x); hist[(int)s]++;//画素値の計算 } } //出力 for(i=0;i<256;i++){ printf("%d\n",hist[i]); } //2.hist[]の値から二値化の闘値Tを求める処理 for(i=0;i<256;i++){ sum=sum+hist[i];//総和 if(sum>nP){ T=hist[i];//闘値Tを求める } } //3.閾値Tを用いた2値化処理 for(y=0;y<src_img.rows;y++){ for(x=0;x<src_img.cols;x++){ if(T>nP){ dst_img.at<unsigned char>(y,x)=COLOR_MIN; }else{ dst_img.at<unsigned char>(y,x)=COLOR_MAX-1; } } }
fana

2020/06/16 11:46

値が怪しいと疑うなばらmTの値がいくつになっているのかを,例えば3.の直前で表示して確認する等してみてください. ぱっと見: おそらく255になっているのではないでしょうか.もしそうであるならば, if(sum>nP) の条件を満たしてTが定まった時点で,ループを抜ける(break)と良いかも,という感じです.
seriko

2020/06/16 12:44

nP(全画素数の10%)とT(闘値)を表示させてみた結果、nPが69697でTが718でした。これでは確かにT>nPという式は一生成り立ちませんね・・・ nPはnP=(src_img.rows*src_img.cols)*0.1という式なのですがこれがあっているのか疑問に思えてきました。全画素数の10%の求め方はこれであっているのでしょうか?また、他に怪しいところはありますでしょうか?
fana

2020/06/17 00:55

> T=hist[i]; T = i; では.
seriko

2020/06/17 03:25

度々すみません。確かに、T=i;でしたね、失礼しました。主に2と3のところを修正したのですが画像が真っ白になってしまいました。何度も申し訳ないのですがまた間違っているところを教えてくだされば嬉しいです。 //2.hist[]の値から二値化の闘値Tを求める処理 for(i=0;i<256;i++){ sum=sum+hist[i];//総和 if(sum>nP) break; } T=I; //3.閾値Tを用いた2値化処理 for(y=0;y<src_img.rows;y++){ for(x=0;x<src_img.cols;x++){ for(i=0;i<=256;i++){ if(T>i){ dst_img.at<unsigned char>(y,x)=COLOR_MIN; }else{ dst_img.at<unsigned char>(y,x)=COLOR_MAX-1; } } } }
fana

2020/06/17 04:15

もはやただのデバッグ作業依頼みたくなっているので,そろそろ打ち止めにしておきますが, とりあえず,【2値化処理を実装して,それが任意に与えた閾値で妥当に動くことを確認してください】. 本件は閾値を動的に決める話ですが,それにトライするのは事前に上記(2値化の実装)ができてからであるべきです. ここまでのコードを拝見した感じ, 「まず処理内容の理屈を正確に理解して,次に,その通りに実装してください.」というのが,今,最も必要な助言であろうかと思います. ”2値化処理”とは,何と何を比較してその結果に応じて何をすることなのか? 少なくとも for(i=0;i<=256;i++){ if(T>i)... みたいなのが2値化処理に入ってくる余地は無いハズです.
fana

2020/06/17 04:49 編集

(私が何か怒っているとか,嫌になったとか,飽きたとかではなく,単に「この場所というのは,マンツーマンで何かを延々とやるべき場所ではない」ということです.) とりあえず方針の案だけ追記しておきました.
seriko

2020/06/17 05:47

もう一度修正し直した結果、プログラムが理想通りに動きました。本当に何度もありがとうございました。
fana

2020/06/17 06:34

学習のために処理を自作しているのだろうと想定していたので,挙げませんでしたが,2値化処理に関してはOpenCVのcv::threshold()という関数が存在します.以降はこれを使えば良いかと.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問