22

画像内のピクセルのRGBを取得したい。ただし、場所は整数の場所ではなく、実数値(x、y)です。バイリニア補間値が必要です。どうすればopencvを実行できますか?

どうもありがとう

4

4 に答える 4

49

サブピクセル アクセス用の単純な関数はありませんが、いくつかのオプションを提案できます。

  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);
    }
    
  2. 1 ピクセル マップを使用して、より柔軟ですが精度の低いリマップを使用します。

    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);
    }
    
  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);
    }
    
于 2012-11-09T03:37:28.327 に答える
5

残念ながら、これを受け入れられた回答に対するコメントとして投稿するのに十分なポイントがありません...フロートの単一チャネルマトリックスでの補間が必要な自分の問題に合わせてコードを調整しました。

どのアプローチが最も速いかについての直感が欲しいと思いました。

Andrey Kamaev's answer の 3 つの方法と、単純な最近傍 (基本的には座標を四捨五入するだけ) を実装しました。

ゴミで埋めたばかりの行列 A(100x100) で実験を行いました。次に、B(i,j) = A(i/4, j/4) のように a から補間された値で満たされた行列 B(400x400) を作成しました。

各実行は 1000 回行われ、平均時間は次のとおりです。

  • 最近隣: 2.173 ミリ秒
  • getRectSubPix: 26.506 ミリ秒
  • 再マップ: 114.265 ミリ秒
  • 手動: 5.086 ミリ秒
  • 境界線なしの手動補間: 3.842 ms

したがって、実際の補間をあまり気にせず、値だけが必要な場合、特にデータが非常にスムーズに変化する場合は、超高速の最近傍です。それ以外の場合は、他の方法よりも一貫して高速に見えるため、手動の双一次補間を使用します。(OpenCV 2.4.9 - Ubuntu 15.10 レポ - 2016 年 2 月)。

寄与している 4 つのピクセルすべてがマトリックスの境界内にあることがわかっている場合は、基本的に最近傍と時間的に同等にすることができますが、違いはほとんど無視できます。

于 2016-02-11T22:07:35.353 に答える
4

双一次補間とは、調べているピクセルに最も近い 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);

最終的な値は、各ピクセルとそれぞれの重みの積の合計として計算されます

于 2012-11-09T02:58:35.710 に答える
2

これを繰り返しまたは一貫して行う場合は、マッピングを使用する方が効率的です。もう 1 つの利点は、補間方法と境界条件の処理方法を選択できることです。最後に、いくつかの補間関数も GPU に実装されています。 再マップ

于 2015-09-29T01:52:15.620 に答える