C/C++ BMP圖像的放大縮小(雙線性插值)及彩色轉黑白


1.放大縮小

轉載:https://blog.csdn.net/baidu_37503452/article/details/73498139

https://blog.csdn.net/wanty_chen/article/details/80283872

圖像的放大縮小其實是一回事,都是先創建一張空白目標圖像(放大縮小后的圖像),其大小就是想要放大縮小后所得到的圖像大小。創建圖像后我們並不知道這張圖像里面的各個像素點RGB(或灰度)值是多少,這個時候就需要經過一個算法去算目標圖像的像素點RGB(或灰度)值。基本上所有相關的算法都是通過算出目標圖像的像素跟原圖像的像素的映射關系來實現的,但是不同的算法由於這個映射關系的求取不同,處理效率和處理效果會有所差異。本文介紹的是比較常用到的雙線性插值算法

  雙線性插值是有兩個變量的插值函數的線性插值擴展,其核心思想是在兩個方向分別進行一次線性插值,線性插值的結果與插值的順序無關。(下圖從https://blog.csdn.net/zhangla1220/article/details/41014541截圖所得)

 

 

 

 

目標圖(x, y) 映射到原圖是(X + u, Y + v)(計算方法同最鄰近插值)。設u與v分別為X + u,Y + v的小數部分。由於下標都是整數,因此原圖其實並不存在該點。則取其附近四個領域點為(X, Y) (X, Y + 1) (X + 1, Y) (X + 1, Y + 1),則目標圖(x, y)處的值為 f(x , y) = f(X + u, Y + v) =f (X, Y)  * (1 - u) * (1 - v) + f(X, Y + 1) * (1 - u) * v + f(X + 1, Y) * u * (1 - v) + f (X + 1, Y + 1) * u * v;

為更加形象地說明該原理,我們給出下圖:

                                         

       如上面所言,我們需要算出目標圖像像素跟原圖像像素的映射關系,我們從遍歷目標圖像像素點開始,對每一個像素點進行比例計算,算出其如果投影到原圖像應該是處於原圖像的哪個位置,通常所得到的結果正如上圖的所示,求得的點P處於原圖像的四個真實像素點中。

       我們假定原圖像像素點間的RGB(或灰度)值是呈線性變化的,則我們可以通過A、B兩點RGB(或灰度)值算出AB線段上面的E點的RGB(或灰度)值,同理亦可算出F點的RGB(或灰度)值。得到了E、F兩點的RGB(或灰度)值后可經由相同的方法得到P點的RGB(或灰度)值。到此,我們就知道該如何通過映射關系去求得目標圖像的RGB(或灰度)值了。

    我們把點A、B、C、D、E、F、P的RGB(或灰度)值分別記為F_A、F_B、F_C、F_D、F_E、F_F、F_P(注意由於RGB是三個值,這個記法其實不嚴謹,可以理解為是FA等是RGB的其中一個值,然后另外兩個也可同理得到),則有

F_E=(1-0.7)*F_A+(1-0.3)*F_B,

F_F=(1-0.7)*F_C+(1-0.3)*F_D,

最終 得到目標點的值:

F_P=(1-0.7)*F_E+(1-0.3)*F_F=0.3*0.3*F_A+0.3*0.7*F_B+0.7*0.3*F_C+0.7*0.7*F_D。

#include <string.h>   
#include <math.h>     
#include <stdio.h>     
#include <stdlib.h>     
#include <malloc.h>  
 
#include<time.h>//時間相關頭文件,可用其中函數計算圖像處理速度  
 
#define   WIDTHBYTES(bits) (((bits)+31)/32*4)//用於使圖像寬度所占字節數為4byte的倍數  
 
#define MYDRAW_HEIGHT 1080  //目標圖像高度  
#define MYDRAW_WIDTH 1920  //目標圖像寬度  
 
typedef unsigned char  BYTE;
typedef unsigned short WORD;
typedef unsigned long  DWORD;
typedef long LONG;
 
//位圖文件頭信息結構定義  
//其中不包含文件類型信息(由於結構體的內存結構決定,要是加了的話將不能正確讀取文件信息)  
 
typedef struct tagBITMAPFILEHEADER {
    DWORD  bfSize;          //文件大小  
    WORD   bfReserved1;     //保留字,不考慮  
    WORD   bfReserved2;     //保留字,同上  
    DWORD  bfOffBits;       //實際位圖數據的偏移字節數,即前三個部分長度之和  
} BITMAPFILEHEADER;
 
//信息頭BITMAPINFOHEADER,也是一個結構,其定義如下:  
 
typedef struct tagBITMAPINFOHEADER {
    //public:  
    DWORD   biSize;             //指定此結構體的長度,為40  
    LONG    biWidth;            //位圖寬  
    LONG    biHeight;           //位圖高  
    WORD    biPlanes;           //平面數,為1  
    WORD    biBitCount;         //采用顏色位數,可以是1,2,4,8,16,24,新的可以是32  
    DWORD   biCompression;      //壓縮方式,可以是0,1,2,其中0表示不壓縮  
    DWORD   biSizeImage;        //實際位圖數據占用的字節數  
    LONG    biXPelsPerMeter;    //X方向分辨率  
    LONG    biYPelsPerMeter;    //Y方向分辨率  
    DWORD   biClrUsed;          //使用的顏色數,如果為0,則表示默認值(2^顏色位數)  
    DWORD   biClrImportant;     //重要顏色數,如果為0,則表示所有顏色都是重要的  
} BITMAPINFOHEADER;
 
void main()
{
    long now = 0;
    now = clock();//存儲圖像處理開始時間  
 
    BITMAPFILEHEADER bitHead, writebitHead;
    BITMAPINFOHEADER bitInfoHead, writebitInfoHead;
    FILE* pfile;//輸入文件  
    FILE* wfile;//輸出文件  
 
    char strFile[50] = "demo.bmp";//打開圖像路徑,BMP圖像必須為24位真彩色格式  
    char strFilesave[50] = "16.bmp";//處理后圖像存儲路徑  
    fopen_s(&pfile, strFile, "rb");//文件打開圖像  
    fopen_s(&wfile, strFilesave, "wb");//打開文件為存儲修改后圖像做准備  
                                       //讀取位圖文件頭信息  
    WORD fileType;
    fread(&fileType, 1, sizeof(WORD), pfile);
    fwrite(&fileType, 1, sizeof(WORD), wfile);
    if (fileType != 0x4d42)
    {
        printf("file is not .bmp file!");
        return;
    }
    //讀取位圖文件頭信息  
    fread(&bitHead, 1, sizeof(tagBITMAPFILEHEADER), pfile);
    writebitHead = bitHead;//由於截取圖像頭和源文件頭相似,所以先將源文件頭數據賦予截取文件頭  
                           //讀取位圖信息頭信息  
    fread(&bitInfoHead, 1, sizeof(BITMAPINFOHEADER), pfile);
    writebitInfoHead = bitInfoHead;//同位圖文件頭相似  
 
    writebitInfoHead.biHeight = MYDRAW_HEIGHT;//為截取文件重寫位圖高度  
    writebitInfoHead.biWidth = MYDRAW_WIDTH;//為截取文件重寫位圖寬度  
    int mywritewidth = WIDTHBYTES(writebitInfoHead.biWidth*writebitInfoHead.biBitCount);//BMP圖像實際位圖數據區的寬度為4byte的倍數,在此計算實際數據區寬度  
    writebitInfoHead.biSizeImage = mywritewidth*writebitInfoHead.biHeight;//計算位圖實際數據區大小  
 
    writebitHead.bfSize = 54 + writebitInfoHead.biSizeImage;//位圖文件頭大小為位圖數據區大小加上54byte  
    fwrite(&writebitHead, 1, sizeof(tagBITMAPFILEHEADER), wfile);//寫回位圖文件頭信息到輸出文件  
    fwrite(&writebitInfoHead, 1, sizeof(BITMAPINFOHEADER), wfile);//寫回位圖信息頭信息到輸出文件  
 
    int width = bitInfoHead.biWidth;
    int height = bitInfoHead.biHeight;
    //分配內存空間把源圖存入內存     
    int l_width = WIDTHBYTES(width*bitInfoHead.biBitCount);//計算位圖的實際寬度並確保它為4byte的倍數  
    int write_width = WIDTHBYTES(writebitInfoHead.biWidth*writebitInfoHead.biBitCount);//計算寫位圖的實際寬度並確保它為4byte的倍數  
 
    BYTE    *pColorData = (BYTE *)malloc(height*l_width);//開辟內存空間存儲圖像數據  
    memset(pColorData, 0, height*l_width);
 
    BYTE    *pColorDataMid = (BYTE *)malloc(mywritewidth*MYDRAW_HEIGHT);//開辟內存空間存儲圖像處理之后數據  
    memset(pColorDataMid, 0, mywritewidth*MYDRAW_HEIGHT);
 
    long nData = height*l_width;
    long write_nData = mywritewidth*MYDRAW_HEIGHT;//截取的位圖數據區長度定義  
 
                                                  //把位圖數據信息讀到數組里     
    fread(pColorData, 1, nData, pfile);//圖像處理可通過操作這部分數據加以實現  
 
    /*******************圖像處理部分******************/
    /*******************雙線性插值******************/
    for (int hnum = 0; hnum < MYDRAW_HEIGHT; hnum++)
        for (int wnum = 0; wnum < MYDRAW_WIDTH; wnum++)
        {
            double d_original_img_hnum = hnum*height / (double)MYDRAW_HEIGHT;
            double d_original_img_wnum = wnum*width / (double)MYDRAW_WIDTH;
            int i_original_img_hnum = d_original_img_hnum;
            int i_original_img_wnum = d_original_img_wnum;
            double distance_to_a_x = d_original_img_wnum - i_original_img_wnum;//在原圖像中與a點的水平距離  
            double distance_to_a_y = d_original_img_hnum - i_original_img_hnum;//在原圖像中與a點的垂直距離  
 
            int original_point_a = i_original_img_hnum*l_width + i_original_img_wnum * 3;//數組位置偏移量,對應於圖像的各像素點RGB的起點,相當於點A    
            int original_point_b = i_original_img_hnum*l_width + (i_original_img_wnum + 1) * 3;//數組位置偏移量,對應於圖像的各像素點RGB的起點,相當於點B  
            int original_point_c = (i_original_img_hnum + 1)*l_width + i_original_img_wnum * 3;//數組位置偏移量,對應於圖像的各像素點RGB的起點,相當於點C   
            int original_point_d = (i_original_img_hnum + 1)*l_width + (i_original_img_wnum + 1) * 3;//數組位置偏移量,對應於圖像的各像素點RGB的起點,相當於點D   
            if (i_original_img_hnum +1 >= width)
            {
                original_point_c = original_point_a;
                original_point_d = original_point_b;
            }
            if (i_original_img_wnum +1 >= height)
            {
                original_point_b = original_point_a;
                original_point_d = original_point_c;
            }
 
            int pixel_point = hnum*write_width + wnum * 3;//映射尺度變換圖像數組位置偏移量  
            for(int i = 0; i < 3; i++)
            {
                pColorDataMid[pixel_point + i] =
                pColorData[original_point_a + i] * (1 - distance_to_a_x)*(1 - distance_to_a_y) +
                pColorData[original_point_b + i] * distance_to_a_x*(1 - distance_to_a_y) +
                pColorData[original_point_c + i] * distance_to_a_y*(1 - distance_to_a_x) +
                pColorData[original_point_c + i] * distance_to_a_y*distance_to_a_x;
            }
 
        }
    /*******************雙線性插值******************/
    /*******************圖像處理部分******************/
 
    fwrite(pColorDataMid, 1, write_nData, wfile);   //將處理完圖像數據區寫回文件  
    fclose(pfile);
    fclose(wfile);
 
    printf("圖像處理完成\n");
    printf("運行時間為:%dms\n", int(((double)(clock() - now)) / CLOCKS_PER_SEC * 1000));//輸出圖像處理花費時間信息  
}

2.黑白

把上面代碼中

            for(int i = 0; i < 3; i++)
            {
                pColorDataMid[pixel_point + i] =
                pColorData[original_point_a + i] * (1 - distance_to_a_x)*(1 - distance_to_a_y) +
                pColorData[original_point_b + i] * distance_to_a_x*(1 - distance_to_a_y) +
                pColorData[original_point_c + i] * distance_to_a_y*(1 - distance_to_a_x) +
                pColorData[original_point_c + i] * distance_to_a_y*distance_to_a_x;
            }

修改為

            for(int i = 0; i < 3; i++)
            {
                pColorDataMid[pixel_point + i] =
                pColorData[original_point_a] * (1 - distance_to_a_x)*(1 - distance_to_a_y) +
                pColorData[original_point_b] * distance_to_a_x*(1 - distance_to_a_y) +
                pColorData[original_point_c] * distance_to_a_y*(1 - distance_to_a_x) +
                pColorData[original_point_c] * distance_to_a_y*distance_to_a_x;
            }

得到的圖片就是黑白的了

 

關於程序里的3:

不是固定值(在我自己的某個程序里為4)

猜測是

write_width/writebitInfoHead.biWidth

 


免責聲明!

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



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