BMP图片读入后写出发生偏移

用程序读入BMP的信息,将该信息重新写出生成图片时,图片右侧一小部分移到了图片的左侧。请各位帮我看看问题出在哪,帮帮我,谢谢!
代码如下:


```c++

typedef struct {
    BITMAP        bmp;       // BITMAP構造体
        RGBQUAD  *rgb ;
} BMPDATA;


bool ReadBmp(string strbmpName, int & m_nBiBitCount)
{
    FILE *fp = new FILE;
    int resOpen = fopen_s(&fp, strbmpName.c_str(), "rb");
    if (resOpen)
    {
        return false;
    }

    fseek(fp, sizeof(BITMAPFILEHEADER), 0);
    //BITMAPFILEHEADER filehead;
    //fread(&filehead, 1, sizeof(BITMAPFILEHEADER), fp);

    BMPDATA* m_pBitmapData = new BMPDATA;
    BITMAPINFOHEADER bitmapInfoHeader;

    //定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中
    fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, fp); //获取图像宽、高、每像素所占位数等信息
    m_nBiBitCount = bitmapInfoHeader.biBitCount;//定义变量,计算图像每行像素所占的字节数(必须是4的倍数)

    m_pBitmapData->bmp.bmType = 0;
    m_pBitmapData->bmp.bmWidth = bitmapInfoHeader.biWidth;
    m_pBitmapData->bmp.bmHeight = bitmapInfoHeader.biHeight;
    int lineByte = (m_pBitmapData->bmp.bmWidth * m_nBiBitCount / 8 + 3) / 4 * 4;//灰度图像有颜色表,且颜色表表项为256
    //int lineByte = ((((m_pBitmapData->bmp.bmWidth * m_nBiBitCount) + 31) & ~31) >> 3);
    m_pBitmapData->bmp.bmWidthBytes = lineByte;
    m_pBitmapData->bmp.bmPlanes = bitmapInfoHeader.biPlanes;    //颜色平面数
    m_pBitmapData->bmp.bmBitsPixel = bitmapInfoHeader.biBitCount;

    if (m_nBiBitCount == 8)
    {
        //申请颜色表所需要的空间,读颜色表进内存
        RGBQUAD *pQuad = new RGBQUAD[256];
        fread(pQuad, sizeof(RGBQUAD), COLOR_TABLE_ENTRIES, fp);

        m_pBitmapData->rgb = pQuad;
    }

    m_pBitmapData->bmp.bmBits = new unsigned char[lineByte * m_pBitmapData->bmp.bmHeight];
    fread(m_pBitmapData->bmp.bmBits, sizeof(UCHAR), lineByte * m_pBitmapData->bmp.bmHeight, fp);

    fclose(fp);//关闭文件

    return true;
}

bool SaveBmp(string strBmpName, BMPDATA *m_pBitmapData, int  m_nBiBitCount)
{
    //如果位图数据指针为0,则没有数据传入,函数返回
    if (m_pBitmapData == NULL)
    {
        return false;
    }
    if (!m_pBitmapData->bmp.bmBits)
    {
        return false;
    }

    //颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0
    int colorTablesize = 0;
    if (m_nBiBitCount == 8)
    {
        colorTablesize = 1024;
    }

    //以二进制写的方式打开文件
    FILE *fp = new FILE;
    int resOpen = fopen_s(&fp, strBmpName.c_str(), "wb");

    if (resOpen)
    {
        return false;
    }

    //申请位图文件头结构变量,填写文件头信息
    BITMAPFILEHEADER fileHead;
    fileHead.bfType = 0x4D42;//bmp类型

    //bfSize是图像文件4个组成部分之和
    fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTablesize +
        m_pBitmapData->bmp.bmWidthBytes * m_pBitmapData->bmp.bmHeight;
    fileHead.bfReserved1 = 0;
    fileHead.bfReserved2 = 0;
    //bfOffBits是图像文件前3个部分所需空间之和
    fileHead.bfOffBits = 54 + colorTablesize;

    //写文件头进文件
    fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp);

    //申请位图信息头结构变量,填写信息头信息
    BITMAPINFOHEADER infohead;
    infohead.biBitCount = m_nBiBitCount;
    infohead.biClrImportant = 0;
    infohead.biClrUsed = 0;
    infohead.biCompression = 0;
    infohead.biHeight = m_pBitmapData->bmp.bmHeight;
    infohead.biPlanes = 1;
    infohead.biSize = 40;
    infohead.biSizeImage = m_pBitmapData->bmp.bmWidthBytes * sizeof(RGBTRIPLE) * abs(m_pBitmapData->bmp.bmHeight);
    infohead.biWidth = m_pBitmapData->bmp.bmWidth;
    infohead.biXPelsPerMeter = 0;
    infohead.biYPelsPerMeter = 0;

    //写位图信息头进内存
    fwrite(&infohead, sizeof(BITMAPINFOHEADER), 1, fp);

    //如果灰度图像,有颜色表,写入文件 
    if (m_nBiBitCount == 8 && m_pBitmapData->rgb)
    {
        fwrite(m_pBitmapData->rgb, sizeof(RGBQUAD), COLOR_TABLE_ENTRIES, fp);
    }

    //写位图数据进文件
    fwrite(m_pBitmapData->bmp.bmBits, m_pBitmapData->bmp.bmHeight * m_pBitmapData->bmp.bmWidthBytes, 1, fp);

    //关闭文件
    fclose(fp);

    return true;
}

```

  • 你可以看下这个问题的回答https://ask.csdn.net/questions/236281
  • 除此之外, 这篇博客: 基于深度学习的车牌识别项目的APP部分之图像预处理(二):C语言实现bmp的二值化处理中的 二 、代码实现 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • bmp.h

    # ifndef BMP_H
    # define BMP_H
    
    /*位图文件头*/
    #pragma pack(1)//单字节对齐
    typedef struct tagBITMAPFILEHEADER
    {
        unsigned char  bfType[2];     //文件格式
        unsigned int   bfSize;        // 文件大小 以字节为单位(2-5字节)
        unsigned short bfReserved1;   // 保留,必须设置为0 (6-7字节)
        unsigned short bfReserved2;   // 保留,必须设置为0 (8-9字节)
        unsigned int   bfOffBits;     // 从文件头到像素数据的偏移  (10-13字节)
    }BITMAPFILEHEADER;
    #pragma pack()
    /*位图信息头*/
    #pragma pack(1)
    typedef struct tagBITMAPINFOHEADER
    {
        unsigned int    biSize;          // 此结构体的大小 (14-17字节)
        long            biWidth;         // 图像的宽  (18-21字节)
        long            biHeight;        // 图像的高  (22-25字节)
        unsigned short  biPlanes;        // 表示bmp图片的平面属,显然显示器只有一个平面,所以恒等于1 (26-27字节)
        unsigned short  biBitCount;      // 一像素所占的位数,(28-29字节)当biBitCount=24时,该BMP图像就是24Bit真彩图,没有调色板项。
        unsigned int    biCompression;   // 说明图象数据压缩的类型,0为不压缩。(30-33字节)
        unsigned int    biSizeImage;     // 像素数据所占大小, 这个值应该等于上面文件头结构中bfSize-bfOffBits (34-37字节)
        long            biXPelsPerMeter; // 说明水平分辨率,用象素/米表示。一般为0 (38-41字节)
        long            biYPelsPerMeter; // 说明垂直分辨率,用象素/米表示。一般为0 (42-45字节)
        unsigned int    biClrUsed;       // 说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。(46-49字节)
        unsigned int    biClrImportant;  // 说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。(50-53字节)
    }BITMAPINFOHEADER;
    #pragma pack()
    
    /*调色板结构*/
    #pragma pack(1)
    typedef struct tagRGBQUAD
    {
        unsigned char rgbBlue;   //该颜色的蓝色分量  (值范围为0-255)
        unsigned char rgbGreen;  //该颜色的绿色分量  (值范围为0-255)
        unsigned char rgbRed;    //该颜色的红色分量  (值范围为0-255)
        unsigned char rgbReserved;// 保留,必须为0
    }RGBQUAD;
    #pragma pack()
    
    #endif
    
    #include<stdio.h>
    #include<malloc.h>
    #include<stdlib.h>
    #include"bmp.h"
    
    //#define DEBUG   //没注释 是图像处理前16 二值化后的图像8位   注释了 是图像处理前和处理后 图像深度16位
    
    
    int main()
    {
      /*变量声明*/
      FILE *fpBMP,*fpTwoValue;//源文件fpBMP,目标文件fpTwoValue
      char filename1[20], filename2[20];
    
      BITMAPFILEHEADER *fileHeader;//位图文件头
      BITMAPINFOHEADER *infoHeader;//位图信息头
    #ifdef DEBUG  
      RGBQUAD *ipRGB;//调色板 读入的图片为16位 和 保存的图像为16位不需要调色板
    #endif
    
      int i,j;
      unsigned char *a;//存储源图每行像素值
      unsigned char *c;//存储每行像素的二值
      
      printf("输入图像文件名:");
      scanf("%s", filename1);
      if ((fpBMP = fopen(filename1, "rb")) == NULL)
      {
          printf("打开图片失败");
          exit(0);
      }
      printf("输出图像文件名:");
      scanf("%s", filename2);
      if ((fpTwoValue = fopen(filename2, "wb")) == NULL)
      {
          printf("创建图片失败");
          exit(0);
      }
      /********************************************************************/
      
      /*创建位图文件头,信息头,调色板*/
      fileHeader=(BITMAPFILEHEADER *)malloc(sizeof(BITMAPFILEHEADER));
      infoHeader=(BITMAPINFOHEADER *)malloc(sizeof(BITMAPINFOHEADER));
    #ifdef DEBUG
      ipRGB=(RGBQUAD *)malloc(2*sizeof(RGBQUAD));
    #endif
      
      /*读入源位图文件头和信息头*/
      fread(fileHeader,sizeof(BITMAPFILEHEADER),1,fpBMP);
      fread(infoHeader,sizeof(BITMAPINFOHEADER),1,fpBMP);
      //经过这两条程序把BMP图像的信息头、文件头赋给fileHeader和infoHeader变量,可以根据fileHeader和infoHeader得到图像的各种属性。
      printf("原始图片每个像素的位数:%d\n" ,infoHeader->biBitCount);
      printf("原始图片每个像素像素数据偏移:%d\n" ,fileHeader->bfOffBits);
    
    
    #ifdef DEBUG  
      //修改信息头
      //信息头共有11部分,灰度化时需要修改4部分 这里位深度是16且没有变 只需要改2部分
      infoHeader->biBitCount=8;//转换成二值图后,颜色深度由16位变为8位
      infoHeader->biSizeImage=((infoHeader->biWidth*1+3)/4)*4*infoHeader->biHeight;//每个像素由2字节变为1字节,同时每行像素要四字节对齐
      infoHeader->biClrUsed=2;//颜色索引表数量,二值图为2
      infoHeader->biClrImportant=0;//重要颜色索引为0,表示都重要
    #else
      //修改信息头
      //信息头共有11部分,灰度化时需要修改4部分 这里位深度是16且没有变 只需要改2部分
      //infoHeader->biBitCount=8;//转换成二值图后,颜色深度由16位变为8位
      //infoHeader->biSizeImage=((infoHeader->biWidth*1+3)/4)*4*infoHeader->biHeight;//每个像素由2字节变为1字节,同时每行像素要四字节对齐
      infoHeader->biClrUsed=2;//颜色索引表数量,二值图为2
      infoHeader->biClrImportant=0;//重要颜色索引为0,表示都重要
    #endif
    
    
    #ifdef DEBUG 
      //修改文件头
      //文件头共有5部分,灰度化时需要修改5部分  用到调色板 
      fileHeader->bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+2*sizeof(RGBQUAD);//数据区偏移量,等于文件头,信息头,索引表的大小之和
      fileHeader->bfSize=fileHeader->bfOffBits+infoHeader->biSizeImage;//文件大小,等于偏移量加上数据区大小
      ipRGB[0].rgbBlue=ipRGB[0].rgbGreen=ipRGB[0].rgbRed=ipRGB[0].rgbReserved=255; //白色对应的索引为150-255
      ipRGB[1].rgbBlue=ipRGB[1].rgbGreen=ipRGB[1].rgbRed=ipRGB[1].rgbReserved=0;//调色板颜色为黑色对应的索引为0 
    #else
      //修改文件头
      //文件头共有5部分,灰度化时需要修改1部分  没有用到调色板 
      fileHeader->bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);//数据区偏移量,等于文件头,信息头,索引表的大小之和
    #endif
    
      printf("修改后的图片每个像素的位数:%d\n" ,infoHeader->biBitCount);
      printf("修改后的图片每个像素数据偏移:%d\n" ,fileHeader->bfOffBits);
    
      /********************************************************************/
      //读取BMP图像的信息头、文件头、BMP调色板到新建的图片
      fwrite(fileHeader,sizeof(BITMAPFILEHEADER),1,fpTwoValue);
      fwrite(infoHeader,sizeof(BITMAPINFOHEADER),1,fpTwoValue);
    #ifdef DEBUG 
      fwrite(ipRGB,2*sizeof(RGBQUAD),1,fpTwoValue);
    #endif
      /*将彩色图转为二值图*/   
      // (infoHeader->biWidth*biBitCount+31)/8/4*4
      // 这里 biBitCount = 16  表达式 (infoHeader->biWidth*16+31)/8/4*4
      // 化简后 (infoHeader->biWidth*2+3)/4*4 用的是数学上的取整操作
    
      a=(unsigned char *)malloc((infoHeader->biWidth*2+3)/4*4);//给变量a申请源图每行像素所占大小的空间,考虑四字节对齐问题
    #ifdef DEBUG
      c=(unsigned char *)malloc((infoHeader->biWidth*1+3)/4*4);//给变量c申请目标图每行像素所占大小的空间,同样四字节对齐
    #else
      c=(unsigned char *)malloc((infoHeader->biWidth*2+3)/4*4);//给变量c申请目标图每行像素所占大小的空间,同样四字节对齐
    #endif
    
      for(i=0;i<infoHeader->biHeight;i++)//遍历图像每行的循环
      {
    
        for(j=0;j<((infoHeader->biWidth*2+3)/4*4);j++)//遍历每行中每个字节的循环
        {
            fread(a+j,1,1,fpBMP);//将源图每行的每一个字节读入变量a所指向的内存空间
        }
      #ifdef DEBUG
        for(j=0;j<(infoHeader->biWidth*1);j++)//循环像素宽度次,就不会计算读入四字节填充位
      #else
        for(j=0;j<(infoHeader->biWidth*2);j++)//循环像素宽度次,就不会计算读入四字节填充位
      #endif
        {
          #ifdef DEBUG  
            //白色为RGB分量都有
            //r = pixel & 0xf800
            //g = pixel & 0x07e0
            //b = pixel & 0x1f
            if(100<=(a[j*2+1] & 0xf8)) //将灰度值转化为二值 这里由于白色含有红绿蓝 ,所以选取其中一个分量比较 ,大于就是255 小于就是0
              c[j]=0;
            else 
              c[j]=255;
          #else
            if(j%2==0)
            {
              if(100<=(a[j+1] & 0xf8)) //将灰度值转化为二值,这里选取的阈值为160-190都可以
                c[j]=255;
              else 
                c[j]=0;
            }
            else
              c[j] = c[j-1];   //存储每行的二值
          #endif
        }
      #ifdef DEBUG 
        fwrite(c,(infoHeader->biWidth*1+3)/4*4,1,fpTwoValue);//将二值像素4字节填充写入文件,填充位没有初始化,为随机值
      #else 
        fwrite(c,(infoHeader->biWidth*2+3)/4*4,1,fpTwoValue);//将二值像素四字节填充写入文件,填充位没有初始化,为随机值
      #endif
      }
      /*释放内存空间,关闭文件*/
      free(fileHeader);
      free(infoHeader);
      // free(ipRGB);
      free(a);
      free(c);
      fclose(fpBMP);
      fclose(fpTwoValue);
      printf("bmp图片背景去除完成\n");
      return 0;
    }
    
  • 您还可以看一下 贺利坚老师的C++语言基础视频教程课程中的 7.7 案例:bmp文件格式剖析小节, 巩固相关知识点
  • 以下回答由chatgpt基于相关博客总结生成:

    我需要您提供代码,以便更好地了解您的问题和提供解决方案。由参考资料可以了解到BMP图片格式的组成,但是无法确定具体是哪里出现了偏移,也无法确定代码中的问题,因此无法提供具体的解决方案。请提供您的代码,谢谢。