Nur gezeichnete Linien nummerieren



  • Ich stehe derzeit in meinem Code total auf dem Schlauch - kurze Erklärung was dieser überhaupt macht:
    Anhand eines OpenCV Algorithmus werden bestimmte Linien berechnet und gezeichnet. Jetzt basiert das Problem nun auf C++, denn ich will nur Linien haben, die voneinander >=12 entfernt sind. Alles schön und gut. Urspünglich habe ich allen gezeichneten Linien eine Art Label verpasst. Dieses Label kommt aus einem

    vector<int> wallNr
    

    und enthält beispielsweise die Nummern 8,9,10,11 etc. Dementsprechend wurde auch die Linie i mit wallNr[i - 1] gelabelt. Doch wie mache ich das nun bei Linien, die wirklich gezeichnet werden? Da ja alle <=12 verworfen werden, aber trotzdem noch ein Label kriegen. 😕
    Hier mein Code, wie ich es versuche zu machen:

    Vec4i current, previous;
    Point pt1, pt2, ppt1, ppt2;
    for (size_t i = 1; i < lines.size(); i++) {				
    
    	current = lines[i];						
    	pt1 = Point(current[0], current[1]);
    	pt2 = Point(current[2], current[3]);
    
    	previous = lines[i - 1];						
    	ppt1 = Point(previous[0], previous[1]);
    	ppt2 = Point(previous[2], previous[3]);
    
    	double distanceBetweenPoints = sqrt(pow(pt1.x - ppt1.x, 2)					///reject lines, which are to cloose to each other
    		+ pow(pt2.x - ppt2.x, 2));
    
    	if (distanceBetweenPoints >= 12) {
    
    		double angle = atan2(ppt2.y - ppt1.y, ppt2.x - ppt1.x) * 180.0 / CV_PI;		///draw only vertical lines (90 degree)
    		if (angle) {
    			line(cdst, pt1, pt2, Scalar(0, 0, 255), 2, CV_AA);
    			}
    
    		vector<Point> pt;
    		pt.push_back(Point(previous[2], previous[3]));	
       	        pt.push_back(Point(previous[0], previous[1]));
    		pt.push_back(Point(current[0], current[1]));
    		pt.push_back(Point(current[2], current[3]));
    
    		int label;
    		for (int j = 0; j < pt.size(); j++) {
    			label = wallNr[j];
    			cout << label << "\n";
    		}
    		cv::putText(cdst, to_string(label), pt2 + Point(0, 10), 4, 1, Scalar(0, 255, 0), 1, 8, false);
    
    		const Point* point = &pt[0];
    		int n = (int)pt.size();
    		polylines(src, &point, &n, 1, true, Scalar(0, 255, 0), 1, CV_AA);
    
    		Rect roi = boundingRect(pt);
    		contourRegion = src(roi);
    
    	}
    }
    

    Entschuldigt, wenn das eine zu banale Frage ist, ich bin (noch) kein Profi in C++. 🙄 Ich freue mich über konstruktive Anregungen, danke schonmal!



  • Hallo,

    du wirst wohl nicht drum herum kommen, die Sachen die du zeichnen willst zunächst in einer geeigneten Struktur aufzubreiten und dann erst dann zu zeichnen.

    Also irgend sowas wie ein vector von items, wobei item halt label, position, usw... enthält.



  • Wofür brauchst du dein Label denn? Eventuell labelst du einfach erst, nachdem du festgestellt hast, ob du die Linie nicht brauchst?

    Anstelle eines extra Vektors, würde ich dir aber zu sowas raten:

    std::map<int, Vec4i> labeldLines;
    

    Da du gesagt hast, dass du noch kein C++ "Experte" bist, ein paar Anmerkungen zu deinem Code:

    Durch Vektoren iteriert man in der Regel mit Iteratoren. Natürlich muss man bei dir auf die "previous" Sache rücksicht nehmen. Und Variablen deklariert man erst in dem Scope in dem man die braucht. Das ist (meiner Meinung nach) lesbarer und hilft dem Compiler beim optimieren:

    for (auto& previousIter = lines.begin(), currentIter = lines.begin() + 1, end = lines.end();  currentIter != end; ++previousIter, ++currentIter) {            
    
        auto current = *currentIter;                    
        auto pt1 = Point(currenIter[0], current[1]);
        auto pt2 = Point(current[2], current[3]);
    
        auto previous = *previousIter;                       
        auto ppt1 = Point(previous[0], previous[1]);
        auto ppt2 = Point(previous[2], previous[3]);
    

    Wenn du die Werte für einen Vektor schon hast, kannst du den auch direkt zu Begin initialisieren und die bereits erstellten Punkte verwenden (ohne die nochmal zu erstellen.

    vector<Point> pt{ppt2, ppt1, pt1, pt2};
    

    Edit: Fehler korrigiert

    Edit 3: Du gibst bei deiner Schleife mit der Ausgabe immer nur die ersten 4 Labels aus....


  • Mod

    double angle = atan2(ppt2.y - ppt1.y, ppt2.x - ppt1.x) * 180.0 / CV_PI;		///draw only vertical lines (90 degree)
    		if (angle) {
    			line(cdst, pt1, pt2, Scalar(0, 0, 255), 2, CV_AA);
    			}
    

    Das ist schwer zu lesen und generell solltest du transzendente Funktionen sparsam einsetzen, die sind nämlich in der Regel recht langsam. Die Multiplikation/Division ist nicht besonders sinnvoll, wenn am Ende sowieso nur mit 0 verglichen wird.
    Ausserdem passt der Kommentar nicht. Die Linie wird gezeichnet, wenn der Winkel ungleich 0 ist, also fast immer.
    Äquivalent wäre

    if (ppt2.y != ppt1.y || ppt2.x < ppt1.x) {
    			line(cdst, pt1, pt2, Scalar(0, 0, 255), 2, CV_AA);
    			}
    
    double distanceBetweenPoints = sqrt(pow(pt1.x - ppt1.x, 2)					///reject lines, which are to cloose to each other
    		+ pow(pt2.x - ppt2.x, 2));
    

    Das sieht auch seltsam und nicht so recht wie eine Metrik aus. Du berücksichtigst ja nur die x-Werte zweier Punktpaare.



  • @Jockelx Du meinst, ich sollte in einer Methode als return-Wert sozusagen die tatsächlich gezeichneten ausgeben und dann in einer weiteren Methode diese "benennen"? Okay, aber wie gebe ich denn nur die gezeichneten aus?

    @Schlangenmensch ich speichere später die Fläche zwischen den Linien ab. Das versuche ich eigentlich auch, aber ich komme leider nicht dahinter wie...
    Ach das ist ja super danke! Ich habe nämlich mal mit Java angefangen, deswegen programmiere ich auch sehr stark im "Java-Stil". Aber wieder mal was dazu gelernt. 🙂
    Ja, dass ich die ersten 4 ausgebe, weiß ich auch und versuche das zu fixen..

    @camper auch dir danke für die Anmerkung. Das mit den x-Werten passt so, nachdem ich nur vertikale Linien habe.



  • vickal93 schrieb:

    double distanceBetweenPoints = sqrt(pow(pt1.x - ppt1.x, 2)					///reject lines, which are to cloose to each other
    		+ pow(pt2.x - ppt2.x, 2));
    

    Lässt sich auch hervorragend durch die Standardbibliothek ersetzen.

    Siehe: http://en.cppreference.com/w/cpp/numeric/math/hypot


  • Mod

    Furble Wurble schrieb:

    vickal93 schrieb:

    double distanceBetweenPoints = sqrt(pow(pt1.x - ppt1.x, 2)					///reject lines, which are to cloose to each other
    		+ pow(pt2.x - ppt2.x, 2));
    

    Lässt sich auch hervorragend durch die Standardbibliothek ersetzen.

    Siehe: http://en.cppreference.com/w/cpp/numeric/math/hypot

    Das wäre doppelt gelogen.

    vickal93 schrieb:

    Das mit den x-Werten passt so, nachdem ich nur vertikale Linien habe.

    dann allerdings sind die Differenzen in beiden Summanden immer gleich, und du könntest gleich

    double distanceBetweenPoints = abs(pt1.x - ppt1.x)*sqrt(2);
    

    schreiben.



  • Nochwas:

    Du zeichnest jede Linie die einen Winkel != 0 und eine Distanz zum Vorgänger >= 12 mit der "lines" Funktion. Was machst du dann noch mit polylines? Zeichnet die Funktion nicht ein Polygon, vom ersten Punkt, über den zweiten und dritten bis zum vierten?

    Wenn du die "Labels" jeweils nur mit putText an die Linie schreiben möchtest, definier dir vor deiner ersten For Schleife einen Zähler, den du dann in dem if hochzählst. Und packe den Zähler in das "to_string()"



  • @camper Sehr guter Punkt, danke, werde ich so auch in meinem Code ändern.

    @Schlangenmnesch über die Linien werden diese Polygone gezeichnet, um all die Fächen, die dadurch entstehen, zu speichern.
    Naja, eigentlich sollen diese "Labels" nicht nur in puText rein (das dient lediglich zum Testen) sondern auch wirklich zu den Linien der Reihe nach zugeordnet werden.



  • Hm... jetzt sehe ich das "closes = true" 😉

    Du kannst dir auch ein vector<Vec4i> definieren und in deinem if, ein push_back darauf aufrufen. Dann hast du alle Linien für die die Distanz zum Vorgänger größer als zwölf ist in dem Vektor. Die Position in dem Vektor wäre quasi eine natürliche Wahl für ein Label.

    Achso, bei dem if(angle) ich glaube du willst die geschlossene Klammer weiter unten haben. Oder was willst du damit genau erreichen?


Anmelden zum Antworten