天天看點

RGB源資料操作: 實作圖檔放大、縮小

一、運作環境介紹

Linux系統: Redhat6.3 (32位)

gcc 版本 4.4.6 20120305 (Red Hat 4.4.6-4) (GCC)

二、功能介紹

打開一張BMP圖檔,讀取RGB源資料進行縮放再生成新的BMP圖檔。

三、核心代碼

由于處理的是BMP圖檔資料,傳入的縮放後的圖檔寬度需要是4的倍數.

縮放算法參考:

http://blog.chinaunix.net/uid-22915173-id-2185545.html

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
int PicZoom(unsigned char *s_buff,unsigned int s_width,unsigned int s_height,unsigned char *buff,unsigned int width,unsigned int height);
void *my_memcpy(void *v_dst,const void *v_src,unsigned char c);
 
#pragma pack(1)   /* 必須在結構體定義之前使用,這是為了讓結構體中各成員按1位元組對齊 */
/*需要檔案資訊頭:14個位元組 */
struct BITMAPFILEHEADER
{
    unsigned short bfType;      //儲存圖檔類似。 'BM'
    unsigned long  bfSize;      //圖檔的大小
    unsigned short bfReserved1;
    unsigned short bfReserved2;
    unsigned long  bfOffBits;  //RGB資料偏移位址
};
 
/* 位圖資訊頭 */
struct BITMAPINFOHEADER { /* bmih */
    unsigned long  biSize;      //結構體大小
    unsigned long  biWidth;     //寬度
    unsigned long  biHeight;    //高度
    unsigned short biPlanes;
    unsigned short biBitCount;  //顔色位數
    unsigned long  biCompression;
    unsigned long  biSizeImage;
    unsigned long  biXPelsPerMeter;
    unsigned long  biYPelsPerMeter;
    unsigned long  biClrUsed;
    unsigned long  biClrImportant;
};
 
/*
圖檔放大與縮小示例
*/
int main(int argc,char *argv[])
{
    struct BITMAPFILEHEADER src_head;  //源檔案頭資料
    struct BITMAPINFOHEADER src_info;  //源檔案參數結構
    struct BITMAPFILEHEADER new_head;  //新檔案頭資料
    struct BITMAPINFOHEADER new_info;  //新檔案參數結構
    unsigned int new_Width;            //縮放後的寬度
    unsigned int new_Height;           //縮放後的高度
    unsigned char *new_buff;           //存放新圖檔的資料
    unsigned char *src_buff;           //存放源圖檔的資料
    unsigned int cnt=0;
 
    if(argc!=5)
    {
        printf("參數格式: ./a.out <原圖檔名稱> <新圖檔名稱> <縮放後寬度> <縮放後高度>\n");
        printf("例如: ./a.out src.bmp new.bmp 80 80 \n");
        return 0;
    }
    /*1. 打開圖檔檔案*/
    FILE *src_file=fopen(argv[1],"rb");
    FILE *new_file=fopen(argv[2],"wb");
    if(src_file==NULL||new_file==NULL)
    {
        printf("%s 源檔案打開失敗!\r\n",argv[1]);
        return;
    }
    
    /*2. 讀取源圖檔參數*/
    fread(&src_head,sizeof(struct BITMAPFILEHEADER),1,src_file);
    fread(&src_info,sizeof(struct BITMAPINFOHEADER),1,src_file);
    printf("源圖檔尺寸:w=%d h=%d\r\n",src_info.biWidth,src_info.biHeight);
 
    /*3. 擷取新圖檔的尺寸*/
    new_Width=atoi(argv[3]);
    new_Height=atoi(argv[4]);
    printf("新圖檔尺寸:w=%d h=%d\r\n",new_Width,new_Height);
 
    /*4. 申請存放圖檔資料的空間*/
    src_buff=malloc(src_info.biWidth*src_info.biHeight*3);
    new_buff=malloc(new_Width*new_Height*3);
    if(new_buff==NULL||src_buff==NULL)
    {
        printf("malloc申請空間失敗!\r\n");
        return -1;
    }
 
    /*5. 讀取源圖檔RGB資料*/
    fseek(src_file,src_head.bfOffBits,SEEK_SET); //移動檔案指針到RGB資料位置
    fread(src_buff,1,src_info.biWidth*src_info.biHeight*3,src_file); //讀取源資料
 
    /*6. 縮放圖檔*/
    if(PicZoom(src_buff,src_info.biWidth,src_info.biHeight,new_buff,new_Width,new_Height))
    {
        printf("圖檔縮放處理失敗!\r\n");
        return -1;
    }
 
    /*7. 寫入新圖檔資料*/
    //填充檔案頭
    memset(&new_head,0,sizeof(struct BITMAPFILEHEADER));
    new_head.bfType=0x4d42;
    new_head.bfSize=54+new_Width*new_Height*3;
    new_head.bfOffBits=54;
    //填充檔案參數
    memset(&new_info,0,sizeof(struct BITMAPINFOHEADER));
    new_info.biSize=sizeof(struct BITMAPINFOHEADER);
    new_info.biWidth=new_Width;
    new_info.biHeight=new_Height;
    new_info.biPlanes=1;
    new_info.biBitCount=24;
    //寫入檔案資料
    fwrite(&new_head,sizeof(struct BITMAPFILEHEADER),1,new_file);
    fwrite(&new_info,sizeof(struct BITMAPINFOHEADER),1,new_file);
    fseek(new_file,new_head.bfOffBits,SEEK_SET); //移動檔案指針到RGB資料位置
    cnt=fwrite(new_buff,1,new_info.biWidth*new_info.biHeight*3,new_file); //寫資料
 
    /*8. 關閉圖檔檔案*/
    fclose(new_file);
    fclose(src_file);
 
    printf("%s 新圖檔建立成功! 路徑:程式運作路徑下\r\n",argv[2]);
    return 0;
}
 
 
/**********************************************************************
* 函數名稱: PicZoom
* 功能描述: 近鄰取樣插值方法縮放圖檔
*            注意該函數會配置設定記憶體來存放縮放後的圖檔,用完後要用free函數釋放掉
*            "近鄰取樣插值"的原理請參考網友"lantianyu520"所著的"圖像縮放算法"
* 輸入參數:  ptOriginPic - 内含原始圖檔的象素資料
*             ptZoomPic    - 内含縮放後的圖檔的象素資料
* 輸出參數: 無
* 返 回 值: 0 - 成功, 其他值 - 失敗
***********************************************************************/
int PicZoom(unsigned char *ptOriginPic_aucPixelDatas,unsigned int ptOriginPic_iWidth,unsigned int ptOriginPic_iHeight,unsigned char *ptZoomPic_aucPixelDatas,unsigned int ptZoomPic_iWidth,unsigned int ptZoomPic_iHeight)
{
    unsigned int ptOriginPic_iLineBytes=ptOriginPic_iWidth*3; //一行的位元組數
    unsigned int ptZoomPic_iLineBytes=ptZoomPic_iWidth*3;  //一行的位元組數
 
    unsigned long dwDstWidth=ptZoomPic_iWidth;
    unsigned long* pdwSrcXTable;
    unsigned long x;
    unsigned long y;
    unsigned long dwSrcY;
    unsigned char *pucDest;
    unsigned char *pucSrc;
    unsigned long dwPixelBytes=3; //像素位元組
    pdwSrcXTable=malloc(sizeof(unsigned long) * dwDstWidth);
    if(NULL==pdwSrcXTable)
    {
        return -1;
    }
 
    for(x=0; x < dwDstWidth; x++)//生成表 pdwSrcXTable
    {
        pdwSrcXTable[x]=(x*ptOriginPic_iWidth/ptZoomPic_iWidth);
    }
 
    for(y=0; y < ptZoomPic_iHeight; y++)
    {
        dwSrcY=(y * ptOriginPic_iHeight/ptZoomPic_iHeight);
 
        pucDest=ptZoomPic_aucPixelDatas + y * ptZoomPic_iLineBytes;
        pucSrc=ptOriginPic_aucPixelDatas+dwSrcY * ptOriginPic_iLineBytes;
 
        for(x=0; x <dwDstWidth; x++)
        {
            my_memcpy(pucDest+x*dwPixelBytes,pucSrc+pdwSrcXTable[x]*dwPixelBytes,dwPixelBytes);
        }
    }
 
    free(pdwSrcXTable);
    return 0;
}
 
/*
函數功能:記憶體拷貝函數
*/
void *my_memcpy(void *v_dst,const void *v_src,unsigned char c)
{
    const char *src=v_src;
    char *dst=v_dst;
    while(c--)*dst++=*src++;
    return v_dst;
}      

四、運作示例

[wbyq@wbyq linux_c]$ gcc app.c 
[wbyq@wbyq linux_c]$ ./a.out 
參數格式: ./a.out <原圖檔名稱> <新圖檔名稱> <縮放後寬度> <縮放後高度>
例如: ./a.out src.bmp new.bmp 80 80 
[wbyq@wbyq linux_c]$ ./a.out 666.bmp 1.bmp 80 80
源圖檔尺寸:w=800 h=383
新圖檔尺寸:w=80 h=80
1.bmp 新圖檔建立成功! 路徑:程式運作路徑下
[wbyq@wbyq linux_c]$ eog 1.bmp       
RGB源資料操作: 實作圖檔放大、縮小

繼續閱讀