C++ OpenCV 詳しい方お力をお貸しください

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ビギナー
記事: 3
登録日時: 9年前
住所: 東京都

C++ OpenCV 詳しい方お力をお貸しください

#1

投稿記事 by ビギナー » 9年前

初めまして、私はここではビギナーと名乗らせていただきます。
私自身C言語についてまだまだ勉強不足なところもありますが、どのような処理をすればよいのか思いつかない点も多くあります。
そのためC言語について理解がまだまだのためこの名前にしました。


数日前に一度同じ内容で投稿したのですが、コードを囲むなどのミスで、分かりにくい内容になっていました。
なので今一度同じ内容を投稿させていただきます。
私はタイトルのように紙の座標 主に紙の四隅の座標を出したいと思っています

そのため下記のような輪郭を取り、座標を取るプログラムを実行しました

コード:

#include <C:\OpenCV2.4.9\sources\include\opencv\cv.h>
#include <C:\OpenCV2.4.9\sources\include\opencv\highgui.h>
int
main (int argc, char *argv[])
{
int i;
IplImage *src_img = 0, **dst_img;
IplImage *src_img_gray = 0;
IplImage *tmp_img;
CvMemStorage *storage = cvCreateMemStorage (0);
CvSeq *contours = 0;
CvPoint *point, *tmp;
CvSeq *contour;
CvTreeNodeIterator it;
CvFileStorage *fs;

if (argc <= 2)
src_img = cvLoadImage ("../test.png", CV_LOAD_IMAGE_COLOR);
if (src_img == 0)
return -1;

src_img_gray = cvCreateImage (cvGetSize (src_img), IPL_DEPTH_8U, 1);
cvCvtColor (src_img, src_img_gray, CV_BGR2GRAY);
tmp_img = cvCreateImage (cvGetSize (src_img), IPL_DEPTH_8U, 1);
dst_img = (IplImage **) cvAlloc (sizeof (IplImage *) * 3);
for (i = 0; i < 3; i++) {
dst_img[i] = cvCloneImage (src_img);
}

// 画像の二値化と輪郭の検出
cvThreshold (src_img_gray, tmp_img, 100, 255, CV_THRESH_BINARY);
cvFindContours (tmp_img, storage, &contours, sizeof (CvContour), CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);


/* 輪郭シーケンスから座標を取得 */
fs = cvOpenFileStorage ("contours.txt", 0, CV_STORAGE_WRITE);
// ツリーノードイテレータの初期化
cvInitTreeNodeIterator (&it, contours, 1);
// )各ノード(輪郭)を走査
while ((contour = (CvSeq *) cvNextTreeNode (&it)) != NULL) {
cvStartWriteStruct (fs, "contour", CV_NODE_SEQ);
// 輪郭を構成する頂点座標を取得
tmp = CV_GET_SEQ_ELEM (CvPoint, contour, -1);
for (i = 0; i < contour->total; i++) {
point = CV_GET_SEQ_ELEM (CvPoint, contour, i);
cvLine (src_img, *tmp, *point, CV_RGB (0, 0, 255), 2);
cvStartWriteStruct (fs, NULL, CV_NODE_MAP | CV_NODE_FLOW);
cvWriteInt (fs, "x", point->x);
cvWriteInt (fs, "y", point->y);
cvEndWriteStruct (fs);
tmp = point;
}
cvEndWriteStruct (fs);
}
cvReleaseFileStorage (&fs);


cvNamedWindow ("Contours", CV_WINDOW_AUTOSIZE);
cvShowImage ("Contours", src_img);
cvWaitKey (0);

cvDestroyWindow ("Contours");
cvReleaseImage (&src_img);
cvReleaseImage (&src_img_gray);
cvReleaseImage (&tmp_img);
cvReleaseMemStorage (&storage);

return 0;
}
 
しかし輪郭の複数の点がテキストファイルに保存されてしまいます.....

コード:

%YAML:1.0
contour:
- { x:158, y:39 }
- { x:156, y:41 }
- { x:156, y:43 }
- { x:154, y:45 }
- { x:154, y:46 }
- { x:153, y:47 }
- { x:153, y:48 }
- { x:151, y:50 }
- { x:151, y:52 }
- { x:149, y:54 }
- { x:149, y:55 }
- { x:148, y:56 }
- { x:148, y:57 }
- { x:147, y:58 }
- { x:147, y:59 }
- { x:145, y:61 }
- { x:145, y:62 }
- { x:144, y:63 }
- { x:144, y:64 }
- { x:143, y:65 }
- { x:143, y:66 }
- { x:141, y:68 }
・
・
・
・
・
 
そこで私なりに考え、テキストファイルからX Yの最大値と最小値の組合わせを取れば、四隅の座標を取得できるのではないかと思っています。

しかしそのプログラムが上手くいかず....
少しだけでもアドバイスいただけませんか?
C++の詳しい方 ご教授ください。



また違うやり方の方が簡単や、お勧めなどあればそれでもかまいません。
よろしくお願いします。


PS.皆さん返信ありがとうございます。

私自身の思っている内容を上手く伝える事が出来ずに申し訳ありません。

もう一度ゆっくり自分自身で考えお話しします。

1.まずはじめにカメラから静止画の画像を取得します。
※画像には机と用紙が写っている画像です。
2.取得した画像を透視投影変換を使い机と垂直方向から見た画像に変換します。
※ここで歪みを取ります。
3.変換後の画像から用紙の位置が知りたいため、輪郭を取得、輪郭を取得するときに使った複数の座標点をテキストファイルに保存

4、テキストファイルには多くのxy軸の座標が書き込まれています。そのため角の座標のみを求めたいと思い、テキストファイル内の情報を整理できないかと自分自身で考えつつ、もし詳しい方がいらっしゃったら、アドバイスをいただきたいと思っています。
この後には重心を求め、画像をまっすぐ回転させようと思っています。

具体的な全体像はこのようなものだと思っています。

文章のみの説明のみでは私があまり文章を書くにが上手ではないようで、伝わらないと思いましたので、とても簡単ではありますがペイントで説明する形の図もつけたためご覧ください。
添付ファイル
説明画像2.png
説明画像2.png (17.31 KiB) 閲覧数: 7660 回
説明画像1.png
説明画像1.png (12.37 KiB) 閲覧数: 7660 回
最後に編集したユーザー ビギナー on 2016年1月18日(月) 21:02 [ 編集 1 回目 ]

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: C++ OpenCV 詳しい方お力をお貸しください

#2

投稿記事 by usao » 9年前

オフトピック
>数日前に一度同じ内容で投稿した

少なくともそこへのリンクを張るくらいはした方がいいと思うし,文面がコピペなせいか,

>タイトルのように

とか意味不明な文章になってたりして,
わざわざトピックを作り直したにしては雑.

>コードを囲むなどのミスで、分かりにくい内容になっていました。

とのことだが,今度はインデントのないコードであり,やはり見難い.

>テキストファイルからX Yの最大値と最小値の組合わせを取れば、四隅の座標を取得できるのではないかと思っています。

という話の意味(具体的にどういうことを言っているのか)がわかりません.
絵とか,あるいはわかりやすいサンプルデータとかを用いて説明できませんか?

can110
記事: 27
登録日時: 10年前

Re: C++ OpenCV 詳しい方お力をお貸しください

#3

投稿記事 by can110 » 9年前

OpenCVはほとんど触ったことないので具体的なコードは提示できませんが、以下の関数で取得できるような気がします。
MinAreaRect2(与えられた2次元点集合を囲む最小の矩形を求める)

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: C++ OpenCV 詳しい方お力をお貸しください

#4

投稿記事 by usao » 9年前

>以下の関数で取得できるような気がします。

撮影対象たる紙の形が長方形なのだとしても
それをカメラで真正面から撮影する(&歪が無視できる程度である)のでなければ
画像上での紙の像の形を長方形とはみなせないですよね

……みたいなことを判断するための,問題設定的な情報が必要かもですね.

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: C++ OpenCV 詳しい方お力をお貸しください

#5

投稿記事 by usao » 9年前

トピック一覧で変化が無いために追記があったことにしばらく気づきませんでした.


>2.取得した画像を透視投影変換を使い机と垂直方向から見た画像に変換します。
>※ここで歪みを取ります。

【A】ここがかなりうまくできていて,結果として変換後の画像では
紙の像はきれいに長方形の像になる,ということであれば
can110 さんの示されている関数を使う方法でいけるようにも思います.


【B】逆に(?),ちょっといろんなところの処理精度が十分ではない場合もあって
(例えば,ご提示の図では少し台形な雰囲気?に見えますが,丁度こんな感じに)
完全には長方形の像とは言えない状況もあるかも…,ということであれば,前記【A】は使えないかもしれません.

・2値化を行っているようですが,その結果の角が欠けるような場合があることを考えるならば,輪郭点の中から4個選ぶという方法ではダメかもしれません.
・ひょっとしたらコーナー検出処理を行えばそれでいけるかもしれません.
・「得られている輪郭点群をエッジ勾配の向きあたりで4グループにわけて,個別に直線を当てはめ,それらの直線の交点を求める」みたいな方法でもいけるかもしれません.
(画像に無駄なものが映ってないことが前提とできるならば,Contour検出→輪郭→4直線 という手順ではなくく最初からハフ変換とかでもいいかもしれません)

アバター
asd
記事: 319
登録日時: 15年前

Re: C++ OpenCV 詳しい方お力をお貸しください

#6

投稿記事 by asd » 9年前

古い方のトピックに書き込んでしまったのでそちらへのリンクを張っておきますね。

http://dixq.net/forum/viewtopic.php?f=3 ... 7&p=136191

OpenCVをC#で使い、表組みの含まれたスキャン画像の上部2点の角を取得、その2点を使い平行線になるよう回転するプログラムを作ったことがあります。
処理的には、

1.二値化
2.ラベリング(一番大きな表組みだけ残すため)
3.図形を内包する矩形を取得
4.上部の角検出
5.4の2点から補正角度を計算し回転

となっています。

既に回答されている内容と同じなのと、私の場合台形ひずみを考えなくてよかったことがあるので
参考までにどうぞ。
具体的な関数名等は後に補足します。
Advanced Supporting Developer
無理やりこじつけ(ぉ

アバター
asd
記事: 319
登録日時: 15年前

Re: C++ OpenCV 詳しい方お力をお貸しください

#7

投稿記事 by asd » 9年前

asd さんが書きました: 具体的な関数名等は後に補足します。
今更感満載ですが、補足しておきます。

コード:

IplImage _iplimg,_thread,_biggestblob,_bloggray;
//1.二値化
Threshold(_thread, 0, 255, ThresholdType.Binary | ThresholdType.Otsu)

//2.ラベリング(一番大きな表組みだけ残すため)
CvBlobs _cvblob = new CvBlobs(_thread);
CvBlob _blob = CvBlobLib.GreaterBlob(_cvblob);
_blob.Contour.Render(_biggestblob, new CvScalar(255,255,255));
_biggestblob.CvtColor(_blobgray, ColorConversion.RgbToGray);

//3.図形を内包する矩形を取得
CvMemStorage _storage = new CvMemStorage();
CvSeq<CvPoint> _firstContour;
_blobgray.FindContours(_storage, out _firstContour, CvContour.SizeOf, ContourRetrieval.External, ContourChain.ApproxTC89L1);

CvSeq<CvPoint> _contour = _firstContour.ApproxPoly(CvContour.SizeOf, _storage, ApproxPolyMethod.DP, 0);

//4.(上部の)角検出
_blob.Contour.Render(_iplimg, new CvScalar(255, 255, 255));//ラベリング結果を描画
Cv.DrawContours(_iplimg, _contour, new CvScalar(0, 255, 0), new CvScalar(255, 0, 0), 255);//近似矩形も描画
IplImage _iplimggray = new IplImage(new CvSize(w, h), BitDepth.U8, 1);
_iplimg.CvtColor(_iplimggray, ColorConversion.RgbaToGray);
int _cnrcnt=4;
CvPoint2D32f[] _point = new CvPoint2D32f[_cnrcnt];
Cv.GoodFeaturesToTrack(_iplimggray, _eig, _tmp, out _point, ref _cnrcnt, 0.01, w/20);//コーナー検出

//5.4の2点から補正角度を計算し回転
//(本題からそれるので割愛します)
折角なので質問者さんの説明画像をお借りして上記処理を行った結果を載せておきます。
若干ずれている点もありますが、4点の角座標が取れているかと思います。
(緑色の線は近接矩形、赤丸が取得した角座標を中心にして描画した円)
添付ファイル
説明画像2.png
処理前の元画像
説明画像2.png (16.96 KiB) 閲覧数: 7390 回
test.png
処理実行後の結果
test.png (6.02 KiB) 閲覧数: 7390 回
Advanced Supporting Developer
無理やりこじつけ(ぉ

ビギナー
記事: 3
登録日時: 9年前
住所: 東京都

Re: C++ OpenCV 詳しい方お力をお貸しください

#8

投稿記事 by ビギナー » 9年前

C#でプログラムは作ったことがないためこのソースコードは使用できないですが、やり方は参考にさせていただきます。
ありがとうございますC++で作ってみようと思います。

閉鎖

“C言語何でも質問掲示板” へ戻る