c++ loops opencv pixel

c++ - Ciclo a través de píxeles con opencv



loops pixel (6)

¿Cómo podría recorrer una imagen usando opencv como si fuera una matriz 2d para obtener los valores rgb de cada píxel? Además, ¿sería preferible un tapete a un iplimage para esta operación?


Desde OpenCV 3.0, hay una forma oficial y más rápida de ejecutar la función en todo el píxel en cv :: Mat.

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

Si usa esta función, la operación se ejecuta en múltiples núcleos automáticamente.

Divulgación: soy colaborador de esta función.


Desde OpenCV 3.3 ( ver changelog ) también es posible usar el estilo de C ++ 11 para bucles:

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


Los documentos muestran una comparación bien escrita de diferentes formas de iterar sobre una imagen Mat here .

La forma más rápida es usar punteros estilo C. Aquí está el código copiado de los documentos:

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

Acceder a los elementos con at es bastante lento.

Tenga en cuenta que si su operación puede realizarse utilizando una tabla de búsqueda, la función incorporada LUT es, con mucho, la más rápida (también descrita en los documentos).



cv::Mat es preferido sobre IplImage porque simplifica su código

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;

Esto supone que necesita usar los valores RGB juntos. Si no lo hace, puede usar cv :: split para obtener cada canal por separado. Vea la respuesta de etarion para el enlace con el ejemplo.

Además, en mi caso, simplemente necesita la imagen en escala de grises. Luego, puede cargar la imagen en escala de grises y acceder a ella como una matriz de 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;

ACTUALIZACIÓN : uso de división para obtener los 3 canales

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

ACTUALIZACIÓN: Gracias a entarion por detectar el error que introduje al copiar y pegar desde el ejemplo de cv :: Vec3b.


Si desea modificar los píxeles RGB uno por uno, el siguiente ejemplo lo ayudará.

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