先講一下線性插值:已知數據 (x0, y0) 與 (x1, y1),要計算 [x0, x1] 區間內某一位置 x 在直線上的y值(反過來也是同樣,略):html
上面比較好理解吧,仔細看就是用x和x0,x1的距離做爲一個權重,用於y0和y1的加權。雙線性插值本質上就是在兩個方向上作線性插值。算法
在數學上,雙線性插值是有兩個變量的插值函數的線性插值擴展,其核心思想是在兩個方向分別進行一次線性插值[1]。見下圖:markdown
假如咱們想獲得未知函數 f 在點 P = (x, y) 的值,假設咱們已知函數 f 在 Q11 = (x1, y1)、Q12 = (x1, y2), Q21 = (x2, y1) 以及 Q22 = (x2, y2) 四個點的值。最多見的狀況,f就是一個像素點的像素值。首先在 x 方向進行線性插值,獲得函數
而後在 y 方向進行線性插值,獲得優化
綜合起來就是雙線性插值最後的結果:ui
因爲圖像雙線性插值只會用相鄰的4個點,所以上述公式的分母都是1。opencv中的源碼以下,用了一些優化手段,好比用整數計算代替float(下面代碼中的*2048就是變11位小數爲整數,最後有兩個連乘,所以>>22位),以及源圖像和目標圖像幾何中心的對齊
SrcX=(dstX+0.5)* (srcWidth/dstWidth) -0.5
SrcY=(dstY+0.5) * (srcHeight/dstHeight)-0.5,spa
這個要重點說一下,源圖像和目標圖像的原點(0,0)均選擇左上角,而後根據插值公式計算目標圖像每點像素,假設你須要將一幅5x5的圖像縮小成3x3,那麼源圖像和目標圖像各個像素之間的對應關係以下。若是沒有這個中心對齊,根據基本公式去算,就會獲得左邊這樣的結果;而用了對齊,就會獲得右邊的結果:.net
cv::Mat matSrc, matDst1, matDst2;
matSrc = cv::imread("lena.jpg", 2 | 4);
matDst1 = cv::Mat(cv::Size(800, 1000), matSrc.type(), cv::Scalar::all(0));
matDst2 = cv::Mat(matDst1.size(), matSrc.type(), cv::Scalar::all(0));
double scale_x = (double)matSrc.cols / matDst1.cols;
double scale_y = (double)matSrc.rows / matDst1.rows;
uchar* dataDst = matDst1.data;
int stepDst = matDst1.step;
uchar* dataSrc = matSrc.data;
int stepSrc = matSrc.step;
int iWidthSrc = matSrc.cols;
int iHiehgtSrc = matSrc.rows;
for (int j = 0; j < matDst1.rows; ++j)
{
float fy = (float)((j + 0.5) * scale_y - 0.5);
int sy = cvFloor(fy);
fy -= sy;
sy = std::min(sy, iHiehgtSrc - 2);
sy = std::max(0, sy);
short cbufy[2];
cbufy[0] = cv::saturate_cast<short>((1.f - fy) * 2048);
cbufy[1] = 2048 - cbufy[0];
for (int i = 0; i < matDst1.cols; ++i)
{
float fx = (float)((i + 0.5) * scale_x - 0.5);
int sx = cvFloor(fx);
fx -= sx;
if (sx < 0) {
fx = 0, sx = 0;
}
if (sx >= iWidthSrc - 1) {
fx = 0, sx = iWidthSrc - 2;
}
short cbufx[2];
cbufx[0] = cv::saturate_cast<short>((1.f - fx) * 2048);
cbufx[1] = 2048 - cbufx[0];
for (int k = 0; k < matSrc.channels(); ++k)
{
*(dataDst+ j*stepDst + 3*i + k) = (*(dataSrc + sy*stepSrc + 3*sx + k) * cbufx[0] * cbufy[0] +
*(dataSrc + (sy+1)*stepSrc + 3*sx + k) * cbufx[0] * cbufy[1] +
*(dataSrc + sy*stepSrc + 3*(sx+1) + k) * cbufx[1] * cbufy[0] +
*(dataSrc + (sy+1)*stepSrc + 3*(sx+1) + k) * cbufx[1] * cbufy[1]) >> 22;
}
}
}
cv::imwrite("linear_1.jpg", matDst1);
cv::resize(matSrc, matDst2, matDst1.size(), 0, 0, 1);
cv::imwrite("linear_2.jpg", matDst2);
好了,本篇到這裏,歡迎你們分享轉載,註明出處便可。code
[1] 雙線性插值(Bilinear Interpolation)
[2] OpenCV ——雙線性插值(Bilinear interpolation)
[3] 雙線性插值算法及須要注意事項
[4] OpenCV中resize函數五種插值算法的實現過程htm