OpenCVを使っています。
輪郭抽出のプログラムでうまくいかないところがあり困っています。
もし原因や修正方法をおわかりの方がいらしたらぜひアドバイスいただけると助かります。
11×11ピクセルの画像(CV_8UC3)に、一本 上から下に伸びた輪郭線があります。
この輪郭線を境に白黒に塗りつぶしています。
左上から右へ走査し
「もしピクセルが白なら黒にする」
として
処理された画像は輪郭線で白黒に塗り分けることはできました。
このとき、輪郭線の「頂点」を保存したいのですが、うまくいきません。
頂点を表示させると以下のようになってしまいます。
0,0
0,1
0,2
0,3
:
0,10
どこを直すとうまくいくでしょうか。
=========
while (k < 11){ // k = y軸
tate[k] = k; // y方向
cb = lap_img->imageData[lap_img->widthStep * k + j * 3];
while(j < 11 && cb == -1){ // j = x軸
lap_img->imageData[lap_img->widthStep * k + j * 3] = 0;
lap_img->imageData[lap_img->widthStep * k + j * 3 + 1] = 0;
lap_img->imageData[lap_img->widthStep * k + j * 3 + 2] = 0;
lap_img->imageData[lap_img->widthStep * k + j * 3 + 3] = 0;
lap_img->imageData[lap_img->widthStep * k + j * 3 + 4] = 0;
yoko[k] = j; // x方向
if (top == j || top < j){
top =j;
}
j = j + 1;
cb = lap_img->imageData[lap_img->widthStep * k + j * 3];
printf("%d, %d\n", tate[k],top);
j = 0;
k = k + 1;
}
}
No.1
- 回答日時:
そもそも、何をどうしたいのかが、プログラムから読み取るのが困難です。
・ while(j < 11 && cb == -1){ // j = x軸
のループの中で
j = 0;
k = k + 1;
を実行しています。つまり、
lap_img->imageData[lap_img->widthStep * k + j * 3] = 0;
のjは常に0であり、「左上から右へ走査し」ているつもりが、一番左を縦に走査しています。
ただ、それだと
printf("%d, %d\n", tate[k],top);
でtopの値が変化する理由がわかりません。
本当にこのプログラムの結果なのでしょうか?
質問する際に、実際のプログラムをコピーして貼り付けていますか?
結果、またはプログラムをここに書き込む際に間違えていませんか?
あとは、今回とは関係無いかもしれませんが。
○ 変数cbの型は?
IPLImageのimageDataはchar * 型であり、char が符号付き整数になっていることがよくあります。
そのため
cb = lap_img->imageData[lap_img->widthStep * k + j * 3];
だと、画素の値が255 だと cb = -1 になったりします。
0か255かの判定くらいならまだいいのですが、 画素Aと画素Bのどちらが明るいか、とかの場合には、判定が難しくなります。
( 0と64では64の方が明るいですが、 64 と -1では-1の方が明るい、ということになります)
cbをunsignd charとか、int等の整数にして、255までの正の整数が扱えるようにして
cb =(uchar)( lap_img->imageData[lap_img->widthStep * k + j * 3] );
等と、符号無しcharに型変換するとかしましょう。
○次の処理
lap_img->imageData[lap_img->widthStep * k + j * 3 + 3] = 0;
lap_img->imageData[lap_img->widthStep * k + j * 3 + 4] = 0;
は
lap_img->imageData[lap_img->widthStep * k + (j + 1) * 3 ] = 0;
lap_img->imageData[lap_img->widthStep * k + (j + 1) * 3 + 1] = 0;
と同じであり、x座標 j+1の画素のうち、2つのチャンネルを0にしています。
これは意図した操作なのでしょうか?
このままだと、 j=10 のときに X座標11という範囲外にアクセスします。
C言語の仕様で、範囲外にアクセスすること自体は可能ですが、その結果どうなるかは保証されていません。
○同じく
j = j + 1;
の後で
cb = lap_img->imageData[lap_img->widthStep * k + j * 3];
をしていますが、 j=10のループのときは j=j+1でj=11になり、そのj=11を使って座標を求めようとします。
これも範囲外です。
○このプログラムだと、y座標kの一番左が白でないとその横一列はなにもしないことになります。
それでいいのでしょうか?
○変数topが初期化されていないようですが、それでいいのですか?
例えば
k=0のとき top=6 だと
k=1の境界線が4でもtop=6のままです。
○この例ならwhileではなくforの方がいいのでは?
○ (top == j || top < j) は (top<=j)とも書けます。というより、普通はそうします。
kmeeさま
いつもとても迅速に丁寧なアドバイスをくださいまして、ありがとうございます。
プログラムの処理経過のスクリーンショットを添付致しますので
ご参照いただけますでしょうか。
なお、コードは以下のように修正致しました。
====
tate[11] = { 0};
yoko[11] = { 0};
if (ue == 1 && shita == 1)
{
top = 0;
while (k < 11){ // k = y軸
tate[k] = k; // y方向
cb = lap_img->imageData[lap_img->widthStep * k + j * 3];
while(j < 11 && cb == -1){ // j = x軸
lap_img->imageData[lap_img->widthStep * k + j * 3] = 0;
lap_img->imageData[lap_img->widthStep * k + j * 3 + 1] = 0;
lap_img->imageData[lap_img->widthStep * k + j * 3 + 2] = 0;
if (j < 11){
lap_img->imageData[lap_img->widthStep * k + (j + 1) * 3] = 0;
lap_img->imageData[lap_img->widthStep * k + (j + 1) * 3 + 1] = 0;
}
yoko[j] = j; // x方向
if (top <= j){
top =j;
}
j = j++;
cb = lap_img->imageData[lap_img->widthStep * k + j * 3];
printf("%d, %d\n", k, j);
//printf("%d\n", k);
//printf("%d\n", j);
}
cvShowImage("Shirokuro", lap_img);
cvWaitKey(0);
k =k ++;
j = 0;
}
}
=====
No.2ベストアンサー
- 回答日時:
画像は小さくて、全然わかりません。
lap_img->imageData[lap_img->widthStep * k + (j + 1) * 3 ] = 0;
を直したようですが、そもそもこれは何のために入れているのですか?
範囲外になる、という他にも、ループの最後にある
cb = lap_img->imageData[lap_img->widthStep * k + j * 3];
は、 その前でj++された状態なので、
lap_img->imageData[lap_img->widthStep * k + (j + 1) * 3 ] = 0;
で0にした場所です。ということは、cbは必ず0になります。
つまり
j=0、cb=-1でループに入る
x座標0 を0にする
x座標1 も0にする
j++
x座標j(つまりx座標1) の値をcbに代入する。(つまり、cb=0)
ループ終了
となります
j = j++;
これ、意味わかってますか?
たまたま期待通りに動いているかもしれませんが、それはたまたまであって、常にそうなるわけではありません。
j ++ のように、++を後に付けると
j の値を使う → j=j+1 にする
と動作します。
j0 = j ++
なら
oldJ = j ;
j0 = oldJ ;
j = j + 1 ;
と等価です。
では、 j= j++ がどうなるか、というと、決まっていません。
j= oldJ
と
j=j+1
が、どちらが先に実行されるか、C言語としては決まっていないのです。
oldJ = j ;
j = oldJ ;
j = j + 1 ;
で実行されれば、 j は一つ増えますが
oldJ = j ;
j = j + 1 ;
j = oldJ ;
だったら、jは前のままです。
単に一つ増やしたいだけなら、 j++ だけ書きましょう。
で、前回も書きましたが、 jを0にしてから始めるのですから、素直に
for( j=0; j < 11 && cb == -1; j ++ )
にして、ループ後のj++を抜いた方がいいのではないでしょうか?
さらに
while(j < 11 && cb == -1){ // j = x軸
となっているのを
for( j=0; j < 11; j ++ ) {
cb = lap_img->imageData[lap_img->widthStep * k + j * 3];
if ( cb != -1 ) { break ; }
とすれば、ループ終わりのcb=~が不要になります。
kmeeさま
2度も長い丁寧なコメントをくださいまして、本当にありがとうございました!
間違いがわかり、ようやく、ようやくまともに動きました。
もちろん、ピクセルの座標もまともに取得できるようになりました。
ひとつひとつどこがどうおかしいか、自分で自覚して確認できるアドバイスを
くださいまして、本当にありがとうございました。
もう、毎回ですけれども、本当に感謝しています!
ありがとうございました!!!
No.3
- 回答日時:
いちおうねんのため:
j=j++;
でどうなるかは確かに #2 でいわれているように「決まっていない」のですが, 正確にいうと
未定義動作である
と決まっています. 「未定義動作」ですから, 何が起きても規格上は全く問題ありません. たとえプログラムを実行している途中で突然鼻歌を歌い出したとしても文句を言ってはいけないのです.
そうなのですね、知りませんでした。
鼻歌を歌いださないように気を付けます(笑)
いろいろとわかっていなくて、アドバイスいただけるととても助かります。
ありがとうございました!
No.4
- 回答日時:
追記します。
まず、やろうとしていることを、日本語(またはあなたが普段使う母国語)で書いてみましょう。
最初はおおざっぱでいいです。
次に「おおざっぱ」の各項目で何をするのか、より細かく書きます。
さらに細かく... と続けて、機械的にC言語の命令に置き換えられる、というくらいまでになったら、C言語に「翻訳」してください。
この設計がちゃんとできていないから、プログラムをどう直していいかもわからないのでは?
うまく動作しないときも、設計図通りに動作していてるなら直すべきは設計図、設計図通りに動作していないなら直すべきはプログラム、と修正箇所の切り分けができます。
この設計図が無ければ、「明らかにおかしい」という箇所しか指摘できません。
指摘できても、修正方法まで指摘できないこともあります。
・j=j++ は「明らかにおかしい」し、意図を推測できたので、修正方法も指摘できました
・+ j * 3 + 3 は範囲外になるという点では「明らかにおかしい」ですが、これが何を意図したものかが理解できないので、修正方法は指摘できません。
・top変数の扱いは、top変数が何を意図したものかがわからないので、指摘も修正もできません。
画像全体で境界線の最も右の座標、なら、このプログラムの通りになりますが
注目y座標での境界線の座標、なら、jのループの前に初期化する必要があります。
コメントが少ないのも、わかりずらい理由の一つです。
先のtopについても、コメントで説明があれば解決します
例えば、設計図からプログラムへは次のような流れになります
輪郭の座標を抽出する
↓
左上から走査して、白→黒になった座標を抽出する
↓
「左上から走査」とは
「左から右へ走査」を上から下まで順番に行う
↓
「上から下まで」とは y座標を0から最大y座標(今回は10)まで繰り返すこと
↓
for( k=0 ; k < 11 ; k ++ ) {
「左から右へ走査して変化点を探す」
}
はい、わかりました。
次回からもっとコメントを多く書いておおまかな流れを誰が見てもわかるように残すようにします。
足りないところを補足して考えてくださいまして、ありがとうございました。
毎回 貴重なアドバイスをありがとうございます。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- JavaScript 画像の表示位置 3 2022/12/23 08:25
- その他(プログラミング・Web制作) pythonで、tkinterとpillowの組み合わせ 2 2022/08/16 17:42
- JavaScript アップロードファイルの種類によって処理を分岐させたいのですが書き方が分からずアドバイスお願いします 4 2023/06/17 19:12
- JavaScript jQueryで同じクラス名のものを別物として扱いたい 1 2022/06/17 14:14
- Perl RSSにdiv,ul classを付けたいのですがどのようにつけるのかわからないです 2 2022/03/28 01:53
- その他(開発・運用・管理) フォルダの中にファルダを作成してファイルを格納するバッチコマンド 1 2022/06/30 11:39
- HTML・CSS CSSのホバーエフェクト 1 2023/06/19 06:53
- Perl 画像が表示でnull; this.src 1 2022/04/19 11:31
- HTML・CSS img と p を縦中央に配置したいのですがうまくいきません。 2 2023/01/12 14:38
- JavaScript jQueryでのドラッグアンドドロップについて 1 2022/07/30 09:10
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
グラフの交点の求め方(Excel)
-
c言語でキーボードから2点の座...
-
エクセルである点からの距離で...
-
ガウシアンフィルタのCプログラム
-
C言語 配列で座標
-
交差する2線分の交点座標の求め方
-
エクセルで回転する座標の出し方
-
C言語について質問です 画像の...
-
緊急 ベーシックで国旗の作り方...
-
【C#】アクションゲームの地...
-
マインクラフト(pc版)で座標...
-
Cで回転プログラムの高速化を
-
位置座標からx軸となす角度(ラ...
-
MATLABの画像処理、2本の直線の...
-
回転する矩形同士の当たり判定...
-
最小二乗平面
-
画像の円形内による画素検出に...
-
Excel VBA ・・・教えてください
-
虚数は我々日常生活の身近なと...
-
ピクチャボックスの座標取得
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
グラフの交点の求め方(Excel)
-
マインクラフト(pc版)で座標...
-
エクセルで回転する座標の出し方
-
エクセルである点からの距離で...
-
3次元空間上の2つの座標から...
-
閉図形の座標の配列が右回りか...
-
ダイアログ内コントロールの位...
-
始点、終点の二つの座標と半径...
-
座標を持った平面範囲に座標を...
-
空間上の二点を結ぶ直線上に任...
-
エクセルシート上のマウスポイ...
-
多角形の内部かどうか判定する方法
-
ワード上Shapeの位置情報を統一...
-
Excel VBA で自在に図形を変化...
-
OpenCvSharp4による画像判定解...
-
C言語 配列で座標
-
シーケンサー(PLC?)で制...
-
以下のプログラムは重心を求め...
-
交差する2線分の交点座標の求め方
-
VB6のPrinter.ScaleWidth に対...
おすすめ情報