我想获取图像中一个像素的RGB值。但该位置不是整数位置,而是实数值(X,Y)。我需要一个双线性插值的值。如何在OpenCV中实现?
非常感谢。
我想获取图像中一个像素的RGB值。但该位置不是整数位置,而是实数值(X,Y)。我需要一个双线性插值的值。如何在OpenCV中实现?
非常感谢。
对于亚像素访问,没有简单的函数可用,但我可以为您提供几个选项:
使用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);
}
(y0,x0)[0]*(1.f-a)*(1.f-c)
是在以 uchar b =
开头的那一行计算的。 - Andrey Kamaev(
img.atcv::Vec3b(y0, x0)[0] * (1.f - a) + img.atcv::Vec3b(y0, x1)[0] * a )
* (1.f - c)"。实际上,在发布之前,我已经测试了所有3个版本,并且它们产生相同的结果。 - Andrey Kamaevremap
比 getRectSubPix
不够精确? - Alessandro Jacopson我想要一些直觉,哪种方法最快。
我实现了Andrey Kamaev答案中的三种方法以及一个简单的最近邻(基本上只是将坐标四舍五入)。
我使用了一个100x100的矩阵A,里面装满了垃圾。然后我又制作了一个400x400的矩阵B,其中填充了从A中插值而来的值,即:B(i,j) = A(i/4, j/4)。
每次运行都进行了1000次,以下是平均时间:
因此,如果你不太关心实际插值,只需要一个值,特别是当你的数据变化非常平稳时,最近邻算法可以获得超快的速度。对于其他情况,我会选择手动双线性插值,因为它似乎比其他方法快得更稳定。(OpenCV 2.4.9 - Ubuntu 15.10 Repo - Feb 2016)。
如果你知道所有四个贡献像素都在矩阵的边界内,那么你可以使它基本上等效于最近邻算法 - 不过两者之间的差异相当微不足道。
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);
您的最终值是通过每个像素与其相应权重的乘积之和来计算的。
import cv2 as cv
def get_subpixel(img: cv.Mat, x: float, y: float):
"""Get interpolated pixel value at (@x, @y) with float precision"""
patch = cv.getRectSubPix(img, (1,1), (x, y), np.zeros((1,1)), cv.CV_32F)
if patch is not None:
return patch[0][0]
return None
getRectSubPix
,但强制opencv在返回的补丁中使用浮点精度。