webentwicklung-frage-antwort-db.com.de

Mit opencv durch Pixel blättern

Wie kann ich ein Bild mit opencv durchlaufen, als wäre es ein 2D-Array, um die RGB-Werte jedes Pixels zu ermitteln? Wäre eine Matte für diese Operation auch einem Bild vorzuziehen?

24
a sandwhich

Wenn Sie C++ verwenden, verwenden Sie die C++ - Schnittstelle von opencv. Anschließend können Sie über http://docs.opencv.org/2.4/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html#the-efficient-way auf die Mitglieder zugreifen oder beispielsweise mit cv :: Mat :: at ().

10
etarion

cv::Mat wird gegenüber IplImage bevorzugt, da es Ihren Code vereinfacht

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;

Dies setzt voraus, dass Sie die RGB-Werte zusammen verwenden müssen. Andernfalls können Sie cv :: split verwenden, um jeden Kanal separat abzurufen. Siehe die Antwort von etarion für den Link mit Beispiel.

In meinen Fällen benötigen Sie einfach das Bild in Graustufen. Dann können Sie das Bild in Graustufen laden und als Array von Uchar darauf zugreifen.

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: Verwenden Sie split, um die 3 Kanäle zu erhalten

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: Dank der Entarion für das Erkennen des Fehlers, den ich beim Kopieren und Einfügen aus dem Beispiel cv :: Vec3b eingeführt hatte.

41
Dat Chu

Seit OpenCV 3.0 gibt es in cv :: Mat eine offizielle und schnellste Möglichkeit, die Funktion auf dem gesamten Pixel auszuführen.

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

Wenn Sie diese Funktion verwenden, wird der Vorgang automatisch auf Multi Core ausgeführt.

Offenlegung: Ich bin an dieser Funktion beteiligt. 

11
user3188838

Die Dokumente zeigen einen gut geschriebenen Vergleich verschiedener Möglichkeiten zum Durchlaufen eines Mattenbildes hier .

Der schnellste Weg ist die Verwendung von Zeigern im C-Stil. Hier ist der aus den Dokumenten kopierte Code:

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

Der Zugriff auf die Elemente mit dem at ist ziemlich langsam.

Wenn Ihre Operation mithilfe einer Nachschlagetabelle ausgeführt werden kann, ist die integrierte Funktions-LUT bei weitem die schnellste (auch in den Dokumenten beschrieben).

3
vipers36

Seit OpenCV 3.3 ( siehe changelog ) ist es auch möglich, den C++ 11-Stil für Schleifen zu verwenden:

// 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.

Wenn Sie RGB-Pixel nacheinander ändern möchten, hilft das folgende Beispiel!

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

Dies ist eine alte Frage, die jedoch aktualisiert werden muss, da opencv aktiv entwickelt wird. Kürzlich hat OpenCV parallel_for_ eingeführt, das den Lambda-Funktionen von C++ 11 entspricht. Hier ist das Beispiel

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

Dies ist erwähnenswert, dass diese Methode die CPU-Kerne in modernen Computerarchitekturen verwendet.

0
Davood Falahati