數字圖像處理之雙線性插值


數字圖像中實現縮放的方法有很多種,其中一種就是雙線性插值,在實現圖像縮放時,有兩種方法來確定縮放后的圖像的像素值,第一種是根據原圖像中的的像素找到對應的縮放后的圖像中的像素,第二種是根據縮放后的圖像找到對應的原圖像中的像素,如下圖

                

但是第一種方法有缺點,因為小圖中的像素點到大圖中的像素點不是滿射,因此大圖中的點不能完全有像素值,第二種方法也有缺點,大圖中的點逆映射為小圖中的點時,得到的像素坐標值可能不是整數,一種辦法是采用最近鄰方法,即將得到的坐標值與相鄰的原圖像中的像素坐標值比較,取離得最近的坐標值對應的像素值作為縮放后的圖像對應的坐標值的像素值,這種辦法可能導致圖像失真,因此采用雙線性差值的辦法來進行計算相應的像素值。

                

右側是最一般的雙線性插值,下面舉一個實際的例子來說明雙線性插值在圖像縮放中的應用。

假設一個圖像的大小是485x647,放大分別放大1.3倍和1.7倍,即485x1.3=630.5,647x1.7=1099.9,根據四舍五入的原則確定放大后的圖像為631x1100,接下來就是計算放大后圖像各個位置的像素值,例如計算放大后圖像位於(136,345)位置的像素值,則136/1.3=104.615,345/1.7=202.941,這里由於示例的原因取小數點后三位,則原圖像中相鄰的四個位置分別是(104,202),(104,203),(105,202),(105,203)這四個點,

如圖我畫出了這個點對應的周圍的四個點,203.941-203=0.941

所以f(R1)=(1-0.941)xf(104,203)+0.941xf(104,204),

f(R2)=(1-0.941)xf(105,203)+0.941xf(105,204),

104.615-104=0.615

所以f(P)=(1-0.615)xf(R1)+0.615xf(R2),

其中f表示的那一點的像素值,這樣就計算出了f(P),實際上就是放大后圖像(136,345)處對應的像素值。

下面還是先看Mat的存儲形式。Mat和Matlab里的數組格式有點像,但一般是二維向量,如果是灰度圖,一般存放<uchar>類型;如果是RGB彩色圖,存放<Vec3b>類型。

單通道灰度圖數據存放格式:
多通道的圖像中,每列並列存放通道數量的子列,如RGB三通道彩色圖:
注意通道的順序反轉了:BGR。通常情況內存足夠大的話圖像的每一行是連續存放的,也就是在內存上圖像的所有數據存放成一行,這中情況在訪問時可以提供很大方便。可以用 isContinuous()函數來判斷圖像數組是否為連續的。
下面介紹兩種訪問mat中像素值的方法
第一種:
uchar* p = I.ptr<uchar>(i);//獲得第i行的指針
p[j]得到的就是第i行第j個像素,注意j指的是從前往后數第j個元素,不是上圖標的column j,例如j=4,則表示上圖的column 1的綠色值。
第二種
I.at<Vec3b>(i,j)[k],表示的是第i行第j列的第k個值,例如i=0,j=1,k=1表示上圖中的row 0,column 1的綠色值。
介紹完這些,下面是我寫的縮放的源代碼了
#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std;
int x, y;
void scale(Mat &srcmat, Mat &desmat, double sx, double sy);
int main(int argc ,char** argv){
    Mat srcimg = imread("opencv.jpg", IMREAD_COLOR);// Read the file
    if (!srcimg.data) // Check for invalid input
    {
        cout << "Could not open or find the image" << std::endl;
        return -1;
    }
    double sx = 0, sy = 0;
    cout << "please input scale x and scale y" << endl;
    cin >> sx >> sy;
    namedWindow("source img",WINDOW_AUTOSIZE);
    imshow("source img", srcimg);
     y = int(srcimg.cols*sy), x = int(srcimg.rows*sx);
    Mat scaimg(x, y, CV_8UC3, Scalar::all(0));
    scale(srcimg, scaimg, sx, sy);
    imshow("scaled img", scaimg);
    waitKey();
    return 0;
}
void scale(Mat &srcmat, Mat &desmat, double sx, double sy){
    int nc = x, nl = y,srccol=0,srcrow=0;
    double alph = 0.0, beta = 0.0;
    for (int i = 0; i < nc; i++){
        uchar* desdata = desmat.ptr<uchar>(i);
        for (int j = 0; j < nl; j++){
            srcrow = int(i / sx);
            //下面的的幾個if是判斷放大后圖像對應到原圖像的坐標是否越界的
            if (srcrow >= srcmat.rows-1){
                srcrow = srcmat.rows - 2;
            }
            alph = i / sx - srcrow;
            if (alph >= 1)
                alph = 1;
            srccol = int(j / sy);
            if (srccol >= srcmat.cols-1)
                srccol = srcmat.cols-2;
            beta = j / sy - srccol;
            if (beta >= 1)
                beta=1;
            for (int k = 0; k < 3; k++){
                double kk=srcmat.at<Vec3b>(srcrow, srccol)[k] +
                    beta*(srcmat.at<Vec3b>(srcrow, srccol+1)[k] - srcmat.at<Vec3b>(srcrow, srccol)[k]);
                double jj = srcmat.at<Vec3b>(srcrow+1, srccol)[k] +
                    beta*(srcmat.at<Vec3b>(srcrow+1, srccol + 1)[k] - srcmat.at<Vec3b>(srcrow+1, srccol)[k]);
                desdata[j*3+k] = kk + alph*(jj - kk);
            }
        }
    }
}

結果如下:

參考:http://en.wikipedia.org/wiki/Bilinear_interpolation

   http://blog.csdn.net/xiaowei_cqu/article/details/7771760

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM