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

Sha*_*han 19 interpolation opencv image

我想要检索图像中像素的rgb.但是位置不是整数位置而是实数值(x,y).我想要一个双线性插值.我怎么能这样做opencv?

非常感谢

And*_*aev 41

子像素访问没有简单的功能,但我可以建议你几个选项:

  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);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用单像素映射使用更灵活但不太精确的重映射:

    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);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 自己实施双线性插值,因为它不是火箭科学:

    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);
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • Yon需要打开括号才能看到它:"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)"实际上我在发布前测试了所有3个版本并且它们产生了相同的结果. (3认同)

kle*_*ric 5

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

我想我想要一些关于哪种方法最快的直觉。

我实现了 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 ms

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

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