PHP画像のセグメンテーションの例

こんにちは

非常にまれですが、それにもかかわらず、画像を論理的な断片に自動的に分割する必要性の問題が生じます。 PHPツールのみに制限されている場合、タスクはもう少し難しくなりますが、解決可能です。

この記事では、あまり洗練されていないオーディエンスを対象としたパターン認識の特殊なケースを検討します。

この記事では、明示的なリンクを持つサイトの1つからの例を使用していますが、そのサイトは私のサイトではありません。最初は記事を書く考えはありませんでした。



だから、ソースデータを考慮してください





画像



さまざまなサイズと比率の長方形の写真。白い背景があり、驚きの幼稚園の数字と、時には数字を指すウォーターマークが描かれています。 タスクは、ウォーターマークのない人物の写真を取得することです。



画像は非常にシンプルであるため、可能なオプションの全範囲(リンクは記事の最後に示されています)から、成長領域の方法を適用するのが妥当です。 その本質は、最初に任意のポイントで突いて隣接するピクセルの色を比較することです。この場合、類似性の基準は色が白であってはならないということです。 色が白でない場合、ピクセルの座標を記憶し、その近傍を比較するため、セグメントの端に到達するまで領域が広がります。 次に、次のポイントで突くなど。 誰もがポーキングが完全にランダムであることは明らかです。これは、最適な方法ではありません。空のピクセルを長時間移動できるからです。 この方法は、焦土法とも呼ばれます。



相互作用1.非白色領域の選択



このステップでは、元の画像の高さと幅を取得し、二重ループでピクセルごとにカラーを読み取ります。 すべてのデータは二重配列に入力されます。白ピクセルが0に設定されている場合、白でない場合は1に設定されます。 画面にデータを表示して、わかりやすくし、間違っているかどうかを確認します。



画像



PHPコード
$path = '../images/series/9/1.jpg'; $size = getimagesize($path); $img = imagecreatefromjpeg($path); $x = 1; $y = 1; $color = array(); for ($i = 1; $i < $size[0] * $size[1]; $i++) { $color_index = imagecolorat($img, $x, $y); //16777215 //    $color_tran = imagecolorsforindex($img, $color_index); if (($color_tran['red'] == 0 && $color_tran['green'] == 0 && $color_tran['blue'] == 0) or ($color_tran['red'] <= 255 && $color_tran['red'] >= 235 && $color_tran['green'] <= 255 && $color_tran['green'] >= 235 && $color_tran['blue'] <= 255 && $color_tran['blue'] >= 235) or ($color_tran['red'] >= 190 && $color_tran['red'] <= 230 && $color_tran['green'] >= 225 && $color_tran['green'] <= 245 && $color_tran['blue'] >= 245 && $color_tran['blue'] <= 255) ) { $color[$x][$y] = 0; } else { $color[$x][$y] = 1; } $x++; if ($x > $size[0]) { $x = 1; $y++; } }
      
      







上記のコードでは、入力に最高品質でないjpgがあるため、白に近いピクセルは破棄されます。



次に、元の画像の結果の領域にペイントします。



画像



ご覧のとおり、このアルゴリズムはゴミの断片をキャッチします(したがって、上記のコードでは色によるフィルタリングがあります)



誤ったアプローチ、セグメントの境界領域を特定する試み



各セグメントはその境界点を定義できます。左側のセグメント境界については、これらは左、上、下、右境界と同様に、左にカラーピクセルを持たない点になります。

実装しようとしています



画像



挽く



画像



また、多くのピクセルがまだ誤ってキャプチャされていますが、次の写真は多かれ少なかれ受け入れられる結果を持っています



画像



これらの制限内のすべての色付きのドットをペイントすると、これらがスマーフであることをすでに理解できる画像が得られます)



画像



人間の側からは、これらのセグメントはすでに明確に分割できますが、アルゴリズムの側から見ると、まだ1と0の配列、0と1の配列です-セグメントの境界のみです(しかし、今は1つの配列であり、主な質問はセグメントに従って分割します)。

元の画像のセグメント間に明確な空白があります。原則に従ってセグメントを分離してみましょう。「シェーディングされたセグメント間に大きなギャップ(N以上)がある場合、ギャップの左側と右側のメイン配列のピクセルを分割します」



画像



画像



さらに同じ方法で水平方向のギャップを作ることもできますが、写真の中央に文字が刻まれているため、それらを行うのはより困難です。

しかし、この瞬間、私は間違った方向に進んでいることに気づいたので、これらの実験を終了しました。

では、結果に戻ります-ユニットの大きな配列があります-色付きピクセル、それらをセグメントに分割する方法は?



面積成長法



簡単な説明は上記(記事の冒頭)にありますので、すぐにビジネスに取り掛かりましょう。



画像



私たちはスマーフに火をつけました。プロセス全体が再帰を経ていることは明らかです。統合ブロックを色で塗りつぶしています。



画像



PHPコード
 function nburn($ret, $color, $img, $col = false) { $ret_arr = array(); foreach ($ret as $k => $v) { foreach ($v as $k2 => $v2) { $ret_arr['numb'] = get_nearleast($color, $k, $k2); $stop = false; if (count($ret_arr['numb']) > 0) { if (count($ret_arr['numb']) == 1) // 1-       $stop = true; else $toys[] = array(); } if (!$stop) { while (count($ret_arr['numb']) > 0) { $x = key($ret_arr['numb']); $y = key($ret_arr['numb'][$x]); $toys[key($toys)][$x][$y] = 1; unset($ret_arr['numb'][$x][$y]); if (count($ret_arr['numb'][$x]) == 0) unset($ret_arr['numb'][$x]); unset($color[$x][$y]); //    $x = key($ret_arr['numb']); if ($x == "") break; $y = key($ret_arr['numb'][$x]); $near = get_nearleast($color, $x, $y); foreach ($near as $f => $g) { foreach ($g as $f2 => $g2) { $ret_arr['numb'][$f][$f2] = 1; } } } next($toys); } } } return $toys; }
      
      







この関数を使用して、ピクセルを持つ配列は多くのサブ配列(多次元配列)に分割されます。 各サブ配列は、1つのセグメント化された画像に対応します。 さらに、結果のサブアレイを分析して、碑文に対応する最も幅の広いアレイを削除できます。

よく知られた例として、誰もが性格を推測したと思います。



画像



ただし、アルゴリズムが正しく機能しない場合があります。 たとえば、次の図では、キャラクターの手に白い物体が残っているため、最初の図が2つに分割されています。 このような場合、スクリーンされた色をより細かく調整するか、手動で調整する必要があります。



画像



ホストされたコードは自分用に作成されたため、よりエレガントなソリューションが存在する可能性が高いです。

関連リンク:





UPD

プラスするすべての人に感謝します。



All Articles