Как эффективно извлечь определенные пиксели из изображения cv :: Mat в C ++? - PullRequest
0 голосов
/ 08 июля 2019

Я выполняю некоторую обработку изображения и хотел бы извлечь определенные значения пикселей из полутонового изображения. Пиксели, которые я хочу извлечь, описываются массивом маски, который имеет те же размеры, что и изображение в оттенках серого.

Это очень легко сделать в python, используя массивы numpy. Пример:

pixels = img[mask != 0]

Может кто-нибудь предложить мне, как сделать это эффективным способом в C ++, используя тип данных opencv cv :: Mat?

Обновление

Я приведу более обширный пример, чтобы прояснить мою проблему. Предположим, у меня есть серое изображение с именем img с размерами (3,4). У меня также есть массив mask с размерами (3,4). Я хочу извлечь значения из массива img в позиции, которые соответствуют положению ненулевых значений в массиве mask . Если мы предположим, что массив mask имеет 4 ненулевых элемента, то 4 элемента из массива img необходимо извлечь (скопировать) в новый массив с именем пикселей,

img = np.arange(12).reshape((3,4))
# img = array([[ 0,  1,  2,  3],
#              [ 4,  5,  6,  7],
#              [ 8,  9, 10, 11]])

mask = np.zeros_like(img)
mask[0:2, 1] = 255
mask[1, 2:4] = 255
# mask = array([[  0, 255,   0,   0],
#               [  0, 255, 255, 255],
#               [  0,   0,   0,   0]])

pixels = img[mask != 0]
# pixels = array([1, 5, 6, 7])

Я хочу реализовать ту же функциональность в C ++, используя массивы cv :: Mat. Я понимаю, что это можно сделать с помощью циклов for, но я бы предпочел более эффективное (векторизованное) решение, если оно вообще существует.

1 Ответ

0 голосов
/ 08 июля 2019

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

srcImage.copyTo(dstImage, mask);

Теперь вы можете создать функцию для работы с пикселями:

//Your function
void doSomething(cv::Point3_<uint8_t> &pixel)
{
    //... in this example you can change value like this: pixel.x = 255 - x means first color channel
}

Теперь, когда вы выполняете итерацию, вы должны проверить, равен ли пиксель нулю. В C ++ Вы можете выполнять итерации несколькими способами:

// .at method: 
// Loop over all rows
for (int r = 0; r < dstImage.rows; r++)
{
    // Loop over all columns
    for (int c = 0; c < dstImage.cols; c++)
    {
        // Obtain pixel
        Point3_<uint8_t> pixel = dstImage.at<Point3_<uint8_t>>(r, c);
        // check if values are zero
        if (pixel.x !=0 && pixel.y !=0 && pixel.z !=0)
        // function
             doSomething(pixel);
        // set result
        dstImage.at<Point3_<uint8_t>>(r, c) = pixel;
    }

}


//with pointers  
// Get pointer to first pixel
Point3_<uint8_t>* pixel = dstImage.ptr<Point3_<uint8_t>>(0, 0);
const Point3_<uint8_t>* endPixel = pixel + dstImage.cols * dstImage.rows;
// Loop over all pixels
for (; pixel != endPixel; pixel++)
{
    // check if values are zero
    if (pixel.x !=0 && pixel.y !=0 && pixel.z !=0)
          doSomething(*pixel);
}


//forEach - utilizes all the cores to apply any function at every pixel - the fastest way
//define Functor
struct Operator
{
    void operator ()(Point3_<uint8_t> &pixel, const int * position) const
    {           
          // check if values are zero
          if (pixel.x !=0 && pixel.y !=0 && pixel.z !=0)
                doSomething(pixel);
    }
};
//execute functor
dstImage.forEach<Point3_<uint8_t>>(Operator());

Это сработает, если на эталонном изображении нет нулевых значений перед наложением маски на него. Если они есть, вы должны перебирать изображение маски с помощью forEach. Затем Вы можете использовать const int * position аргумент int x = position[0]; int y = position[1];, чтобы проверить, в каких пикселях маски координат равны 0, и только для них сделать что-то с опорным изображением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...