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

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

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

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

C++

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

Q&A

解決済

2回答

6663閲覧

プログラムの処理の過程を理解したいです。

carnage0216

総合スコア194

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

C++

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

0グッド

0クリップ

投稿2018/06/05 13:26

編集2018/06/07 01:36

<実行した環境>

  • Windows10 64bit
  • visual studio 2017
  • opencv 3.4.1

こちらのサイトを参考にしました。http://www.cellstat.net/centroid/
こちらのプログラムに関しての質問です。

#include <stdio.h> #include <opencv/cv.h> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgcodecs/imgcodecs.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/core/core.hpp> using namespace cv; int main(){ Mat img = imread("sample.jpg", IMREAD_GRAYSCALE); Mat img2 =img.clone(); vector<vector<Point> > contours; findContours(img2, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); double max_size=0; int max_id=-1; for(int i=0; i<contours.size();i++){ if(contours[i].size()>max_size){ max_size=contours[i].size(); max_id=i; } } Moments mu = moments( contours[max_id]); Point2f mc = Point2f( mu.m10/mu.m00 , mu.m01/mu.m00 ); circle( img, mc, 4, Scalar(100), 2, 4); printf("x: %f y: %f", mc.x, mc.y); imshow("img",img); waitKey(0); return 0; }

のプログラムに関して

findContours(img2, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); ```のcontoursは検出した輪郭を保存する変数とのことですが、これは変数名がcontoursで何バイトかのメモリを占めているということでしょうか? そして、変数名contoursが占めているメモリに

double max_size=0;
int max_id=-1;

for(int i=0; i<contours.size();i++){ if(contours[i].size()>max_size){ max_size=contours[i].size(); max_id=i; } }
の計算結果の値を代入して、その輪郭の値から面積を求める為に関数Momentsを使った。

Moments mu = moments( contours[max_id]);

最後に関数Momentsにより求めた面積から重心を求めるために

Point2f mc = Point2f( mu.m10/mu.m00 , mu.m01/mu.m00 );

を使ったと考えているのですが、正直以上のような解釈であっているかわかりません。 実行は出来ましたがちゃんとプログラムを理解したいと思い質問しました。 間違っていましたらどうかご指導お願いいたします。 載せましたプログラムで重心を求めた結果の画像を載せます。 一枚目はボールペンです。 ![イメージ説明](532c924a5588c63622d890e0b5e75798.png) 二枚目はペットボトルです。 ![イメージ説明](11c6127d9da67904565ac5f0429bbe08.png) 画像からの重心を求めたので正しいかどうかはわかりませんが、ボールペンに関してはあながち間違ってないように思えます。(実験しました。) 編集 処理を読んでいてもなぜ重心が求まるのか理解できません。 直球に言いますと、なぜ、あのプログラムから重心が得られるのか原理が知りたいです。 三角形や円なら数式を使えば重心を簡単に求められますが、歪なものなどは数式が立てにくく常に一定の形の物の重心を求める訳ではありません。 そのため、数式を使わずに、載せたプログラムだけで様々な形の物の重心が求まる理由が知りたいと思いました。

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

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

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

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

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

guest

回答2

0

ベストアンサー

contoursは検出した輪郭を保存する変数

若干複雑ですが間違いではありません。
vector<vector<Point> > contours;
ですので、Point型の可変長配列の可変長配列になります。
可変長配列ですので、それが何バイトの領域なのかは計算してみないと分かりませんし
すべてが連続した領域という訳でもありません。(C言語でのポインタの配列みたいな感じ)

ちなみに、findContoursのマニュアルによると、OutputArrayOfArrays型となっています。
cv::Matやstd::vector等をコンストラクタで変換する事で同様に扱えるようにしているらしいです。


計算結果の値を代入して

まず、代入という用語について
ある特定の名前(ここではXとおく)を持つ変数に値(とりあえずYとする)を設定(もしくは変更)するときに,
XY代入する」と言います。

ご提示のソースでは、contoursへの設定ないしは変更は発生していないため、contoursへの代入は行われていません。

cpp

1 for(int i=0; i<contours.size();i++){ 2 if(contours[i].size()>max_size){ 3 max_size=contours[i].size(); 4 max_id=i; 5 } 6 }

ここで行われているのは、最大の頂点数を持つ輪郭がどれかを探しています。
結果、最大の頂点数を持つ輪郭はcontours[max_id]になります。


関数Momentsを使った。

まず使う関数はmoments、小文字の方です。

それぞれのドットの輝度を重さとみなし
(グレイスケールなので1・・・らしい、適切な文献見つからず検証不十分)
モーメントを計算します。

高校物理の力のモーメントを抽象化した概念になります。

それぞれの点の重さの総和がm00です。(重さが1ならば点の数になり、更に点が連続的であるならば面積になります。)
それぞれの点の重さにx座標を掛けた値の総和がm10
それぞれの点の重さにy座標を掛けた値の総和がm01
になります。

モーメントを用いた重心の計算方法は公式化されていて
それが、
Xg = m10 / m00
Yg = m01 / m00
なわけです。

投稿2018/06/06 06:30

asm

総合スコア15147

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

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

carnage0216

2018/06/06 09:48

どうもありがとうございます。 読ませていただきます。
carnage0216

2018/06/06 09:58

あの >>モーメントを用いた重心に関して 参考書に重心の計算のプログラムをopnecvを使わずに書いてあるのですが、このプログラムにはモーメントの計算も考慮しているのでしょうか? //重心の計算 int count=0; double x_g=0.0,y_g=0.0,x_d=0.0,y_d=0.0,xy_d=0.0 for(int y=0; y<height; y++){ for(int x=0;x<width; x++){ if(img_src[y*width+x] == 255){ count++; x_g +=x; y_g +=y; } } } x_g /=conut; y_g/=count;
asm

2018/06/06 10:10

同じことですよ ループを抜けた直後の x_g = x * 1 の総和 = 1ピクセルの重さを1とした時のモーメント(m10) y_g = y * 1 の総和 = 1ピクセルの重さを1とした時のモーメント(m01) count = ピクセルの数 = 1ピクセルの重さを1とした時のモーメント(m00) になっています。
carnage0216

2018/06/06 23:31 編集

おはようございます。 というと、こちらのプログラム int count=0; double x_g=0.0,y_g=0.0,x_d=0.0,y_d=0.0,xy_d=0.0 for(int y=0; y<height; y++){ for(int x=0;x<width; x++){ if(img_src[y*width+x] == 255){ count++; x_g +=x; y_g +=y; } } } x_g /=conut; y_g/=count; は質量の計算も考慮しるのでしょうか?重心を求めるには画素の座標と質量が必要と知りました。 個人的には質量がわからなければ重心はわからないのではと思います。 どうやって載せたプログラムだけで重心を求めたのかアルゴリズムが知りたいです。 地頭が足りないこともありx_g = x * 1 の x * 1とy_g = y * 1のy * 1がどこにあるか確認できませんでした。 もしかしたら*1は掛ける1なので x=x * 1、y=y * 1なのならば x_g +=x; y_g +=y; の部分に該当しますね。(Chironianの回答より解決しました。)
carnage0216

2018/06/06 22:57 編集

あの、重心を求めるとopencvでは書いてありましたが、重心を求めたい物体は質量もわからないのに、どうやって二次元の二値化の画像から求めたのでしょうか?何かしらの方法で質量を求めているのでしょうか? 例えば、以上と同じように質量を求められる方法があるならば形が歪な物の二次元の画像を二値化にしても重心は求められると思うのですが。
carnage0216

2018/06/06 22:59 編集

私の方でも確認してみます。どうかよそしくお願いいたします。
carnage0216

2018/06/06 23:09

度々すいません。 for(int i=0; i<contours.size();i++){ if(contours[i].size()>max_size){ max_size=contours[i].size(); max_id=i; } } に関して、最大の頂点数を持つ輪郭がどれかを探しています。とのことですが、最大の頂点数を求める部分をプログラムから消せば単純に輪郭を検出プログラムとして使えるのですよね? すなわち、そのプログラムが単純に輪郭を検出するためのアルゴリズムをプログラムにしたものと言えるのかもしれません。
carnage0216

2018/06/06 23:32

3つの質問ではありますが、どうかよろしくお願いいたします。 もちろん私の方でも調べています。
asm

2018/06/07 00:21

> 質量がわからなければ重心はわからないのでは 「密度・厚さが均一」ならば画素の質量を1とみなしても重心は変わりません。 > 重心を求めたい物体は質量もわからないのに、どうやって二次元の二値化の画像から求めたのでしょうか 求めてはいません。「密度・厚さが均一の平たい板」を想定しています。 > 単純に輪郭を検出プログラムとして使える findContours関数がそれです。複数の輪郭を検出してくれるので、使用する輪郭を決定しているわけです。
carnage0216

2018/06/07 01:40

なるほど、では密度、厚さが一定ならばあらゆる形からでも重心が得られるようにプログラムが組まれているのですね。 しかし、プログラムを見る限り、xy座標を求めて、座標の平均値から重心を求めていますが、そんな密度、厚さが一定ならば、そんな単純なやり方で重心が戻るのですか? もしかしたら、何か勘違いしているかもしれませんが、よろしくお願い致します。
asm

2018/06/07 02:49

単純になるように 「密度・厚さが均一の平たい板」という条件があります。
carnage0216

2018/06/07 08:33

そうだったのですか。 どうもありがとうございます。
guest

0

こんにちは。

これは変数名がcontoursで何バイトかのメモリを占めているということでしょうか?

その通りです。

の計算結果の値を代入して

代入はしていないと思います。コードをよく見てみましょう。

その輪郭の値から面積を求める為に関数Momentsを使った。

「モーメント」は面積ではないと思います。
ただ、ごめんなさい。モーメントについて人に説明できるほど理解していません。

最後に関数Momentsにより求めた面積から重心を求めるために

「モーメント」から重心を求めています。

単なる面積から重心を計算することは出来ないですよ。位置との関係が入った値を使わないと座標を求めることは出来ません。
モーメントは位置✕質量の積分のような値です。(もうちょっと難しいのですが、その辺をよく判っていません。でも実は重心を求めるためには難しい部分は不要だった筈です。)
値0のピクセルを質量0、値1のピクセルを質量1として計算するようなことをやっていると思います。

投稿2018/06/05 18:16

Chironian

総合スコア23272

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

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

carnage0216

2018/06/06 03:33

わかりやすい解説ありがとうございます。 位置とは画素のx,y座標の事でしょうか? そのx,y座標の情報と面積の情報から関数^モーメントが重心を決めるのでしょうか? まぁ、確かに座標がわからないと同じ面積でも丸や四角であるかもしれませんので、面積の情報だけで重心を決められるわけがないですよね。 くどくて申し訳ありません。 集まった画素(の座標)が円の形をしていたため、すなわち、「各座標(各画素)」と「各座標(各画素)により作られた形の面積」によって重心が求まるということでしょうか?
Chironian

2018/06/06 07:39

> 位置とは画素のx,y座標の事でしょうか? その通りです。 > そのx,y座標の情報と面積の情報から関数^モーメントが重心を決めるのでしょうか? 「関数^モーメント」の意味は分かりませんが、面積は無関係と思いますよ。 > によって重心が求まるということでしょうか? ごめんなさい。おっしゃっている意味を理解できません。 でも、繰り返しますが、面積は無関係と思います。モーメントの式を見ても分かるように面積に該当するような値は出てこないと思います。 今回の場合、モーメントの計算式のi, jは0と1だけを使っていますから、展開すると至極簡単な式になります。これを理解してみると良いと思いますよ。 ところで、「重心」は質量の中心です。 http://wakariyasui.sakura.ne.jp/p/mech/gou/jyuusinn.html 本質的に質量が存在しない画像に重心なんてものは存在しません。特定の条件(1画素=1単位質量)を仮定した時の重心=図形の重心としているケースが多いと思います。 リンクされているサイトの方は、グレイスケール画像を使っていますから1画素=1質量と仮定しているかどうかは不明です。(画像は2値化されているっぽいので近いと思いますが、jpeg圧縮しているからよく分かりません。)
carnage0216

2018/06/06 08:08

面積を扱っているとばかり考えていました。 どうもありがとうございます。 じっくり読ませていただきます。
carnage0216

2018/06/06 10:00 編集

>>最後に関数Momentsにより求めた面積から重心を求めるために 「モーメント」から重心を求めています。 >>面積は無関係と思います。モーメントの式を見ても分かるように面積に該当するような値は出てこないと思います。 ありがとうございます。 ちなみに、「モーメント」から重心を求めています。とは、すなわち重心の計算しているということを言っているのですか?(間違っていないか心配です‥。) えーと、確認なのですが、ハフ変換によって円を検出してとします。 その検出された円の重心を知るためにモーメントを使って求めるということでしょうか? 物覚えが悪くてすいません。でも必ず理解したいです。 あの気になったのですが、重心を知るのにモーメントを使う理由がいまいちわかりません。 私の参考にしているopencv本家の本には重心を求めるプログラムがOpencvを使わないcのみで書いてありました。何が言いたいかと申しますかというと、その重心を求めるプログラムがあればモーメントを求める計算部分はいらないのではないかと思っています。(なにか大きな勘違いをしているかもしれません。) もしかしたらモーメント(力)を掛けることで物体の重心がわかるため、重心を求めるプログラム中のどこかにモーメントの計算する部分があるかもしれません。 こちらが重心を求めるプログラムです。 //重心の計算 int count=0; double x_g=0.0,y_g=0.0,x_d=0.0,y_d=0.0,xy_d=0.0 for(int y=0; y<height; y++){ for(int x=0;x<width; x++){ if(img_src[y*width+x] == 255){ count++; x_g +=x; y_g +=y; } } } x_g /=conut; y_g/=count;
Chironian

2018/06/06 11:02

同じ式でも、それを計算するプログラムはいくらでもあります。 OpenCVのmoment関数を使うのも手ですし、ご提示されているように自力で計算するのもありです。 因みに、見かけは全く異なるように見えて実はほぼ同じ式になってます。 (m00=count,m10=x_g,m01=y_g) ほぼ同じ式なのか、全く異なる式で計算しているのか程度はご自身で確認された方が良いと思いますよ。それほど難しい内容ではありませんし。
carnage0216

2018/06/06 20:52

おはようございます。 どうもありがとうございます。 はい。確認してみたいと思います。 個人的には重心はモーメントを使うやり方もあれば、そうでない求め方もできるようなので、確認してみます。
carnage0216

2018/06/06 22:17 編集

ありがとうございました。読み直します。 改めて、 >>ところで、「重心」は質量の中心です。 http://wakariyasui.sakura.ne.jp/p/mech/gou/jyuusinn.html 本質的に質量が存在しない画像に重心なんてものは存在しません。特定の条件(1画素=1単位質量)を仮定した時の重心=図形の重心としているケースが多いと思います。 リンクされているサイトの方は、グレイスケール画像を使っていますから1画素=1質量と仮定しているかどうかは不明です。(画像は2値化されているっぽいので近いと思いますが、jpeg圧縮しているからよく分かりません。) に関しては大変参考になります。あの、もしよろしければどうやって以上の情報を探したのか方法を教えてもらえないでしょうか?
carnage0216

2018/06/06 22:39 編集

また、moments関数に関しては「特定の条件(1画素=1単位質量)を仮定した時の重心=図形の重心としている」の原理を利用して、moments関数で物体の重心を導いているのでしょうね。 もう一つ質問なのですが、1画素はわかるのですが、1単位質量とはどのような意味でしょうか? 1画素=1g(グレースケールにおいて)ということでしょうか。正直、グレースケールにしただけで重心が求まる理由はわかっていません。定義(1画素=1質量)などしているのかもしれませんがそんな単純な物で物体の重心がなぜわかるのか不思議です。 個人的には1画素RGB24bitとしてやったほうがいいような気がしましたが、色によって質量が変わるのはおかしな話なのでグレースケールにしたほうがいいのかもしれません。 もしかしたら、これに関しては輪郭から重心を求めたプログラムを読み解けば、グレースケールからでも厳密な重心が得られるアルゴリズムを知ることができるので解読してみます。
ozwk

2018/06/07 02:45 編集

> そんな単純な物で物体の重心がなぜわかるのか不思議です。 念のための確認ですが、 画像に写っている物体の実際の質量的な意味での重心を求めているわけじゃないです。 リンク先の「厳密な重心」の意味を取り違えてませんか?
Chironian

2018/06/07 02:51 編集

carnage0216さん > に関しては大変参考になります。あの、もしよろしければどうやって以上の情報を探したのか方法を教えてもらえないでしょうか? 明確に書いてある資料を見た記憶はないです。画像処理を体系的に学んだことがないからかも知れません。 「画像の重心の計算式」として提示されている式と物理的な重心の計算式を比較して導いた結論だったように記憶しています。(同じ式ですから意味を理解すれば極簡単です。) > 1画素はわかるのですが、1単位質量とはどのような意味でしょうか? Kgでもgでもtでもなんでもよいです。何であろうが何か1つの固定な質量であれば良いような時に使います。重心の計算式を見れば分かりますが、割り算するので「1単位質量」の値がなんだろうと消えます。10a / 2a = 5ですね。aが消えます。これと同じ原理です。 > 個人的には1画素RGB24bitとしてやったほうがいいような気がしましたが、色によって質量が変わるのはおかしな話なのでグレースケールにしたほうがいいのかもしれません。 例えば、輝度が高いところほど「重い」として重心を求めることも可能です。必要に応じて定義して使うものです。 http://blog.livedoor.jp/soudensan/archives/7442139.html > グレースケールからでも厳密な重心が得られるアルゴリズムを知ることができるので解読してみます。 繰り返しますが、質量のない画像に「重心」などというものは存在しません。ですので、求めたい「重心」の定義を決めないと「厳密な重心」を求めることは不可能です。まずは定義しましょう。それを定義できれば、物理的な重心の式を当てはめて計算することで「厳密な重心」を求めることが出来ます。 ちなみに、carnage0216さんがリンクしていたサイトの人は、よく使われる「画像の重心」の定義とは異なった使い方をしているのに、「画像の重心」を定義していません。ですので少なくとも「厳密」ではないです。
carnage0216

2018/06/07 08:47

勘違いしていました。 やはり質量がないと重心を求めるのは無理みたいですね。 少し話がずれるのですが、 質量から重心を求めるプログラムを作るとしたら、 重心が知りたい物体の質量をあらかじめ定義してから、 質量と物体の形を検出して3dにしてから重心を求めれば良いと考えますが、それ以外に良い考えが思いつきません。
Chironian

2018/06/07 09:13

> やはり質量がないと重心を求めるのは無理みたいですね。 重心は質量の中心ですから、質量がなければ求めることはできないです。 しかし、「図形の中心」を定義してそれを重心と呼ぶことも多いですよ。きちんと定義された重心(図形の中心)であれば厳密に計算可能です。 > 重心が知りたい物体の質量をあらかじめ定義してから、 物体の質量を「定義」するなんてことはできませんよ。 多くの人が使っているのと異なる意味で言葉を使うと会話が成り立たないです。 私は↓のような意味で使っています。 http://web.ydu.edu.tw/~uchiyama/ron/ron_22.html
carnage0216

2018/06/07 09:20

度々すいません。 >>特定の条件(1画素=1単位質量)を仮定した時の重心=図形の重心としているケースが多いと思います。 との事ですが、プログラムを見る限り定義らしきものは見当たりませんでした。 って事はグレースケールにした事で1か0だけになり、わざわざ定義しなくても良かったため定義が書いていないのでしょうか?
Chironian

2018/06/07 09:44

この場合の「定義」は通常は日本語で書きますので、プログラム中には現れません。コメントに書くことはありますが、参考にされたサイトはコメントにも解説にも書いていないようです。 因みに、グレースケール画像は0~255の値を取ることが多いです。2値化する際にグレースケール画像で0と255のみ使うケースも少なくないです。因みにjpeg画像で0と255のみということは有りえません。
carnage0216

2018/06/07 09:54

えーと、 >>きちんと定義された重心(図形の中心)であれば厳密に計算可能です。 きちんと定義された重心(図形の中心)であれば、その重心の座標が求まる分けですね。
Chironian

2018/06/07 10:10

その通りです。 ところで、どうも「定義」の周辺で会話が成り立っていない印象を受けます。 不毛な会話はこの辺で止めにしましょう。
carnage0216

2018/06/07 10:12

すいませんでした。 解答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問