2009-04-23 20 views
1

extrahieren Das Problem ist ziemlich einfach. (Ich bin verwirrt, warum die Suche nichts gefunden)Howto elegant eine 2D-Rechteck-Region aus einem C++ - Vektor

ich ein rechteckiges „Bild“, die es Pixelfarbe Linie nach Zeile in einem std :: vector

ich kopieren mag einen rechteckiges speichern Region aus diesem Bild heraus.

Wie würde ich das elegant in C++ programmieren?

Mein erster Versuch:

template <class T> std::vector<T> copyRectFromVector(const std::vector<T>& vec, std::size_t startx, std::size_t starty, std::size_t endx, std::size_t endy, std::size_t fieldWidth, std::size_t fieldHeight) 
    { 
    using namespace std; 
    vector<T> ret((endx-startx)*(endy-starty)+10); // 10: chickenfactor 

    // checks if the given parameters make sense: 
    if (vec.size() < fieldWidth*endy) 
    { 
     cerr << "Error: CopyRectFromVector: vector to small to contain rectangular region!" << std::endl; 
     return ret; 
    } 

    // do the copying line by line: 
    vector<T>::const_iterator vecIt = vec.begin(); 
    vector<T>::forward_iterator retIt = ret.end(); 

    vecIt += startx + (starty*fieldWidth); 
    for(int i=starty; i < endy; ++i) 
    { 
      std::copy(vecIt, vecIt + endx - startx, retIt); 
     } 
     return ret; 
} 

nicht einmal kompilieren .....

Addit: Klarstellung: Ich weiß, wie diese "von Hand" zu tun. Es ist kein Problem als solches. Aber ich würde gerne einige C++ stl Iterator Magie, die das gleiche tut, aber schneller und ... mehr C++ stilvoll.

Zusatz: Ich gebe dem Algorithmus den imageDataVector, die Breite und Höhe des Bildes und ein Rechteck, das die Region angibt, die ich aus dem Bild kopieren möchte. Der Rückgabewert sollte ein neuer Vektor mit dem Inhalt des Rechtecks ​​sein.

Betrachten Sie es als Öffnen Sie Ihre Lieblings-Bildbearbeitung, und kopieren Sie eine rechteckige Region daraus. Das Bild wird als langes 1D-Array (Vektor) von Pixelfarben gespeichert.

+0

Neil Sie sind ein Lebensretter :) Schreiben Sie einen Kommentar, so dass ich Ihnen ein :) – AndreasT

+0

Klärung +1 kann: Neil my (crappy) Beispielcode nur gerettet :) Immer noch nicht sicher, was ich falsch gemacht haben ... aber was zur Hölle ... – AndreasT

+2

Sie haben versucht, HTML zu verwenden - tun Sie das nicht. Wählen Sie stattdessen den gesamten Code mit der Maus aus, und geben Sie Strg-K ein. –

Antwort

2

Ihre Frage fragt nach einer C++ - Methode zum Kopieren eines rechteckigen Felds von Elementen in einem Container. Sie haben ein ziemlich genaues Beispiel dafür und werden mehr Antworten erhalten. Lassen Sie uns jedoch verallgemeinern:

Sie möchten einen Iterator, der einen rechteckigen Bereich von Elementen über einen Bereich von Elementen reist. Wie wäre es, wenn Sie eine Art Adapter schreiben, der auf einem beliebigen Container sitzt und diesen speziellen Iterator bereitstellt?

gehend geht breite Striche mit dem Code hier:

vector<pixels> my_picture; 
point selTopLeft(10,10), selBotRight(40, 50); 
int picWidth(640), picHeight(480); 
rectangular_selection<vector<pixels> > selection1(my_picture.begin(), 
    my_picture.end(), picWidth, picHeight, selTopLeft, selBotRight); 

// Now you can use stl algorithms on your rectangular range 
vector<pixels> rect_copy = std::copy(selection1.begin(), selection1.end()); 
// or maybe you don't want to copy, you want 
// to modify the selection in place 
std::for_each (selection1.begin(), selection1.end(), invert_color); 

Ich bin sicher, dies ist völlig tun können, aber ich bin nicht bequem Codierung stl-Stilvorlage Sachen off-the-Manschette. Wenn ich etwas Zeit habe und Sie interessiert sind, kann ich später einen groben Entwurf neu bearbeiten, da dies ein interessantes Konzept ist.

Siehe diese SO question's answer für Inspiration.

+0

Das hört sich interessant an! ein Iterator mit einem Schritt! Einfach. Schön. Ich mag es wirklich! – AndreasT

+0

Obwohl es nicht genau das ist, was Sie in Ihrem Beispiel vorgeschlagen haben, haben Sie die rechte Gehirnzelle ausgelöst. Danke! – AndreasT

3
for (int r = startRow; r < endRow; r++) 
    for (int c = startCol; c < endCol; c++) 
     rect[r-startRow][c-startCol] = source[r*rowWidth+c]; 
+0

Ich würde gerne die STL-Algorithmen verwenden. Ich bin nicht einmal sicher, ob die 2d-Indizierung auf einem Vektor funktioniert. <> – AndreasT

+0

Die doppelte Indizierung ist die Standard-C++ -Array-of-Arrays-Notation für den Umgang mit mehrdimensionalen Informationen. Ich bezweifle, dass STL Ihnen bei diesem Problem helfen wird. Beachten Sie, dass das Quell-Array als einfach dimensioniert wie ein Vektor angenommen wird. Sie könnten die gleiche Mathematik verwenden, um ein eindimensionales Ausgabearray zu verwenden. –

2

Güter C++ Code muss zunächst leicht zu lesen und zu verstehen (wie jeder Code), objektorientierte (wie jeder Code in einer objektorientierten Sprache) und dann die Sprache Einrichtungen verwenden, um die Implementierung zu vereinfachen.

Ich würde mir keine Sorgen über die Verwendung von STL-Algorithmen machen, um es mehr C++ - ish, es wäre viel besser, anfangen, die Benutzerfreundlichkeit (Schnittstelle) in einer objektorientierten Weise zu vereinfachen. Verwenden Sie keine einfachen Vektoren extern, um Ihre Bilder darzustellen. Stellen Sie eine Ebene von Abstraktion: Stellen Sie eine Klasse, die das Bild darstellt und bieten die Funktionalität, die Sie dort brauchen. Das verbessert die Benutzerfreundlichkeit durch Einkapseln Details aus der regulären Verwendung (das 2D-Flächenobjekt kann seine Dimensionen kennen, der Benutzer muss sie nicht als Argumente übergeben). Und das macht den Code mehr robust, wie der Benutzer weniger Fehler machen kann.

Auch wenn Sie STL-Container verwenden, denken Sie immer zuerst an Lesbarkeit. Wenn es einfacher ist, es mit einer regulären for-Schleife zu implementieren, und es mit STL-Algorithmen schwerer zu lesen ist, dann vergiss es: mach deinen Code einfach und wartbar.

Das sollte Ihr Fokus sein: besser, einfacher, lesbarer Code. Verwenden Sie Sprachfeatures, um Ihren Code zu verbessern, nicht Ihren Code, um die Funktionen in der Sprache auszuüben oder vorzuzeigen. Es wird sich auszahlen, wenn Sie diesen Code in zwei Monaten pflegen müssen.

Hinweis: Die Verwendung von mehr STL wird Ihren Code in C++ nicht idiomatischer machen, und ich glaube, das ist einer dieser Fälle. Wenn Sie STL missbrauchen, kann der Code tatsächlich schlechter werden.

+0

+1, da stimme ich größtenteils zu, aber das Schreiben von Algorithmen, die an Iteratoren anstatt an Sammlungen arbeiten, ist in gewissem Sinne idiomatisch C++, also glaube ich nicht, dass der Fragesteller vollständig den falschen Baum bellt. –

+0

Ich denke, du verpasst den Punkt. Es gibt einige Dinge, die Sie sehr elegant und generisch in C++ mit den STI-Iteratoren und -Algorithmen tun können. Da es eine ziemlich einfache Sache war: Stücke mit regelmäßigen Abständen aus einem Container zu kopieren, dachte ich, dass es einen guten generischen Weg geben muss. – AndreasT

+0

(zurück von Feiertagen): @onebyone Ich kann Ihnen nicht zustimmen, dass 'Schreibalgorithmen, die an Iteratoren arbeiten' gilt. Erstens: Es handelt sich um ein Implementierungsdetail, das nicht Teil der Schnittstelle ist, sodass die Benutzer es nicht bemerken.Zweitens, wie in Ihrem Codebeispiel, auch wenn es scheint, da es Iteratoren hat, glaube ich nicht, dass es hilft. Welches Iteratorkonzept wird von der Schnittstelle benötigt? Die Anforderungen gehen über das hinaus, was Iteratoren anbieten: Es ist nicht nur ein Iterator, sondern ein Iterator in einen Container, der eine 2D-Datenstruktur einer bestimmten Breite (Schrittweite) darstellt ... –

3

Im Grunde die gleiche Idee, mit der Ausnahme, dass es kompiliert und ist ein bisschen mehr iteratory:

#include <vector> 
#include <algorithm> 
#include <iostream> 
#include <iterator> 

template <typename I, typename O> 
void copyRectFromBiggerRect(
    I input, 
    O output, 
    std::size_t startx, 
    std::size_t cols, 
    std::size_t starty, 
    std::size_t rows, 
    std::size_t stride 
) { 
    std::advance(input, starty*stride + startx); 
    while(rows--) { 
     std::copy(input, input+cols, output); 
     std::advance(input, stride); 
    } 
} 

template<typename T> 
std::vector<T> copyRectFromVector (
    const std::vector<T> &vec, 
    std::size_t startx, 
    std::size_t starty, 
    std::size_t endx, 
    std::size_t endy, 
    std::size_t stride 
) { 
    // parameter-checking omitted: you could also check endx > startx etc. 

    const std::size_t cols = endx - startx; 
    const std::size_t rows = endy - starty; 

    std::vector<T> ret; 
    ret.reserve(rows*cols); 
    std::back_insert_iterator<std::vector<T> > output(ret); 

    typename std::vector<T>::const_iterator input = vec.begin(); 
    copyRectFromBiggerRect(input,output,startx,cols,starty,rows,stride); 
    return ret; 
} 

int main() { 
    std::vector<int> v(20); 
    for (int i = 0; i < 20; ++i) v[i] = i; 
    std::vector<int> v2 = copyRectFromVector(v, 0, 0, 1, 2, 4); 
    std::copy(v2.begin(), v2.end(), std::ostream_iterator<int>(std::cout, "\n")); 
} 

würde ich nicht erwarten, dass diese als zwei Schleifen durch den Index Kopieren schneller sein. Wahrscheinlich langsamer, sogar, obwohl es im Grunde ein Wettlauf zwischen Overhead von vector :: push_back und dem Gewinn von std :: copy über eine Schleife ist.

Es könnte jedoch flexibler sein, wenn Ihr anderer Vorlagencode so ausgelegt ist, dass er mit Iteratoren im Allgemeinen und nicht mit Vektor als spezifischem Container arbeitet. copyRectFromBiggerRect kann ein Array, eine Deque oder sogar eine Liste genauso einfach als Vektor eingeben, obwohl es momentan nicht optimal für Iteratoren ist, die keinen wahlfreien Zugriff haben, da es derzeit zweimal durch jede kopierte Zeile vorrückt. Für andere Möglichkeiten, dies mehr wie anderen C++ - Code zu machen, betrachten Sie boost :: multi_array für mehrdimensionale Arrays (in diesem Fall würde die Implementierung von diesem völlig anders sein), und vermeiden Sie die Rückgabe von Sammlungen wie Vektor nach Wert (Erstens kann es ineffizient sein, wenn Sie die Rückgabewert-Optimierung nicht erhalten, und zweitens, damit die Kontrolle darüber, welche Ressourcen zugewiesen werden, auf der höchstmöglichen Ebene bleibt).

Verwandte Themen