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

Image 如何在opencv中获得真实位置的图像像素?,image,opencv,interpolation,Image,Opencv,Interpolation,我想检索图像中像素的rgb。 但位置不是整数位置,而是实值(x,y)。 我想要一个双线性插值。 我该怎么做 非常感谢双线性插值只意味着根据与正在检查的像素最近的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;

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


非常感谢

双线性插值只意味着根据与正在检查的像素最近的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个像素对值进行加权。权重可按如下方式计算

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);

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

子像素访问没有简单的函数,但我可以建议您几个选项:

  • 使用并提取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);
    }
    
    cv::Vec3b getColorSubpix(常数cv::Mat&img,cv::Point2f pt)
    {
    cv::垫片;
    cv::getRectSubPix(img,cv::Size(1,1),pt,patch);
    返回补丁。at(0,0);
    }
    
  • 使用更灵活但精度更低的单像素贴图:

    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(常数cv::Mat&img,cv::Point2f pt)
    {
    cv::垫片;
    cv::重新映射(img、补丁、cv::Mat(1、1、cv_32FC2和pt)、cv::noArray(),
    cv::内部线性,cv::边界反射(101);
    返回补丁。at(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);
    }
    
    cv::Vec3b getColorSubpix(常数cv::Mat&img,cv::Point2f pt)
    {
    断言(!img.empty());
    断言(img.channels()==3);
    int x=(int)pt.x;
    int y=(int)pt.y;
    int x0=cv::borderInterpolate(x,img.cols,cv::BORDER\u REFLECT\u 101);
    int x1=cv::borderInterpolate(x+1,img.cols,cv::BORDER\u REFLECT\u 101);
    int y0=cv::borderInterpolate(y,img.rows,cv::BORDER\u REFLECT\u 101);
    int y1=cv::borderInterpolate(y+1,img.rows,cv::BORDER\u REFLECT\u 101);
    浮点数a=pt.x-(浮点数)x;
    浮点数c=pt.y-(浮点数)y;
    uchar b=(uchar)cvRound((在(y0,x0)[0]*(1.f-a)+在(y0,x1)[0]*a)的img.at)*(1.f-c)
    +(在(y1,x0)[0]*(1.f-a)+img.在(y1,x1)[0]*a)*c);
    uchar g=(uchar)cvRound((img.at(y0,x0)[1]*(1.f-a)+img.at(y0,x1)[1]*a)*(1.f-c)
    +(在(y1,x0)[1]*(1.f-a)+img.at(y1,x1)[1]*a)*c);
    uchar r=(uchar)cvRound((img.at(y0,x0)[2]*(1.f-a)+img.at(y0,x1)[2]*a)*(1.f-c)
    +(在(y1,x0)[2]*(1.f-a)+img.at(y1,x1)[2]*a)*c);
    返回cv::Vec3b(b,g,r);
    }
    

  • 亚像素访问没有简单的功能,但我可以向您推荐几个选项:

  • 使用并提取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);
    }
    
    cv::Vec3b getColorSubpix(常数cv::Mat&img,cv::Point2f pt)
    {
    cv::垫片;
    cv::getRectSubPix(img,cv::Size(1,1),pt,patch);
    返回补丁。at(0,0);
    }
    
  • 使用更灵活但精度更低的单像素贴图:

    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(常数cv::Mat&img,cv::Point2f pt)
    {
    cv::垫片;
    cv::重新映射(img、补丁、cv::Mat(1、1、cv_32FC2和pt)、cv::noArray(),
    cv::内部线性,cv::边界反射(101);
    返回补丁。at(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);
    }
    
    cv::Vec3b getColorSubpix(常数cv::Mat&img,cv::Point2f pt)
    {
    断言(!img.empty());
    断言(img.channels()==3);
    int x=(int)pt.x;
    int y=(int)pt.y;
    int x0=cv::borderInterpolate(x,img.cols,cv::BORDER\u REFLECT\u 101);
    int x1=cv::borderInterpolate(x+1,img.cols,cv::BORDER\u REFLECT\u 101);
    int y0=cv::borderInterpolate(y,img.rows,cv::BORDER\u REFLECT\u 101);
    int y1=cv::borderInterpolate(y+1,img.rows,cv::BORDER\u REFLECT\u 101);
    浮点数a=pt.x-(浮点数)x;
    浮点数c=pt.y-(浮点数)y;
    uchar b=(uchar)cvRound((在(y0,x0)[0]*(1.f-a)+在(y0,x1)[0]*a)的img.at)*(1.f-c)
    +(在(y1,x0)[0]*(1.f-a)+img.在(y1,x1)[0]*a)*c);
    uchar g=(uchar)cvRound((img.at(y0,x0)[1]*(1.f-a)+img.at(y0,x1)[1]*a)*(1.f-c)
    +(在(y1,x0)[1]*(1.f-a)+img.at(y1,x1)[1]*a)*c);
    uchar r=(uchar)cvRound((img.at(y0,x0)[2]*(1.f-a)+img.at(y0,x1)[2]*a)*(1.f-c)
    +(在(y1,x0)[2]*(1.f-a)+img.at(y1,x1)[2]*a)*c);
    返回cv::Vec3b(b,g,r);
    }
    

  • 如果希望重复或一致地执行此操作,则使用映射可以更有效。另一个优点是选择插值方法以及如何处理边界条件。最后在GPU上实现了部分插值函数。

    如果您希望重复或一致地执行此操作,则使用映射可以更有效。另一个优点是选择插值方法以及如何处理边界条件。最后在GPU上实现了部分插值函数。
    不幸的是,我没有足够的分数将此作为对已接受答案的评论。。。我对代码进行了调整,以适应我自己的问题,这需要在单个浮动通道矩阵上进行插值

    我想我需要一些直觉,哪种方法是最快的

    我实现了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毫秒
    所以最近邻超高速如果