「朝ずっと探していたインスピレーション、
最も残念な瞬間に追い越した。
そして、私がSCRUMに向けて出発することを説明するにはどうすればいいですか?
-私と一緒に来て!? 」
チームのコミュニケーションは、大規模プロジェクトの緊急の必要性です。 足場や匿名のアルコール依存症の強制的な集まりのように見えるべきではありません。 チームからの参加が必要です。目にきらめきが必要です。誰もがこの提案の哀osのように、話したいという欲求から引き離されるべきです。 徐々に、私たちのチームは、シンプルで透明なステッカーのおかげで、SCRUMモデルに進化しました。 ステッカーなしのスクラムとは何ですか? 幼少期のほぼ全員がステッカーを持っていて、潜在意識のどこか深いところで、私たちが子供の頃に教師から接着剤を教えられ、ステッカーが均等に接着されていれば、励ましとして彼女は手を打たなかったときに記憶が落ち着きました。 しかし、のんきな幼年期でさえ、私たちは退屈で理解しにくいと思われることをしなければなりませんでした。おもちゃを外したり、ペンで壁を拭いたり、口述で書きます。 成熟したら、私たちには選択肢があります-仕事を他の人に変えることができます。 そして、誰のためにバックログ(レポート)を書き、データをJiraに転送したいのでしょうか? 集会中にJiraを直接使用すると、参加者は議論から除外されます。したがって、奴隷制度廃止に関する国連条約の採択後、このタスクをロボットに移すことが残っています。
その結果、SCRUMボード上のタスクカードを認識および追跡するためのプログラムを作成するというアイデアが生まれました。
最小の問題のステートメントは次のとおりです。
- SCRUMボードの画像を読み取ります。
- ハイライトステッカー
- ステッカー画像を保存します。
- ステッカーがあるボードの領域を決定します。
- ステッカーが属するタスクを決定します。
- タスクのステータスに関する情報を含むファイルを作成します。
実際、タスクは同時に非常にエレガントでシンプルに見えますが、今年はプログラミングのサマースクールでのトレーニングタスクとしてそれを提供しました。 この記事では、最初の3つの要件の実装を検討することを提案します。
ボードの画像をデジタル化すると、ルールは簡単です。椅子から立ち上がった最後の人がボードの写真を撮ります。 将来的には、ロボットに置き換える必要があります。
以下は、簡略化されたSCRUMボードがどのように見えるかの写真です。 非常に単純です。
図1 SCRUMボードの例。
上は、選択されていないタスクのステッカー、開発者のタスクの領域の下(青、緑、赤)です。 各開発者の領域は2つの部分に分かれています-左側は実行中のタスク、右側は完了したタスクです。
セグメンテーション
当たり前のことから始めましょう-ボードの写真を含む元の画像のロードは、非常に簡単にOpenCVツールを使用して行われます。
int main(int argc, char** argv) { vector<cv::Mat> stickers; cv::CommandLineParser parser( argc, argv, keys ); String image_path = parser.get<String>( 0 ); if( image_path.empty() ) { help(); return -1; } cv::Mat image = cv::imread(image_path);
OpenCVはcv :: Matクラスを使用して画像を表現します。 これは興味深いデータ構造であり、このクラスに関する詳細情報は夏に見つけることができます。
次に、ステッカー画像を強調表示するメイン関数が必要です。
recognizeStickers(stickers);
最初のバージョンでは、見つかったステッカーをsticker1.jpg ... stickerN.jpgという名前でファイルに保存するだけです。
saveStickers(stickers); }
ステッカーの画像を選択する機能をさらに詳しく考えてみましょう。 関数のプロトタイプは次のようになります
void recognizeStickers(vector<cv::Mat> &stickers);
均一な背景で対照的なオブジェクトを強調表示する問題を解決するアルゴリズムは、さまざまな方法で実装できます。
- アルゴリズム1. inRange関数を使用して、特定の色を持つオブジェクト(ステッカー)を選択します。
- アルゴリズム2.しきい値関数を使用して、HSV画像のSチャネルから明るいオブジェクト(ステッカー)を強調表示します。
アルゴリズム1
inRangeを使用してステッカーを強調表示するには、次のようにします。
- ステッカーに特徴的な色の範囲を決定します(最初の実装では、定数によって範囲を明示的に設定します)。
- 段階的な変換を使用して、背景色と色が異なるポイントを選択します。
- フィルターを使用して、疑わしいステッカーのポイントを組み合わせて、ソリッドイメージを取得します。
- 連続領域の境界を強調します。
- 疑わしいステッカーのポイントのグループの垂直および水平の境界線を定義します。
以下は、アルゴリズムの概念を示す概略図です。
void recognizeStickersByRange(cv::Mat image,std::vector<cv::Mat> &stickers) { cv::Mat imageHsv; std::vector< std::vector<cv::Point> > contours; // hsv, cv::cvtColor(image, imageHsv, cv::COLOR_BGR2HSV); cv::Mat tmp_img(image.size(),CV_8U); // cv::inRange(imageHsv, cv::Scalar(key_light-delta_light,key_sat-delta_sat,key_hue-delta_hue), cv::Scalar(key_light+delta_light,key_sat+delta_sat,key_hue+delta_hue), tmp_img); // "" cv::dilate(tmp_img,tmp_img,cv::Mat(),cv::Point(-1,-1),3); cv::erode(tmp_img,tmp_img,cv::Mat(),cv::Point(-1,-1),1); // cv::findContours(tmp_img,contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); for (uint i = 0; i<contours.size(); i++) { cv::Mat sticker; // cv::Rect rect=cv::boundingRect(contours[i]); image(rect).copyTo(sticker); // stickers.push_back(sticker); } }
ノイズで分離された領域を結合するためにフィルターを使用して領域を段階的に変換し、ストレッチした後(cv :: dilateメソッド)、次のようになります。
図2。 二値化後の画像。
二値化された画像をパス選択アルゴリズムcv :: findContursの入力に送信し、cv :: boundingRectを使用して各パスの境界矩形を見つけます。 明確にするために、元の画像上に境界矩形を緑色で描画します。
エリアを選択した結果、ステッカーが正常に選択されました。 以下は、テスト画像のアルゴリズムの結果です。
図3 ステッカーが強調表示されます。
境界矩形のパラメーターを知っていれば、ステッカー画像を個別のファイルとして簡単に切り取ってディスクに保存できます。
for (uint i = 0;i < stickers.size();i++) { cv::imwrite("sticker"+toString(i+1)+".jpg",stickers[i]); }
その結果、sticker1.jpg ... stickerN.jpgファイルがディスク上に生成されます。 ステッカーファイルの内容の例を以下に示します。
図4 選択したステッカーの画像。
上記の例では、ステッカーの色を決定するアルゴリズムを実装していませんが、HSV空間の定数key_light、key_sat、key_hueを使用して設定していることに注意してください。これは通常の状態では適切ではありません。 突然ステッカーの色が変わった場合、アルゴリズムを再構成する必要があります。 開発者エリア(青、緑、赤)の境界長方形は選択されていません。 基本的に色の定数を設定し、それらに同様のアルゴリズムを割り当てることができます。これにより、領域の境界が自動的に決定され、タスクのステータスが決定されます。
アルゴリズム2。
例/ 1 /および/ 3 /に示すように、cv :: threshold関数を使用します。 最初に、cv :: cvtColor関数を使用して入力フレームをHSV形式に変換し、cv :: splitを使用して結果を分割しました。 結果は以下のとおりです。
図5 Hチャンネル画像。
図6 Sチャネル画像。
図7 Vチャンネル画像。
図5-図7からわかるように、白い背景でステッカーを処理する最大の関心は、画像のSチャンネルです。色飽和ステッカーの値は最大になり、白い背景は最小になります。 これは、 ここで最も明確に示されています 。 正しい境界値でcv :: threshold関数を使用すると、選択したステッカーで目的のバイナリイメージが得られ、そこからアルゴリズム1と同様にcv :: findContours関数を使用してステッカーを選択できると想定できます。
std::vector<cv::Mat> hsvPlanes; cv::split(inputHsvImage, hsvPlanes); cv::Mat image = hsvPlanes[1]; double thresh = 110; double maxValue = 255; threshold(image,image, thresh, maxValue, cv::THRESH_BINARY);
上記の例では、境界値が110の場合、目的の2値化結果が得られます。 アルゴリズム1の場合と同様に、境界値を選択する必要があります。境界値は、画像のヒストグラムを分析することで計算できます。
図8 Sチャネル画像のヒストグラム。
ステッカーは明るいので、ステッカーの色はSチャンネルのヒストグラムの右端のピークに対応しています。 アルゴリズム/ 4 /を使用して境界を決定したら、段階的な変換に必要な境界の値を取得します。
int findMostRightExtremum(cv::Mat histNorm) { vector< float > data; for (int i=0; i<histNorm.rows; i++) data.push_back(histNorm.at<float>(i)); // . Persistence1D p; p.RunPersistence(data); // 0,002. vector< TPairedExtrema > Extrema; p.GetPairedExtrema(Extrema, 0,002); sort(Extrema.begin(),Extrema.end(), [](const TPairedExtrema &a, const TPairedExtrema &b) -> bool { return (a.MaxIndex) > (b.MaxIndex); } ); // return (Extrema[0].MinIndex)*(255/histNorm.rows); }
cv :: threshold関数を使用した2値化の結果、次のようになります。
図9 計算された境界のcv ::しきい値による2値化。
ご覧のとおり、開発者の領域の境界を定義するステッカーと色付きの境界マーカーの両方が強調表示されているため、各開発者のボード領域を選択できます。
これは、SCRUMプロセスへのコンピュータービジョンテクノロジーの導入に関する一連の記事の最初の記事です。 次のタスクは考慮されていません。
- ボードゾーンの選択(「選択されていないタスクのゾーン」、開発者向けの「進行中」および「完了」領域);
- ステッカーが配置されているボードの領域を決定します。
- ボード上を移動した後のステッカーのマッチング。
- タスクテキストの認識;
- jiraとの相互作用。
ところで、まもなく、C ++ 11/14プログラミングに関する一連の無料ウェビナーを計画しています。画像処理と拡張現実の分野の例もあります。