ランレングスエンコーディングからのピクセルチェーン
-
20-09-2019 - |
質問
私はこれについて長い間頭をぶつけてきました
映像化の仕事をしています。これまでのところ、画像を 2 値化してきました。つまり、グレースケール画像から、特定の値以下のすべてのピクセルが削除されます。これにより、元の画像の一部の領域のみが得られ、それらの領域の周囲には多くの「ゼロ ピクセル」が含まれます。
次に、領域を「blob」にランレングスエンコードしました。実行はデータの圧縮方法です。たとえば、正方形を 2 値化したと仮定すると、画像全体を記述する実行は数回しかないことになります。ランは、X、Y 座標と長さによって定義されます。
イメージを再作成するときは、実行ごとに X、Y 座標に移動し、実行の長さに合わせて X 軸上のピクセルを追加します。
次に、ランを取得し、そこから領域の輪郭を記述するチェーンを作成する必要があります。その方法がわかりません。
たくさんの X、Y、長さのランがあり、エッジを「ナビゲート」して、 鎖. 。通常、イメージングではこのプロセスは元の画像を使用して行われますが、ここではもう元の画像を使用できないため、実行で計算する必要があります。
これが文章の大きな壁のように見えることはわかっていますが、この質問をより適切に行う方法がわかりません。
同一の実装に関するヒントやポインタがあれば、それは素晴らしいことです。
編集
unwind のおかげで、いくつかの画像をリンクします。
(ソース: tudelft.nl)
この例では、画像 B を処理して輪郭 C (これをチェーンと呼びます) にします。ただし、D、ランレングスから輪郭を生成したいと考えています。
解決 3
さて、私はその契約を紛失しましたが、答えは フリーマンチェーンコーディング技術
以前考えていたのとは異なり、ランレングス エンコーディングであるという事実はアルゴリズムとは何の関係もありません。
他のヒント
一見したところ、そのための実用的なアルゴリズムはわかりません。貧しい人の解決策はこうだろう 拡大する 長さエンコードされた画像から元の画像を抽出します。したがって、行が次のようになった場合:
A 3B 10A C 8D
C 4D 3A 6C 9A
ここで、文字は実際のピクセル値を返します (例:A = 0、B = 127、...)。ピクセル値を 2 次元配列 (または選択した別のデータ構造) に書き込むことができます。次のようになります。
ABBBAAAAAAAAAACDDDDDDDD
CDDDDAAACCCCCCAAAAAAAAA
その後、チェーンを生成し、配列を削除してチェーン情報を保持します。確かにこれは高価なので、 多分 元の画像を長さエンコードする前にこれを行うこともできます。
完全にシンプルで実用的な解決策 (C++) を次に示します。
#include <iostream>
#include <vector>
struct Run { int x, w; };
enum { EAST, NORTHEAST, NORTH, NORTHWEST, WEST, SOUTHWEST, SOUTH, SOUTHEAST };
int main() {
const Run data[] = {
{ 7, 2 },
{ 5, 6 },
{ 5, 7 },
{ 5, 7 },
{ 6, 6 },
{ 0, 12 },
{ 0, 12 },
{ 0, 11 },
{ 1, 7 },
{ 3, 4 },
{ 3, 4 },
{ 3, 5 },
{ 3, 7 },
{ 3, 7 },
{ 5, 5 }
};
std::vector<Run> runs(data, data + 15);
std::vector<int> before;
std::vector<int> after;
unsigned int i;
int j;
for (i = 0; i < runs.size() - 1; ++i) {
if (runs[i].x < runs[i + 1].x) {
for (j = 0; j < runs[i + 1].x - runs[i].x - 1; ++j)
before.push_back(WEST);
before.push_back(NORTHWEST);
} else if (runs[i].x > runs[i + 1].x) {
before.push_back(NORTHEAST);
for (j = 0; j < runs[i].x - runs[i + 1].x - 1; ++j)
before.push_back(EAST);
} else {
before.push_back(NORTH);
}
int first_right(runs[i].x + runs[i].w);
int second_right(runs[i + 1].x + runs[i + 1].w);
if (first_right < second_right) {
after.push_back(SOUTHEAST);
for (j = 0; j < second_right - first_right - 1; ++j)
after.push_back(EAST);
} else if (first_right > second_right) {
for (j = 0; j < first_right - second_right - 1; ++j)
after.push_back(WEST);
after.push_back(SOUTHWEST);
} else {
after.push_back(SOUTH);
}
}
for (j = 0; j < runs.back().w - 1; ++j)
after.push_back(WEST);
std::reverse(before.begin(), before.end());
after.insert(after.end(), before.begin(), before.end());
for (j = 0; j < int(after.size()); ++j) {
switch (after[j]) {
case EAST: std::cout << "EAST\n"; break;
case NORTHEAST: std::cout << "NORTHEAST\n"; break;
case NORTH: std::cout << "NORTH\n"; break;
case NORTHWEST: std::cout << "NORTHWEST\n"; break;
case WEST: std::cout << "WEST\n"; break;
case SOUTHWEST: std::cout << "SOUTHWEST\n"; break;
case SOUTH: std::cout << "SOUTH\n"; break;
case SOUTHEAST: std::cout << "SOUTHEAST\n"; break;
}
}
}
これは、実行を反復し、左右のエンドポイントのジャンプ先の方向をテストし、適切な数のチェーン要素を 2 つのベクトルに追加することで機能します。1 つは順方向で右側、もう 1 つは逆順で左側です。次に、最後のスキャンラインに適切な数のリンクを追加して 2 つのチェーンを接続し、左側のチェーンを反転して右側のチェーンに追加して、最終的なチェーンを生成します。
これがあなたが探しているものであることを願っています!