如何在opencv中获取真实位置的图像像素?

Posted

技术标签:

【中文标题】如何在opencv中获取真实位置的图像像素?【英文标题】:How to get the image pixel at real locations in opencv? 【发布时间】:2012-10-29 06:16:34 【问题描述】:

我想检索图像中像素的 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);
    
    

【讨论】:

+1 用于显示多个版本,我从没想过前两个。您在第三次实施中没有遗漏涉及 c 的术语吗?以 (y0,x0)[0]*(1.f-a)*(1.f-c) 为例 不,我没有失踪。 (y0,x0)[0]*(1.f-a)*(1.f-c) 是在从uchar b = 开始的行上计算的 你的意思是 "uchar b = (uchar)cvRound((img.at<:vec3b>(y0, x0)[0] * (1.f - a) + img.at <:vec3b>(y0, x1)[0] * a) * (1.f - c)"?我没看到... 你需要打开括号才能看到它:“uchar b = (uchar)cvRound((img.at<:vec3b>(y0, x0)[0] * (1. f - a) + img.at<:vec3b>(y0, x1)[0] * a ) * (1.f - c)" 实际上我在发布之前测试了所有 3 个版本,它们产生了相同的结果. 啊我现在看到了,我应该仔细阅读。谢谢解释【参考方案2】:

不幸的是,我没有足够的分数将其发布为对已接受答案的评论...我调整了代码以适应我自己的问题,该问题需要在单通道浮点矩阵上进行插值。

我想我想知道哪种方法最快。

我实现了 Andrey Kamaev 的回答中的 3 种方法以及一个简单的最近邻(基本上只是四舍五入坐标)。

我用刚刚填充了垃圾的矩阵 A(100x100) 进行了实验。然后我制作了一个矩阵 B(400x400),其中填充了从 a 插值的值:B(i,j) = A(i/4, j/4)。

每次运行 1000 次,以下是平均次数:

最近邻:2.173 毫秒 getRectSubPix:26.506 毫秒 重新映射:114.265 毫秒 手动:5.086 毫秒 手动无边框插值:3.842 毫秒

如果您不太关心实际插值并且只需要一个值 - 特别是如果您的数据变化非常平稳,那么最近邻可以获得超高速。对于其他任何事情,我都会使用手动双线性插值,因为它似乎始终比其他方法快。 (OpenCV 2.4.9 - Ubuntu 15.10 回购 - 2016 年 2 月)。

如果您知道所有 4 个贡献像素都在矩阵的范围内,那么您可以使其在时间上与最近邻基本相等 - 尽管无论如何差异都可以忽略不计。

【讨论】:

【参考方案3】:

双线性插值仅意味着根据与您正在检查的像素最近的 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);

您的最终值计算为每个像素与其各自权重的乘积之和

【讨论】:

【参考方案4】:

如果您想反复或始终如一地执行此操作,使用映射会更有效。另一个优点是选择插值方法以及如何处理边界条件。最后,一些插值函数也在 GPU 上实现。 remap

【讨论】:

请注意混叠和极端缩放问题。双线性插值并不是一个可以解决所有问题的神奇技巧。它仅使用 4 个相邻像素。有时人们会创建一个图像金字塔来确保数据的正确采样。

以上是关于如何在opencv中获取真实位置的图像像素?的主要内容,如果未能解决你的问题,请参考以下文章

opencv如何计算图像中物体的像素值

opencv如何读取多边形区域内的像素值?

04 图像像素的读写操作

如何在 OpenCV 中使用网络摄像头捕获 Mat 图像的像素值

在OpenCV打开的图像上,已知图像上的某点坐标,如何获取该点像素值

使用OpenCV如何获取图像每个像素的灰度值??