web-dev-qa-db-fra.com

Parcourez les pixels avec opencv

Comment pourrais-je parcourir une image en utilisant l'opencv comme s'il s'agissait d'un tableau 2D pour obtenir les valeurs rgb de chaque pixel? En outre, un tapis serait-il préférable à un iplimage pour cette opération?

24
a sandwhich

Si vous utilisez C++, utilisez l'interface C++ d'opencv et vous pourrez ensuite accéder aux membres via http://docs.opencv.org/2.4/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html#the-efficient-way ou en utilisant cv :: Mat :: at (), par exemple.

10
etarion

cv::Mat est préférable à IplImage car cela simplifie votre code

cv::Mat img = cv::imread("lenna.png");
for(int i=0; i<img.rows; i++)
    for(int j=0; j<img.cols; j++) 
        // You can now access the pixel value with cv::Vec3b
        std::cout << img.at<cv::Vec3b>(i,j)[0] << " " << img.at<cv::Vec3b>(i,j)[1] << " " << img.at<cv::Vec3b>(i,j)[2] << std::endl;

Cela suppose que vous deviez utiliser les valeurs RVB ensemble. Si vous ne le faites pas, vous pouvez utiliser cv :: split pour obtenir chaque canal séparément. Voir la réponse de etarion pour le lien avec exemple.

De plus, dans mon cas, vous avez simplement besoin d'une image en niveaux de gris. Ensuite, vous pouvez charger l'image en niveaux de gris et y accéder sous la forme d'un tableau d'uchar.

cv::Mat img = cv::imread("lenna.png",0);
for(int i=0; i<img.rows; i++)
    for(int j=0; j<img.cols; j++)
        std::cout << img.at<uchar>(i,j) << std::endl;

UPDATE: Utiliser split pour obtenir les 3 canaux

cv::Mat img = cv::imread("lenna.png");
std::vector<cv::Mat> three_channels = cv::split(img);

// Now I can access each channel separately
for(int i=0; i<img.rows; i++)
    for(int j=0; j<img.cols; j++)
        std::cout << three_channels[0].at<uchar>(i,j) << " " << three_channels[1].at<uchar>(i,j) << " " << three_channels[2].at<uchar>(i,j) << std::endl;

// Similarly for the other two channels

UPDATE: Merci à entarion d'avoir repéré l'erreur que j'ai introduite lors de la copie et du collage à partir de l'exemple cv :: Vec3b.

41
Dat Chu

Depuis OpenCV 3.0, il existe un moyen officiel et rapide d’exécuter la fonction sur tous les pixels de cv :: Mat.

void cv :: Mat :: forEach (Functor const et operation)

Si vous utilisez cette fonction, l'opération est automatiquement exécutée sur plusieurs coeurs.

Divulgation: Je contribue à cette fonctionnalité. 

11
user3188838

Les documents présentent une comparaison bien écrite de différentes manières de parcourir une image Mat/ here .

Le moyen le plus rapide consiste à utiliser des pointeurs de style C. Voici le code copié à partir de la documentation:

Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));

int channels = I.channels();

int nRows = I.rows;
int nCols = I.cols * channels;

if (I.isContinuous())
{
    nCols *= nRows;
    nRows = 1;
}

int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
    p = I.ptr<uchar>(i);
    for ( j = 0; j < nCols; ++j)
    {
        p[j] = table[p[j]];
    }
}
return I;
}

Accéder aux éléments avec le at est assez lent.

Notez que si votre opération peut être effectuée à l'aide d'une table de correspondance, la fonction intégrée LUT est de loin la plus rapide (également décrite dans la documentation).

3
vipers36

Depuis OpenCV 3.3 ( voir changelog ), il est également possible d’utiliser le style C++ 11 pour les boucles:

// Example 1
Mat_<Vec3b> img = imread("lena.jpg");
for( auto& pixel: img ) {
    pixel[0] = gamma_lut[pixel[0]];
    pixel[1] = gamma_lut[pixel[1]];
    pixel[2] = gamma_lut[pixel[2]];
}

// Example 2
Mat_<float> img2 = imread("float_image.exr", cv::IMREAD_UNCHANGED);
for(auto& p : img2) p *= 2;
2
Martin R.

Si vous souhaitez modifier les pixels RVB un par un, l'exemple ci-dessous vous aidera!

void LoopPixels(cv::Mat &img) {
    // Accept only char type matrices
    CV_Assert(img.depth() == CV_8U);

    // Get the channel count (3 = rgb, 4 = rgba, etc.)
    const int channels = img.channels();
    switch (channels) {
    case 1:
    {
        // Single colour
        cv::MatIterator_<uchar> it, end;
        for (it = img.begin<uchar>(), end = img.end<uchar>(); it != end; ++it)
            *it = 255;
        break;
    }
    case 3:
    {
        // RGB Color
        cv::MatIterator_<cv::Vec3b> it, end;
        for (it = img.begin<cv::Vec3b>(), end = img.end<cv::Vec3b>(); it != end; ++it) {
            uchar &r = (*it)[2];
            uchar &g = (*it)[1];
            uchar &b = (*it)[0];
            // Modify r, g, b values
            // E.g. r = 255; g = 0; b = 0;
        }
        break;
    }
    }
}
1
Acidic

Cette question est ancienne mais doit être mise à jour car opencv est activement développé. Récemment, OpenCV a introduit parallel_for_, qui est conforme aux fonctions c ++ 11 lambda. Voici l'exemple

parallel_for_(Range(0 , img.rows * img.cols), [&](const Range& range){
    for(int r = range.start; r<range.end; r++ )
    {
         int i = r / img.cols;
         int j = r % img.cols;
        img.ptr<uchar>(i)[j] = doSomethingWithPixel(img.at<uchar>(i,j));
    }
});

Il est à noter que cette méthode utilise les cœurs de processeur dans les architectures informatiques modernes.

0
Davood Falahati