使用标准IO对bitmap打马赛克

该博客介绍了如何使用C++通过标准IO对Bitmap图片进行马赛克处理。首先,解析bmp文件头获取图片的宽度、高度、像素点大小等信息,然后将图片划分为小方格,选取每个方格的第一个像素点作为代表色,将方格内其他像素点替换为该颜色,从而实现马赛克效果。代码详细展示了读取、处理和写入像素数据的过程。
摘要由CSDN通过智能技术生成

使用标准IO对bitmap打马赛克

了解bmp格式图片的存储格式

bmp文件头(bmp file header): 提供文件的格式、大小等信息(14字节)
bitmap文件头格式
位图信息头(bitmap information): 提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息(40字节)
bitmap信息头格式

我们需要从图片文件中获取

bfOffBits :图片像素数据距离文件开头的偏移量,找到(0,0)位置的像素点数据的位置
biWidth :图片的宽度,以像素点为单位
biHeigh :图片的高度,以像素点为单位
biBitCount : 每个像素点数据的大小,以位为单位,因为下述程序只能处理,rgb888的图片,所以可以使用这个判断图片是否是rgb888的图片

实现过程

1. 打开文件

    FILE* fp;
    ****
    //以读和追加的方式打开文件
    //open 打开成功则返回文件指针,失败返回NULL,并置位错误码
    //若失败则使用perror打印错误信息,并return -1
    if ((fp = fopen(argv[1], "r+")) == NULL){
        perror("fopen  error\n");
        return -1;
    }

2. 读取文件头的信息

 //printf是用来查看数据有没有正常读出

 //根据文件头数据格式读取实际像素数据的偏移量
 fseek(fp, 4, SEEK_CUR);
 fread(&bfOffBits, 4, 1, fp);
 //printf("bfOffBit = %d\n", bfOffBits);

 //由文件头格式读取图片的宽度
 fseek(fp, 18, SEEK_SET);
 fread(&biWidth, 4, 1, fp);
 //printf("biWidth = %d\n", biWidth);

 //由文件头格式读取图片的高度,因为宽度和高度存储的位置相邻,所有就不用移动光标的数据了
 fread(&biHeigh, 4, 1, fp);
 //printf("biHeigh = %d\n", biHeigh);

 //由文件头格式读取图片像素点存储宽度
 fseek(fp, 2, SEEK_CUR);
 fread(&biBitCount, 2, 1, fp);
 //printf("biBitCount = %d\n", biBitCount);

3. 将图片划分为若干方格

    //将光标定位到图片像素数据
    fseek(fp, bfOffBits, SEEK_SET);
    //将图片划分为n*n的小格子
    for (int i = 0; i < biHeigh / n ; i++) {
        for (int j = 0; j < biWidth / n; j++) {
            //将光标定位在第i行第j列的格子中第一像素点在文件中的存储位置
            fseek(fp, (biWidth * i + j) * n * 3 + bfOffBits, SEEK_SET);
        }
    }

4. 读取方格中第一个像素点的信息

    //像素点信息的结构体,本程序只能处理rgb888的bitmap
    //b代表blue,g代表green,r代表red,分别用来存储对应颜色的值
    //     typedef struct {
    //     unsigned char b, g, r;
    // } rgb_t;

    rgb_t rgb = { 255, 255,255};
    fread(&rgb, sizeo(rgb), 1, fp);

5. 将方格中第一个像素点的信息覆盖方格中其它像素点的信息

  for (int i = 0; i < n; i++) {
      for (int j = 0; j < n; j++) {
          //第一个元素不用处理,且在读的时候光标向后移动了,因此不能要跳过第一个像素点
          if (i == 0 && j == 0)
              continue;
          fwrite(&rgb, 3, 1, fp); //将光标所在的像素点置为第一个像素点
      }
      fseek(fp, (biWidth - n) * 3, SEEK_CUR); //将光标移动到下一行
  }

效果展示

原图:
原图.bmp
打过马赛克后:
mosic.bmp

源码

#include <head.h>

int bfOffBits = 0;
int biWidth = 0;
int biHeigh = 0;
int biBitCount = 0;

typedef struct {
    unsigned char b, g, r;
} rgb_t;

int get_value(FILE* fp)
{
    fseek(fp, 4, SEEK_CUR);
    fread(&bfOffBits, 4, 1, fp);
    //printf("bfOffBit = %d\n", bfOffBits);

    fseek(fp, 18, SEEK_SET);
    fread(&biWidth, 4, 1, fp);
    //printf("biWidth = %d\n", biWidth);

    fread(&biHeigh, 4, 1, fp);
    //printf("biHeigh = %d\n", biHeigh);

    fseek(fp, 2, SEEK_CUR);
    fread(&biBitCount, 2, 1, fp);
    //printf("biBitCount = %d\n", biBitCount);

    if(biBitCount != 24){
        printf("the bitmap is not rgb888\n");
        return -1;
    }
    return 0; 
}

//获取blocksize * blocksize方格中第一个像素点,然后将其它像素点都替换
void set_block_mosic(FILE* fp, int n, int m, int blocksize)
{
    rgb_t rgb = {0,0,0};
    fread(&rgb, sizeof(rgb), 1, fp);
    for (int i = 0; i < blocksize; i++) {
        for (int j = 0; j < blocksize; j++) {
            if (i == 0 && j == 0)
                continue;
            fwrite(&rgb, sizeof(rgb), 1, fp); //将光标所在的像素点置为第一个像素点
        }
        fseek(fp, (biWidth - blocksize) * 3, SEEK_CUR); //将光标移动到下一行
    }
}

void set_mosic(FILE* fp,int n)
{
    fseek(fp, bfOffBits, SEEK_SET);
    //将图片划分成一个个n * n 的小格
    for (int i = 0; i < biHeigh / n ; i++) {
        for (int j = 0; j < biWidth / n; j++) {
            fseek(fp, (biWidth * i + j) * n * 3 + bfOffBits, SEEK_SET);
            set_block_mosic(fp, i, j, n);
        }
    }
}

int main(int argc, const char* argv[])
{
    FILE* fp;
    int n = 1;
    //以读和追加的方式打开文件
    //open 打开成功则返回文件指针,失败返回NULL,并置位错误码
    //若失败则使用perror打印错误信息,并return -1
    if ((fp = fopen(argv[1], "r+")) == NULL)
        PRINT_ERR("fopen error");

    //若图片不是rgb888
    if(get_value(fp))
        return -1;
    printf("请输入马赛克的宽度(以像素点为单位):");
    scanf("%d",&n);
    set_mosic(fp, n);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值