1. 程式人生 > >OpenCV函式:resize() 實現 (最近鄰,雙線性)

OpenCV函式:resize() 實現 (最近鄰,雙線性)

前言

在 OpenCV 中,resize()用來改變影象的大小,包含了 5 種插值方式。在這裡,我用程式碼實現了其中兩種最具代表性的插值方式:最近鄰與雙線性插值。

最近鄰

最近鄰插值方式如下:

xd=xsfxyd=ysfyfx=wd/wsfy=hd/hs
其中,xd,yd為目標影象座標,xs,ys為源影象座標。
實現最近鄰的 resize() 程式碼:
Mat ReSizeForNearest(Mat src, Size dsize,double fx = 0, double fy = 0)
{
    if (src.empty())
        throw exception("Image is empty!"
); int c = src.channels(); if (dsize.width!=0 && dsize.height!=0) { fx = (double)dsize.width / (double)src.cols; fy = (double)dsize.height / (double)src.rows; } else if (fx != 0 && fy != 0) { dsize.width = int(src.cols*fx); dsize.height = int
(src.rows*fy); } else { throw exception("Invalid parameter!"); } Mat dst(dsize, src.type(), Scalar::all(0)); for (int i = 0; i < dst.rows; i++) { uchar* dstData = dst.ptr<uchar>(i); int srcy = cvFloor(i / fy); srcy = min(srcy, src.rows - 1
); uchar* srcData = src.ptr<uchar>(srcy); for (int j = 0; j < dst.cols*c; j+=c) { int srcx = cvFloor((j/c) / fx); srcx = min(srcx, src.cols - 1); for (int k = 0; k < c; k++) { dstData[j + k] = srcData[srcx*c + k]; } } } return dst; }

實驗結果:
原始圖片:

這裡寫圖片描述

呼叫 ReSizeForNearest(image, Size(600, 1000));的結果:

這裡寫圖片描述

可以發現放大後的圖片非常粗糙。

雙線性插值

先給出雙線性插值的公式:

Dst(xd,yd)=(1u)(1v)Src(xs,ys)+(1u)vSrc(xs,ys+1)+u(v1)Src(xs+1,ys)+uvSrc(xs+1,ys+1)
其矩陣表示為:
Dst(xd,yd)=[1uu][Src(xs,ys)Src(xs+1,ys)Src(xs,ys+1)Src(xs+1,ys+1)][1vv]
其中,xd,yd 的計算方式與最近鄰相同,向下取整,u,v 為計算得到座標值的小數部分。

實現雙線性插值的 resize() 程式碼:

Mat ReSizeForLinear(Mat src, Size dsize, double fx = 0, double fy = 0)
{
    if (src.empty())
        throw exception("Image is empty!");
    int c = src.channels();
    if (dsize.width != 0 && dsize.height != 0)
    {
        fx = (double)dsize.width / (double)src.cols;
        fy = (double)dsize.height / (double)src.rows;
    }
    else if (fx != 0 && fy != 0)
    {
        dsize.width = int(src.cols*fx);
        dsize.height = int(src.rows*fy);
    }
    else
    {
        throw exception("Invalid parameter!");
    }
    Mat dst(dsize, src.type(), Scalar::all(0));
    for (int i = 0; i < dst.rows; i++)
    {
        uchar* dstData = dst.ptr<uchar>(i);
        double srcy = i / fy;
        int y = cvFloor(srcy);
        double v = srcy - y;
        if (v < 0)
        {
            y = 0;
            v = 0;
        }
        if (y >= src.rows - 1)
        {
            y = src.rows - 2;
            v = 1;
        }
        uchar* srcData1 = src.ptr<uchar>(y);
        uchar* srcData2 = src.ptr<uchar>(y+1);
        for (int j = 0; j < dst.cols*c; j += c)
        {
            double srcx = (j/c) / fx;
            int x = cvFloor(srcx);
            double u = srcx - x;
            if (x < 0)
            {
                x = 0;
                u = 0;
            }
            if (x >= src.cols - 1)
            {
                x = src.cols - 2;
                u = 1;
            }
            for (int k = 0; k < c; k++)
            {
                dstData[j + k] = (1 - u)*(1 - v)*srcData1[x*c + k] +
                    (1 - u)*v*srcData2[x*c + k] +
                    u*(1 - v)*srcData1[(x + 1)*c + k] +
                    u*v*srcData2[(x + 1)*c + k];
            }
        }
    }
    return dst;
}

實驗結果:
原圖與上面一致,ReSizeForLinear(image, Size(600, 1000));的結果為:
這裡寫圖片描述

其結果比上面的圖片效果好了很多。