Как получить пиксель изображения в реальных местах в 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 г.).
Если вы знаете, что все ваши пиксели, входящие в ваш состав, находятся в пределах вашей матрицы, тогда вы можете сделать это в основном эквивалентным по времени ближайшему соседству - хотя разница все равно незначительна.