I want to retrieve the rgb of a pixel in the image. But the location is not integer location but real values (x,y). I want a bilinear interpolated value. How could I do it opencv?
我想要检索图像中像素的rgb。但是位置不是整数位置,而是实值(x,y)。我想要一个双线性插值。我怎么能做opencv?
Thanks a lot
非常感谢
4 个解决方案
#1
31
There is no simple function for subpixel access but I can suggest you few options:
对于子像素访问没有简单的功能,但我可以建议您选择:
-
Use getRectSubPix and extract 1 pixel region:
使用getRectSubPix和extract 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); }
-
Use more flexible but less precise remap with one-pixel map:
使用更灵活但更不精确的重新映射与一像素地图:
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); }
-
Implement bilinear interpolation yourself, as it is not a rocket science:
实现双线性插值,因为它不是火箭科学:
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
bilinear interpolation just means weighting the value based on the 4 nearest pixels to the one you are examining. The weights can be calculated as follows.
双线性插值只是意味着根据你正在检查的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);
Your final value is calculated as the sum of the products of each pixel with its respective weight
你的最终值被计算为每个像素的乘积和其各自的权重之和。
#3
2
Unfortunately I don't have enough points to post this as a comment on the accepted answer... I adjusted the code to suit my own problem which requires interpolation on a single channel matrix of floats.
不幸的是,我没有足够的观点来评论这个被接受的答案……我调整了代码以适应我自己的问题,这需要在一个浮动的单一通道矩阵上进行插值。
I thought I'd like some intuition of which of the approaches are the fastest.
我想我需要一些直觉,哪些方法是最快的。
I implemented the 3 methods from Andrey Kamaev's answer as well as a simple nearest neighbour (basically just rounding off the co-ordinates).
我从Andrey Kamaev的答案和一个简单的最近的邻居(基本上只是舍入了坐标)中实现了3种方法。
I ran an experiment with a matrix A(100x100) which I just filled with garbage. I then made a matrix B(400x400) which is filled with values interpolated from a such that: B(i,j) = A(i/4, j/4).
我用一个矩阵a (100x100)做了一个实验,我把它填满了垃圾。然后,我做了一个矩阵B(400x400),其中填充了一个这样的值:B(I,j) = a (I /4, j/4)。
Each run was done 1000 times and here are the average times:
每次运行1000次,这里是平均次数:
- Nearest Neighbour: 2.173 ms
- 最近的邻居:2.173 ms
- getRectSubPix: 26.506 ms
- 女士getRectSubPix:26.506
- remap: 114.265 ms
- 女士重新映射:114.265
- manual: 5.086 ms
- 手动:5.086女士
- manual without borderInterpolate: 3.842 ms
- 手动无边界插值:3.842 ms。
So nearest neighbour for super speed if you don't really care about the actual interpolation too much and just need a value - particularly if your data varies very smoothly. For anything else just I'd go with the manual bilinear interpolation as it seems consistently faster than the other methods. (OpenCV 2.4.9 - Ubuntu 15.10 Repo - Feb 2016).
如果你不太关心实际的插值,那么最近的邻居,如果你不关心实际的插值,只需要一个值,特别是当你的数据非常平滑的时候。对于其他任何东西,我都用手动双线性插值,因为它看起来总是比其他方法快。(OpenCV 2.4.9 - Ubuntu 15.10 Repo - 2016年2月)。
If you know all 4 your contributing pixels are within the bounds of your matrix, then your can make it basically equivalent in time to Nearest Neighbour - although the difference is pretty negligible anyway.
如果你知道所有4个贡献的像素都在你的矩阵的范围内,那么你就可以使它在时间上与最近的邻居基本相同——尽管无论如何,差别是可以忽略不计的。
#4
1
Using mapping can be more efficient if you want to do this repeatedly or consistently. ANother advantage is choosing an interpolation method and how to handle border conditions. Finally some of the interpolation functions are also implemented on GPU. remap
如果您想重复或持续地使用映射,那么使用映射会更有效。另一个优点是选择插值方法以及如何处理边界条件。最后在GPU上实现了一些插值函数。重新映射
#1
31
There is no simple function for subpixel access but I can suggest you few options:
对于子像素访问没有简单的功能,但我可以建议您选择:
-
Use getRectSubPix and extract 1 pixel region:
使用getRectSubPix和extract 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); }
-
Use more flexible but less precise remap with one-pixel map:
使用更灵活但更不精确的重新映射与一像素地图:
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); }
-
Implement bilinear interpolation yourself, as it is not a rocket science:
实现双线性插值,因为它不是火箭科学:
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
bilinear interpolation just means weighting the value based on the 4 nearest pixels to the one you are examining. The weights can be calculated as follows.
双线性插值只是意味着根据你正在检查的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);
Your final value is calculated as the sum of the products of each pixel with its respective weight
你的最终值被计算为每个像素的乘积和其各自的权重之和。
#3
2
Unfortunately I don't have enough points to post this as a comment on the accepted answer... I adjusted the code to suit my own problem which requires interpolation on a single channel matrix of floats.
不幸的是,我没有足够的观点来评论这个被接受的答案……我调整了代码以适应我自己的问题,这需要在一个浮动的单一通道矩阵上进行插值。
I thought I'd like some intuition of which of the approaches are the fastest.
我想我需要一些直觉,哪些方法是最快的。
I implemented the 3 methods from Andrey Kamaev's answer as well as a simple nearest neighbour (basically just rounding off the co-ordinates).
我从Andrey Kamaev的答案和一个简单的最近的邻居(基本上只是舍入了坐标)中实现了3种方法。
I ran an experiment with a matrix A(100x100) which I just filled with garbage. I then made a matrix B(400x400) which is filled with values interpolated from a such that: B(i,j) = A(i/4, j/4).
我用一个矩阵a (100x100)做了一个实验,我把它填满了垃圾。然后,我做了一个矩阵B(400x400),其中填充了一个这样的值:B(I,j) = a (I /4, j/4)。
Each run was done 1000 times and here are the average times:
每次运行1000次,这里是平均次数:
- Nearest Neighbour: 2.173 ms
- 最近的邻居:2.173 ms
- getRectSubPix: 26.506 ms
- 女士getRectSubPix:26.506
- remap: 114.265 ms
- 女士重新映射:114.265
- manual: 5.086 ms
- 手动:5.086女士
- manual without borderInterpolate: 3.842 ms
- 手动无边界插值:3.842 ms。
So nearest neighbour for super speed if you don't really care about the actual interpolation too much and just need a value - particularly if your data varies very smoothly. For anything else just I'd go with the manual bilinear interpolation as it seems consistently faster than the other methods. (OpenCV 2.4.9 - Ubuntu 15.10 Repo - Feb 2016).
如果你不太关心实际的插值,那么最近的邻居,如果你不关心实际的插值,只需要一个值,特别是当你的数据非常平滑的时候。对于其他任何东西,我都用手动双线性插值,因为它看起来总是比其他方法快。(OpenCV 2.4.9 - Ubuntu 15.10 Repo - 2016年2月)。
If you know all 4 your contributing pixels are within the bounds of your matrix, then your can make it basically equivalent in time to Nearest Neighbour - although the difference is pretty negligible anyway.
如果你知道所有4个贡献的像素都在你的矩阵的范围内,那么你就可以使它在时间上与最近的邻居基本相同——尽管无论如何,差别是可以忽略不计的。
#4
1
Using mapping can be more efficient if you want to do this repeatedly or consistently. ANother advantage is choosing an interpolation method and how to handle border conditions. Finally some of the interpolation functions are also implemented on GPU. remap
如果您想重复或持续地使用映射,那么使用映射会更有效。另一个优点是选择插值方法以及如何处理边界条件。最后在GPU上实现了一些插值函数。重新映射