實現基於最近鄰內插和雙線性內插的圖像縮放C++實現


平時我們寫圖像處理的代碼時,如果需要縮放圖片,我們都是直接調用圖像庫的resize函數來完成圖像的縮放。作為一個機器視覺或者圖像處理算法的工作者,圖像縮放代碼的實現應該是必須掌握的。在眾多圖像縮放算法中,最近鄰內插算法和雙線性內插算法最為基本和常見,所以這篇文章就說一說如何用c++實現這兩種算法下的圖像縮放。

最近鄰內插

最近鄰內插這種算法就是根據原圖像和目標圖像的尺寸,計算縮放的比例,然后根據縮放比例計算目標像素所依據的原像素,過程中自然會產生小數,這時就采用四舍五入,取與這個點最相近的點,當然向下取整也是可以的。

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>

using namespace std;
void scale(cv::Mat& input_img, int width, int height);

int main()
{
    cv::Mat img = cv::imread("41.png",0);
    cv::imshow("src", img);
    scale(img, 450, 300);

    return 0;
}

//獲取原圖相應坐標的像素值
uchar get_scale_value(cv::Mat& input_img, int i, int j)
{
    uchar* p = input_img.ptr<uchar>(i);
    return p[j];
    
}

void scale(cv::Mat& input_img,int width,int height)
{
    cv::Mat output_img(height, width, CV_8UC1);
    output_img.setTo(0);
    float h_scale_rate = (float)input_img.rows/ height;  //高的比例
    float w_scale_rate = (float)input_img.cols / width;  //寬的比例
    for (int i = 0; i < height; i++)
    {
        uchar* p = output_img.ptr<uchar>(i);
        for (int j = 0; j < width; j++)
        {
            int i_scale = h_scale_rate * i;   //依照高的比例計算原圖相應坐標中的x,這里采用的是向下取整,當然四舍五入也可以
            int j_scale = w_scale_rate * j;  //依照寬的比例計算原圖相應坐標中的y
            //cout << "i_scale: " << i_scale <<" j_scale: "<< j_scale << endl;
 
            p[j] = get_scale_value(input_img,i_scale, j_scale);
        }
    }

    cv::imshow("scale", output_img);
    cv::imwrite("result.png", output_img);
    cv::waitKey();

}

原圖

縮放圖

最近鄰內插算法實現的圖像縮放的原理很簡單,編碼起來也容易,缺點就是得到的圖像效果不太好。

雙線性內插

對於一個目的像素,設置坐標通過反向變換得到的浮點坐標為(x,y),當然我們也可以將其表示成整數+小數的形式,即(i+u,j+v),其中i、j均為非負整數,u、v為[0,1)區間的浮點數,則這個像素得值 f(i+u,j+v) 可由原圖像中坐標為 (i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所對應的周圍四個像素的值決定,即:

    f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1)

雙線性內值算法的核心思想就是上面的那個公式,它解釋了對於原圖不存在的浮點像素(比如<1.5,1.5>)是如何確定其實際值的。其實它是以4個相鄰的像素值來共同確定,即<1,1> <2,1> <1,2> <2,2>。誰離<1,1>比較近,誰就對它起的影響比較大,這些都在公式中有所體現,這就是雙線性插值的精髓。

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>

using namespace std;
void bin_linear_scale(cv::Mat& input_img, int width, int height);

int main()
{
    cv::Mat img = cv::imread("41.png", 0);
    cv::imshow("src", img);
    bin_linear_scale(img, 450, 300);

    return 0;
}


//f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1)  
uchar get_scale_value(cv::Mat& input_img, float raw_i, float raw_j)
{
    int i = raw_i;
    int j = raw_j;
    float u = raw_i - i;
    float v = raw_j - j;

    //注意處理邊界問題,容易越界
    if (i + 1 >= input_img.rows || j + 1 >= input_img.cols)
    {
        uchar* p = input_img.ptr<uchar>(i);
        return p[j];
    }
    
    uchar* p = input_img.ptr<uchar>(i);
    uchar x1 = p[j];  //f(i,j)
    uchar x2 = p[j + 1];  //f(i,j+1)
    p = input_img.ptr<uchar>(i+1);
    uchar x3 = p[j];   //(i+1,j)
    uchar x4 = p[j + 1];  //f(i+1,j+1) 
     
   // printf("%d %d\n", i, j);
    return ((1-u)*(1-v)*x1+(1-u)*v*x2+u*(1-v)*x3+u*v*x4);
}

         
void bin_linear_scale(cv::Mat& input_img, int width, int height)
{
    cv::Mat output_img(height, width, CV_8UC1);
    output_img.setTo(0);
    float h_scale_rate = (float)input_img.rows / height;
    float w_scale_rate = (float)input_img.cols / width;
    for (int i = 0; i < height; i++)
    {
        uchar* p = output_img.ptr<uchar>(i);
        for (int j = 0; j < width; j++)
        {
            float i_scale = h_scale_rate * i;
            float j_scale = w_scale_rate * j;
            //cout << "i_scale: " << i_scale <<" j_scale: "<< j_scale << endl;

            p[j] = get_scale_value(input_img, i_scale, j_scale);
        }
    }

    cv::imshow("scale", output_img);
    cv::imwrite("result.png", output_img);
    cv::waitKey();
}


免責聲明!

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



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