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?
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 ().
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.
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.
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).
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;
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;
}
}
}
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.