OpenCV-approxPolyDP dla map krawędzi (nie konturów)

Z powodzeniem zastosowałem metodę CV:: approxPolyDP na konturach( CV:: findContours), aby przedstawić kontur z prostszym wielokątem i w domyśle zrobić denoising.

Chciałbym zrobić to samo na mapie krawędzi nabytej z kamery RGBD (która jest w ogóle bardzo głośna), ale z niewielkim sukcesem do tej pory i nie mogę znaleźć względnych przykładów w Internecie. Powodem, dla którego tego potrzebuję, jest to, że za pomocą mapy krawędzi można również użyć krawędzi między palcami, krawędziami tworzone przez okluzję palca lub krawędzie utworzone w dłoni.

Czy ta metoda ma zastosowanie do ogólnych map krawędzi, innych niż kontury?

Czy ktoś mógłby wskazać mi przykład?

Niektóre zdjęcia dołączone:

Udany przykład konturów: Tutaj wpisz opis obrazka

Problematyczny przypadek dla map krawędzi:

Najprawdopodobniej rysuję rzeczy w niewłaściwy sposób, ale rysowanie tylko pikseli zwróconych metodą pokazuje, że prawdopodobnie duże obszary nie są reprezentowane w wyniku końcowym (i to niewiele zmienia w zależności od parametru epsilon).

Tutaj wpisz opis obrazka

Dołączam również obraz głębi, podobny do tych, których używam w eksperymentalnym potoku opisanym powyżej. Ten obraz głębi nie został uzyskany przez kamerę głębi, ale został syntetycznie wygenerowany przez odczyt bufora głębi gpu za pomocą OpenGL.

Tutaj wpisz opis obrazka

Dla odniesienia, jest to również Mapa krawędzi obrazu głębi uzyskanego bezpośrednio z kamery głębi (przy użyciu obrazu raw, nie wygładzanie itp.)

Tutaj wpisz opis obrazka

(dłoń jako widok z kamery głębi, dłoń skierowana w górę, palce" zamykające się " w kierunku dłoni)

Author: rwong, 2014-03-02

1 answers

Twój problem z approxPolyDP wynika z formatowania danych wejściowych do approxPolyDP.

Wyjaśnienie

approxPolyDP oczekuje, że jego wejście będzie wektorem Point s. punkty te definiują krzywą wielokątną, która będzie przetwarzana przez approxPolyDP. Krzywa może być otwarta lub zamknięta, którą można kontrolować za pomocą flagi.

Kolejność punktów na liście jest ważna. Tak jak odwzorowuje się wielokąt ręcznie, każdy kolejny punkt w wektorze musi być kolejnym wierzchołkiem wielokąta, zgodnie z ruchem wskazówek zegara lub przeciwnie do ruchu wskazówek zegara.

Jeśli lista punktów jest przechowywana w porządku rastrowym (posortowana przez Y, a następnie X), to point[k] i point[k+1] niekoniecznie należą do tej samej krzywej. To jest przyczyna problemu.

Ten problem jest wyjaśniony ilustracjami w OpenCV-Jak wyodrębnić krawędzie z wyniku funkcji Canny? . Cytat z Mikhail : "Canny nie łączy pikseli w łańcuchy lub segmenty."


Ilustracja " porządku rastrowego" to jest generowane przez Canny.

Kolejność rastrów


Ilustracja "porządku konturowego", który jest oczekiwany przez approxPolyDP

Kolejność konturów


Co jest potrzebne

Potrzebna jest lista "łańcuchów pikseli krawędzi". Każdy łańcuch musi zawierać sąsiadujące ze sobą piksele krawędzi, tak jak ktoś rysuje kontur obiektu ołówkiem, bez opuszczania papieru przez czubek ołówka.

To nie jest to, co jest zwracane z metod wykrywania krawędzi, takich jak Canny. Dalsze przetwarzanie jest potrzebne do przekształcenia mapy krawędzi w łańcuchy sąsiadujących (ciągłych) pikseli krawędzi.

Sugerowane rozwiązania

(1) Użyj binary threshold zamiast detekcji krawędzi jako wejścia do findContours

Ma to zastosowanie, jeśli istnieje wartość progowa, która oddziela rękę od tła i że ta wartość działa dla całego układu (a nie tylko jego części).

(2) Zeskanuj mapę krawędzi i zbuduj listę sąsiadujących pikseli, badając sąsiadów każdego piksela krawędzi.

Jest to podobne do algorytmu connected-components, z tą różnicą, że zamiast znajdować blob (gdzie musisz znać tylko przynależność każdego piksela), próbujesz znaleźć łańcuchy pikseli, tak aby można było określić poprzedni i następny piksel krawędzi wzdłuż łańcucha.

(3) Użyj alternatywnego algorytmu wykrywania krawędzi, takiego jak rysowanie krawędzi.

Szczegóły można znaleźć na http://ceng.anadolu.edu.tr/cv/EdgeDrawing/

Niestety, nie jest to dostarczane po wyjęciu z pudełka z OpenCV, więc być może będziesz musiał znaleźć implementację gdzie indziej.


Przykładowy kod dla opcji # 1.

#include <stdint.h>
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
    Mat matInput = imread("~/Data/mA9EE.png", false);

    // ---- Preprocessing of depth map. (Optional.) ----

    GaussianBlur(matInput, matInput, cv::Size(9, 9), 4.0);

    // ---- Here, we use cv::threshold instead of cv::Canny as explained above ----

    Mat matEdge;

    //Canny(matInput, matEdge, 0.1, 1.0);

    threshold(matInput, matEdge, 192.0, 255.0, THRESH_BINARY_INV);

    // ---- Use findContours to find chains of consecutive edge pixels ----

    vector<vector<Point> > contours;
    findContours(matEdge, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    // ---- Code below is only used for visualizing the result. ----

    Mat matContour(matEdge.size(), CV_8UC1);

    for (size_t k = 0; k < contours.size(); ++k)
    {
        const vector<Point>& contour = contours[k];
        for (size_t k2 = 0; k2 < contour.size(); ++k2)
        {
            const Point& p = contour[k2];
            matContour.at<uint8_t>(p) = 255;
        }
    }

    imwrite("~/Data/output.png", matContour);
    cout << "Done!" << endl;
    return 0;
}
 34
Author: rwong,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:19