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截图所得)

C/C++ BMP图像的放大缩小(双线性插值)及彩色转黑白 C/C++ BMP图像的放大缩小(双线性插值)及彩色转黑白

目标图(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.30.3F_A+0.30.7F_B+0.70.3F_C+0.70.7F_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("运行时间为:%dmsn", 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

原文链接: https://www.cnblogs.com/Toya/p/12871979.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/197224

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月12日 下午7:30
下一篇 2023年2月12日 下午7:30

相关推荐