OpenCV Как обращаться с Mat в качестве указателя для ускорения кода? - PullRequest
0 голосов
/ 07 ноября 2019

Я пытаюсь использовать указатель с cv :: Mat, но я не совсем понимаю это.

Когда я пытаюсь это сделать:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
    Mat src = imread("image.png");
    Mat img;
    Mat temp;

    img = Mat(src.rows, src.cols, CV_8UC1, cv::Scalar(0));
    temp = Mat(src.rows, src.cols, CV_8UC1, cv::Scalar(0));

    temp = img(Range(10, 20), Range(40, 60));
    temp.setTo(255);

    imshow("img", img);
    waitKey();
    return 0;
}

Это работает, и нетпроблема. Однако, когда я изменяю его на:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
    Mat src = imread("image.png");
    Mat* img;
    Mat* temp;

    *img = Mat(src.rows, src.cols, CV_8UC1, cv::Scalar(0));
    *temp = Mat(src.rows, src.cols, CV_8UC1, cv::Scalar(0));

    temp = img(Range(10, 20), Range(40, 60));
    temp.setTo(255);

    imshow("img", *img);
    waitKey();
    return 0;
}

, я получаю эту ошибку:

выражение, предшествующее круглым скобкам видимого вызова, должно иметь (указатель на) тип функции

в

temp = img(Range(10, 20), Range(40, 60));

и ошибка:

выражение должно иметь тип класса

в

temp.setTo(255);

Каково общее правило при работе с Mat s в качестве указателей для ускорения кода?

Я знаю, например, что в аргументах функции мы используем & для ввода Mats и* для выходных матов. Но есть ли общее правило, как определять и использовать Маты внутри функций?

Скажите, пожалуйста, есть ли другие проблемы с этим кодом, так как я новичок. Спасибо!

1 Ответ

2 голосов
/ 07 ноября 2019

В примере, который вы разместили, вы не получите никакой выгоды от использования указателей. В вашем примере с указателями есть ряд проблем.

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
    Mat src = imread("image.png");
    Mat* img; // Uninitialized pointer; points to random memory
    Mat* temp; // Uninitialized pointer; points to random memory

    // Undefined behavior: dereferencing an uninitialized pointer
    // You are basically trying to treat some random piece of memory
    // as a cv::Mat and trying to assign another cv::Mat to it.
    *img = Mat(src.rows, src.cols, CV_8UC1, cv::Scalar(0)); 
    *temp = Mat(src.rows, src.cols, CV_8UC1, cv::Scalar(0));

    // Syntax error: img has type Mat*; you could call the
    // Mat Mat::operator()( Range _rowRange, Range _colRange ) const
    // Like this:
    // *temp = img->operator()(Range(10, 20), Range(40, 60));
    // or like this:
    // *temp = (*img)(Range(10, 20), Range(40, 60));
    // that would work if img and temp were to point to valid cv::Mats
    temp = img(Range(10, 20), Range(40, 60));
    // Syntax error temp has type Mat*
    //  to access a pointers members use -> instead of .
    temp.setTo(255);

    imshow("img", *img);
    waitKey();
    return 0;
}

В общем случае копирование cv :: Mat - это дешевая операция, поскольку она не создает копию всего буфера, а просто увеличиваетref count и копирует некоторую информацию о том, как интерпретировать этот буфер. На типичном оборудовании вы можете ожидать, что он займет порядка нескольких десятков наносекунд максимум. Простые операции обработки изображений могут легко занять миллион раз.

Редко есть причина иметь указатель на cv :: Mat. Если вы переключаетесь на указатель, сделайте это, потому что это имеет больше смысла, а не в целях повышения производительности. Передача ваших матов по (const) ссылке, а не по значению, все равно может быть правильным выбором по умолчанию.

Один из вариантов использования указателя cv :: Mat может быть необязательным параметром out:

 void mayBeNull(cv::Mat* matPointer = nullptr)
 {
    if(matPointer!=nullptr)
    {
       // assign something to *matPointer
    }
    else
    {
       // do not use matPointer
       // the caller does not care about our outparam
    }
 }
...