• 使用标准IO对bitmap打马赛克


    使用标准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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    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);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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); //将光标移动到下一行
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    效果展示

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

    源码

    #include 
    
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
  • 相关阅读:
    项目干系人管理
    社会工程攻击依然是企业面临的最大威胁
    Docker 容器应急
    【鸿蒙软件开发】文本显示(Text/Span)
    31.JavaScript数组进阶,一网打尽数组操作函数slice、filter、map、reduce、some、every、find、splice
    Hello, Vector DB | AIGC 时代,你需要一个真正的向量数据库么?
    项目持续集成配置流程
    [激光原理与应用-20]:《激光原理与技术》-6- 谐振腔的结构、作用、工作原理
    这玩意儿都能优化?果然是细节都在魔鬼里。
    Java数组的一些小知识点
  • 原文地址:https://blog.csdn.net/qq_41555003/article/details/126692041