基于深度学习的车牌识别项目的APP部分之图像预处理(二):C语言实现bmp的二值化处理


本文实现的带选择开关是输入图像是16位深度,输出保存图像分别可以是16位或者8位 ,8位的需要加上调色板如果不懂概念,请参考前面的文章

基于深度学习的车牌识别项目的APP部分之图像预处理(一):C语言读取bmp图像信息

一、二值化概念

二值化(英语:Binarization)是图像分割的一种最简单的方法。二值化可以把灰度图像转换成二值图像。把大于某个临界灰度值的像素灰度设为灰度极大值,把小于这个值的像素灰度设为灰度极小值,从而实现二值化。(来源维基百科)

图像二值化( Image Binarization)就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的黑白效果的过程。
在数字图像处理中,二值图像占有非常重要的地位,图像的二值化使图像中数据量大为减少,从而能凸显出目标的轮廓。

将256个亮度等级的灰度图像通过适当的阈值选取而获得仍然可以反映图像整体和局部特征的二值化图像。在数字图像处理中,二值图像占有非常重要的地位,首先,图像的二值化有利于图像的进一步处理,使图像变得简单,而且数据量减小,能凸显出感兴趣的目标的轮廓。其次,要进行二值图像的处理与分析,首先要把灰度图像二值化,得到二值化图像。
所有灰度大于或等于阈值的像素被判定为属于特定物体,其灰度值为255表示,否则这些像素点被排除在物体区域以外,灰度值为0,表示背景或者例外的物体区域。

(如果要得到二值化图像 ,要先经过灰度化)

二 、代码实现

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;
}

三、运行结果:

原图:

1、图像是8位深度的运行结果:


2、图像是16位深度的运行结果:


文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
乘风的头像乘风管理团队
上一篇 2022年5月12日
下一篇 2022年5月12日

相关推荐