Как получить пиксель изображения в реальных местах в opencv?

Я хочу получить rgb пикселя в изображении.  Но местоположение не целое, а реальные значения (x, y).  Я хочу билинейное интерполированное значение.  Как я могу сделать это opencv?

Спасибо большое

Ответы

Ответ 1

Нет простой функции для доступа к подпикселю, но я могу предложить вам несколько вариантов:

  • Используйте getRectSubPix и извлеките область с 1 пикселем:

    cv::Vec3b getColorSubpix(const cv::Mat& img, cv::Point2f pt)
    {
        cv::Mat patch;
        cv::getRectSubPix(img, cv::Size(1,1), pt, patch);
        return patch.at<cv::Vec3b>(0,0);
    }
    
  • Используйте более гибкую, но менее точную remap с однопиксельной картой:

    cv::Vec3b getColorSubpix(const cv::Mat& img, cv::Point2f pt)
    {
        cv::Mat patch;
        cv::remap(img, patch, cv::Mat(1, 1, CV_32FC2, &pt), cv::noArray(),
            cv::INTER_LINEAR, cv::BORDER_REFLECT_101);
        return patch.at<cv::Vec3b>(0,0);
    }
    
  • Реализуйте билинейную интерполяцию самостоятельно, так как это не ракетостроение:

    cv::Vec3b getColorSubpix(const cv::Mat& img, cv::Point2f pt)
    {
        assert(!img.empty());
        assert(img.channels() == 3);
    
        int x = (int)pt.x;
        int y = (int)pt.y;
    
        int x0 = cv::borderInterpolate(x,   img.cols, cv::BORDER_REFLECT_101);
        int x1 = cv::borderInterpolate(x+1, img.cols, cv::BORDER_REFLECT_101);
        int y0 = cv::borderInterpolate(y,   img.rows, cv::BORDER_REFLECT_101);
        int y1 = cv::borderInterpolate(y+1, img.rows, cv::BORDER_REFLECT_101);
    
        float a = pt.x - (float)x;
        float c = pt.y - (float)y;
    
        uchar b = (uchar)cvRound((img.at<cv::Vec3b>(y0, x0)[0] * (1.f - a) + img.at<cv::Vec3b>(y0, x1)[0] * a) * (1.f - c)
                               + (img.at<cv::Vec3b>(y1, x0)[0] * (1.f - a) + img.at<cv::Vec3b>(y1, x1)[0] * a) * c);
        uchar g = (uchar)cvRound((img.at<cv::Vec3b>(y0, x0)[1] * (1.f - a) + img.at<cv::Vec3b>(y0, x1)[1] * a) * (1.f - c)
                               + (img.at<cv::Vec3b>(y1, x0)[1] * (1.f - a) + img.at<cv::Vec3b>(y1, x1)[1] * a) * c);
        uchar r = (uchar)cvRound((img.at<cv::Vec3b>(y0, x0)[2] * (1.f - a) + img.at<cv::Vec3b>(y0, x1)[2] * a) * (1.f - c)
                               + (img.at<cv::Vec3b>(y1, x0)[2] * (1.f - a) + img.at<cv::Vec3b>(y1, x1)[2] * a) * c);
    
        return cv::Vec3b(b, g, r);
    }
    

Ответ 2

билинейная интерполяция просто означает взвешивание значения на основе 4 ближайших пикселей к тому, которое вы изучаете. Весы можно рассчитать следующим образом.

cv::Point2f current_pos; //assuming current_pos is where you are in the image

//bilinear interpolation
float dx = current_pos.x-(int)current_pos.x;
float dy = current_pos.y-(int)current_pos.y;

float weight_tl = (1.0 - dx) * (1.0 - dy);
float weight_tr = (dx)       * (1.0 - dy);
float weight_bl = (1.0 - dx) * (dy);
float weight_br = (dx)       * (dy);

Ваше окончательное значение рассчитывается как сумма продуктов каждого пикселя с соответствующим весом

Ответ 3

Использование сопоставления может быть более эффективным, если вы хотите сделать это многократно или последовательно. Другим преимуществом является выбор метода интерполяции и способы обработки пограничных условий. Наконец, некоторые функции интерполяции также реализованы на графическом процессоре.  remap

Ответ 4

К сожалению, у меня нет достаточного количества баллов, чтобы опубликовать это как комментарий к принятому ответу... Я скорректировал код в соответствии со своей собственной проблемой, которая требует интерполяции на одноканальной матрице с плавающей точкой.

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

Я реализовал три метода из ответа Андрея Камаева, а также простой ближайший сосед (в основном просто округляя координаты).

Я провел эксперимент с матрицей A (100x100), которую я просто заполнил мусором. Затем я сделал матрицу B (400x400), которая заполнена значениями, интерполированными из a такими, что: B (i, j) = A (i/4, j/4).

Каждый прогон выполнялся 1000 раз, а здесь среднее время:

  • Ближайший сосед: 2.173 мс
  • getRectSubPix: 26.506 ms
  • remap: 114.265 мс
  • руководство: 5.086 мс
  • руководство без borderInterpolate: 3.842 мс

Итак, ближайший сосед для сверхскоростной скорости, если вы действительно не слишком заботитесь об актуальной интерполяции и просто нуждаетесь в стоимости, особенно если ваши данные изменяются очень плавно. Для всего остального я бы пошел с ручной билинейной интерполяцией, поскольку он кажется последовательно быстрее других методов. (OpenCV 2.4.9 - Ubuntu 15.10 Repo - февраль 2016 г.).

Если вы знаете, что все ваши пиксели, входящие в ваш состав, находятся в пределах вашей матрицы, тогда вы можете сделать это в основном эквивалентным по времени ближайшему соседству - хотя разница все равно незначительна.