赤い上部と白い「柱」を持つ2つの白い壁の下の写真の赤い壁と白い四角の両方の画像で位置を検出しようとしています:
私のアプローチは、この出力から簡単に検出できるようになった赤い壁を見つけるためにしきい値処理を行うことでした。
今私の問題は白い正方形の位置を検出することですが、白い壁を考えるとこれはより困難です. 白に基づいてしきい値を設定すると、白い四角い支柱の間に望ましくない白い壁が残ってしまいます。
どんな助けでも大歓迎です。
赤い上部と白い「柱」を持つ2つの白い壁の下の写真の赤い壁と白い四角の両方の画像で位置を検出しようとしています:
私のアプローチは、この出力から簡単に検出できるようになった赤い壁を見つけるためにしきい値処理を行うことでした。
今私の問題は白い正方形の位置を検出することですが、白い壁を考えるとこれはより困難です. 白に基づいてしきい値を設定すると、白い四角い支柱の間に望ましくない白い壁が残ってしまいます。
どんな助けでも大歓迎です。
1 つのアプローチは、次のように入力画像をしきい値処理cv::inRange()
することで構成されます。
cv::Mat image = cv::imread(argv[1]);
if (image.empty())
{
std::cout << "!!! Failed imread()" << std::endl;
return -1;
}
cv::Mat red_image;
cv::inRange(image, cv::Scalar(40, 0, 180), cv::Scalar(135, 110, 255), red_image);
//cv::imwrite("out1.png", red_image);
出力:
cv::findContours
しきい値処理された画像の輪郭を取得して、境界ボックスを作成できるようにするために使用できます。これは、ここで説明する手法です。
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours( red_image,
contours,
hierarchy,
CV_RETR_TREE,
CV_CHAIN_APPROX_SIMPLE,
cv::Point(0, 0) );
std::vector<std::vector<cv::Point> > contours_poly( contours.size() );
std::vector<cv::Rect> boundRect( contours.size() );
for( int i = 0; i < contours.size(); i++ )
{
cv::approxPolyDP( cv::Mat(contours[i]), contours_poly[i], 3, true );
boundRect[i] = cv::boundingRect( cv::Mat(contours_poly[i]) );
}
// Debug purposes: draw bonding rects
//cv::Mat tmp = cv::Mat::zeros( red_image.size(), CV_8UC3 );
//for( int i = 0; i< contours.size(); i++ )
// rectangle( tmp, boundRect[i].tl(), boundRect[i].br(), cv::Scalar(0, 255, 0), 2, 8, 0 );
//cv::imwrite("out2.png", tmp);
出力:
上の画像に表示されているすべての長方形は、ベクトルcv::Rect
内にオブジェクトとして格納されています。boundRect
各長方形は 2 つの反対側のオブジェクトで構成されているため、このベクトルを反復処理して、オブジェクトのみcv::Point
で構成された新しいベクトルを作成します。cv::Point
// Two opposite cv::Point can be used to draw a rectangle.
// Iterate on the cv::Rect vector and retrieve all cv::Point
// and store them in a cv::Point vector.
std::vector<cv::Point> rect_points;
for( int i = 0; i < contours.size(); i++ )
{
rect_points.push_back(boundRect[i].tl());
rect_points.push_back(boundRect[i].br());
}
//cv::Mat drawing = cv::Mat::zeros( red_image.size(), CV_8UC3 );
cv::Mat drawing = image.clone();
白い正方形を見つけるロジックは次のとおりです。互いに 25x25 の距離内にある 2 つのピクセルが白い正方形を定義すると仮定します。
// Draw a rectangle when 2 points are less than 25x25 pixels of
// distance from each other
for( int i = 0; i < rect_points.size(); i++ )
{
for( int j = 0; j < rect_points.size(); j++ )
{
if (i == j)
continue;
int x_distance = (rect_points[i].x - rect_points[j].x);
if (x_distance < 0)
x_distance *= -1;
int y_distance = (rect_points[i].y - rect_points[j].y);
if (y_distance < 0)
y_distance *= -1;
if ( (x_distance < 25) && (y_distance < 25) )
{
std::cout << "Drawing rectangle " << i << " from "
<< rect_points[i] << " to " << rect_points[j]
<< " distance: " << x_distance << "x" << y_distance << std::endl;
cv::rectangle( drawing,
rect_points[i],
rect_points[j],
cv::Scalar(255, 50, 0),
2 );
break;
}
}
}
//cv::imwrite("out3.png", drawing);
cv::imshow("white rectangles", drawing);
cv::waitKey();
出力:
このアルゴリズムは非常に生々しく、下にある 2 つの白い四角が見落とされています。これは、下に赤い壁がなく、上にあるだけだからです。
したがって、このアプローチを改善するのはあなた次第です:)
幸運を。
シーンのすべての重要な情報は、抽出された赤いバーの 2 値化された画像にあるようです。このステップでは元の画像を無視して、長方形のジオメトリのみを使用してそれらの間の領域を見つけようとします。
たとえば、findContoursを呼び出して、サンプル画像の 8 つのブロブを取得できます。重心間の線上の点を確認すると、pointPolygonTest に最小値を返す点は、白い点の 1 つの中心点 (または少なくとも近い点) です。
遭遇するシーンとその画像に関する既知の情報を使用できます。たとえば、等高線を「左」と「右」のバーにグループ化し、特定の等高線間でのみライン検索を行うことができます。ただし、より不可知論者になる必要がある場合は、re. あなたの入力、しきい値処理された画像から多かれ少なかれすべて(シーンの向き、壁の数、長方形の厚さ...)を導き出すことはかなり可能です。