C++:OpenCV:快速像素迭代

14

我正在尝试从实时网络摄像头图像中获取BGR值。在嵌套的for循环中,我由于没有正确使用指针而导致内存访问冲突,但是我不知道语法应该是什么。我找不到特定于我要执行的这个看似基本任务的足够详细的文档。

除了解决内存访问冲突外,我还想能够实时编辑每个像素,而不必进行深层复制,但我也不知道语法应该是什么。

这是目前我的代码:

int main(int argc, char** argv)
{

    int c;
    Mat img;
    VideoCapture capture(0);
    namedWindow("mainWin", CV_WINDOW_AUTOSIZE);
    bool readOk = true;

    while (capture.isOpened()) {

        readOk = capture.read(img);

        // make sure we grabbed the frame successfully 
        if (!readOk) {
            std::cout << "No frame" << std::endl;
            break;
        }

        int nChannels = img.channels();
        int nRows = img.rows;
        int nCols = img.cols * nChannels;

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

        int i, j;
        uchar r, g, b;
        for (i = 0; i < nRows; ++i)
        {
            for (j = 0; j < nCols; ++j)
            {
                r = img.ptr<uchar>(i)[nChannels*j + 2];
                g = img.ptr<uchar>(i)[nChannels*j + 1];
                b = img.ptr<uchar>(i)[nChannels*j + 0];
            }
        }

        if (!img.empty()) imshow("mainWin", img);
        c = waitKey(10);
        if (c == 27)
            break;
    }
}

1
这篇来自OpenCV文档的教程介绍了几种图像扫描方法,并提供了关于最快方法的信息。http://docs.opencv.org/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html - Jablonski
1个回答

34
你的扫描循环不正确。每行只应该获取一次指向行的指针。 由于像素是3个字节,最好将它们视为Vec3b。
你应该有类似这样的代码:
    uchar r, g, b;
    for (int i = 0; i < img.rows; ++i)
    {
        cv::Vec3b* pixel = img.ptr<cv::Vec3b>(i); // point to first pixel in row
        for (int j = 0; j < img.cols; ++j)
        {
            r = pixel[j][2];
            g = pixel[j][1];
            b = pixel[j][0];
        }
    }

或者

    uchar r, g, b;
    for (int i = 0; i < img.rows; ++i)
    {
        uchar* pixel = img.ptr<uchar>(i);  // point to first color in row
        for (int j = 0; j < img.cols; ++j)
        {
            b = *pixel++;
            g = *pixel++;
            r = *pixel++;
        }
    }

注意

在像这样顺序访问像素时,经常会看到使用Mat::at()

    // DON'T DO THIS!
    uchar r, g, b;
    for (int i = 0; i < img.rows; ++i)
    {
        for (int j = 0; j < img.cols; ++j)
        {
            cv::Vec3b pixel = img.at<cv::Vec3b>(i, j); 
            r = pixel[2];
            g = pixel[1];
            b = pixel[0];
        }
    }

然而,这种用法是不合适的。对于每个像素访问,at() 需要通过乘以行号和行长度来计算索引,在整个图像中,这种计算可能导致处理时间比上面的代码慢得多(其中 ptr() 每行执行一次等效的计算)。此外,在调试模式下,at() 有一个断言,会使它变得更慢。
如果你确定行之间没有填充,则可以通过消除对 ptr() 的调用来加快速度。在这种情况下,第二个循环中的像素指针将在每行结束后指向下一行的开头。但如果你的 Mat 是另一个 Mat 的某个区域,则这种方法无效。
另一方面,如果你是随机访问像素,而不是像上面那样顺序扫描,则使用 at() 就非常适合。

1
有没有不使用 Mat::at() 的理由? - a-Jays
2
@a-Jays 如果你使用 at(),对于每个像素来说,都会有一个乘法操作 row_number x row_length;在整个图像上,这可能会使速度比上述方案慢得多,因为它只在每行中获取一次相同的结果。此外,在调试模式下,at() 还有一个断言,使其变得更慢。另一方面,如果你以随机方式访问像素,而不是像上面那样顺序扫描,那么使用 at() 是合适的。 - Bull
1
太棒了!考虑将此注释添加到答案中。 - a-Jays

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接