2009-04-23 12 views
1

Le problème est assez basique. (Je suis perplexe pourquoi la recherche n'a rien trouvé)Comment extraire avec élégance une région rectangulaire 2D à partir d'un vecteur C++

Je rectangulaire « image » qui stocke sa ligne de couleur du pixel après la ligne dans un std :: vecteur

Je veux copier rectangulaire région hors de cette image.

Comment est-ce que je pourrais le coder avec élégance en C++?

Mon premier essai:

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; 
} 

ne compile même pas .....

supp: Précision: Je sais comment faire "à la main". Ce n'est pas un problème en tant que tel. Mais j'aimerais beaucoup la magie de l'itérateur C++ stl qui fait la même chose, mais plus vite et ... plus C++ stylé.

Addition: Je donne à l'algorithme le pictureDataVector, la largeur et la hauteur de l'image et un rectangle indiquant la région que je veux copier hors de l'image. La valeur de retour doit être un nouveau vecteur avec le contenu du rectangle. Pensez-y comme l'ouverture de votre éditeur d'image favori, et copiez une région rectangulaire à partir de cela. L'image est stockée sous la forme d'un long tableau 1D (vecteur) de pixels.

+0

Neil vous êtes un épargnant de vie :) écrire un commentaire afin que je puisse vous +1 à un :) – AndreasT

+0

Précision: Neil juste sauvé mon (merdique) exemple de code :) Toujours pas sûr de ce que je fait de mal ... mais que diable ... – AndreasT

+2

Vous avez essayé d'utiliser HTML - ne faites pas ça. Au lieu de cela, sélectionnez tout le code avec la souris et tapez Ctrl-K. –

Répondre

2

Votre question demande une méthode C++ pour copier un champ rectangulaire d'éléments dans un conteneur. Vous avez un exemple assez proche de le faire et obtiendrez plus dans les réponses. Généralisons, cependant:

Vous voulez un itérateur qui parcourt une plage rectangulaire d'éléments sur une gamme d'éléments. Alors, que diriez-vous écrire une sorte d'adaptateur qui se trouve sur un conteneur et fournit cet itérateur spécial.

gonna go grands traits avec le code ici:

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); 

Je suis sûr que cela est tout à fait faisable, mais je ne suis pas à l'aise de codage des trucs de modèle de style stl off-the-brassard. Si j'ai un peu de temps et que cela vous intéresse, je pourrai rééditer un brouillon plus tard, car c'est un concept intéressant.

Voir ce SO question's answer pour l'inspiration.

+0

Cela semble intéressant! un itérateur avec une foulée! Simple. Beau. J'aime vraiment ça! – AndreasT

+0

Bien que ce ne soit pas exactement ce que vous avez proposé dans votre exemple, vous avez déclenché la cellule du cerveau droit. THX! – 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

Je voudrais utiliser les algorithmes stl. Je ne suis même pas sûr si l'indexation 2d que vous faites fonctionne sur un vecteur <> – AndreasT

+0

L'indexation double est une notation de tableau de tableaux C++ standard pour traiter des informations multidimensionnelles. Je doute que STL vous aidera avec ce problème. Notez que le tableau source est présumé unique comme un vecteur. Vous pouvez faire le même calcul pour utiliser un tableau de sortie à une seule dimension. –

2

Bon code C++ doit d'abord être facile à lire et à comprendre (comme un code), orienté objet (comme un code dans un langage orienté objet) et doivent utiliser les facilités linguistiques pour simplifier la mise en œuvre. Je ne m'inquiéterais pas d'utiliser des algorithmes STL pour le rendre plus C++ - ish, il vaudrait beaucoup mieux commencer à simplifier la convivialité (interface) d'une manière orientée objet. N'utilisez pas de vecteurs simples externes pour représenter vos images. Fournissez un niveau de abstraction: créez une classe qui représente l'image et fournissez la fonctionnalité dont vous avez besoin. Cela améliorera la facilité d'utilisation par encapsulant détails de l'utilisation régulière (l'objet zone 2D peut connaître ses dimensions, l'utilisateur n'a pas besoin de les passer en arguments). Et cela rendra le code plus robuste que l'utilisateur peut faire moins d'erreurs.

Même si vous utilisez des conteneurs STL, tenez toujours compte de la lisibilité en premier. S'il est plus simple à implémenter en terme de boucle régulière et qu'il sera plus difficile à lire avec les algorithmes STL, oubliez-les: faites votre code simple et maintenable.

Cela devrait être votre objectif: rendre le code meilleur, plus simple et plus lisible. Utilisez les fonctionnalités de langue pour améliorer votre code, pas votre code pour exercer ou montrer les fonctionnalités dans la langue. Ce sera payant si vous avez besoin de maintenir ce code dans deux mois.

Note: L'utilisation de plus de STL ne rendra pas votre code plus idiomatique en C++, et je crois que c'est l'un de ces cas. Abuser STL peut rendre le code pire.

+0

+1 parce que je suis plutôt d'accord, mais écrire des algorithmes qui fonctionnent sur les itérateurs plutôt que sur les collections est en quelque sorte plus idiomatique du C++, donc je ne pense pas que le questionneur aboie complètement dans le mauvais arbre. –

+0

Je pense que vous manquez le point. Il y a certaines choses que vous pouvez faire de manière extrêmement élégante et générique en C++ avec les itérateurs et les algorithmes stl. Comme c'était une chose assez simple: copier des pièces avec des distances régulières sur un conteneur, je pensais qu'il devait y avoir une bonne façon générique de le faire. – AndreasT

+0

(de retour de vacances): @onebyone Je ne peux pas être d'accord avec vous en ce sens que "les algorithmes d'écriture qui fonctionnent sur les itérateurs ..." s'applique ici. Premièrement: il s'agit d'un détail d'implémentation, qui ne fait pas partie de l'interface, de sorte que les utilisateurs ne s'en apercevront même pas.Deuxièmement, comme dans votre exemple de code, même s'il semble avoir des itérateurs, je ne pense pas que ça aide. Quel concept d'itérateur est requis par l'interface? Les exigences vont au-delà de ce qu'offrent les itérateurs: ce n'est pas simplement un itérateur, mais un itérateur dans un conteneur qui représente une structure de données 2D d'une largeur particulière (foulée) ... –

3

Fondamentalement, la même idée, sauf qu'il compile et est un peu plus 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")); 
} 

je ne voudrais pas attendre que ce soit plus vite que deux boucles de copie par index. Probablement plus lent, même s'il s'agit essentiellement d'une course entre le overhead de vector :: push_back et le gain de std :: copy sur une boucle.

Toutefois, il se peut que ce soit plus flexible si votre autre code de modèle est conçu pour fonctionner avec les itérateurs en général, plutôt que comme un conteneur spécifique. copyRectFromBiggerRect peut utiliser un tableau, une deque ou même une liste en tant qu'entrée tout aussi facilement qu'un vecteur, bien qu'il ne soit pas optimal pour les itérateurs qui ne sont pas random-access, car il avance deux fois sur chaque ligne copiée. Pour d'autres façons de faire cela plus comme un autre code C++, pensez à boost :: multi_array pour les tableaux multidimensionnels (auquel cas l'implémentation sera complètement différente de ceci), et évitez de retourner des collections comme vecteur par valeur (Premièrement, il peut être inefficace si vous n'obtenez pas l'optimisation de la valeur de retour, et deuxièmement, de sorte que le contrôle sur les ressources allouées est laissé au plus haut niveau possible).