bmp image graying and binarization

Keywords: less Windows C

C language removes the background of bmp picture

I. Preface

In image processing, most of the processing methods need to convert color image into gray image in advance to carry out relevant calculation and recognition.
The principle of color image conversion to gray image is as follows:
We know that the color bitmap is composed of three components of R/G/B, and its file storage format is
BITMAPFILEHEADER+BITMAPINFOHEADER, followed by:

  • If it is a 24 bit true color image, each point is represented by three bytes R/G/B respectively, so the color information of the image is directly followed here;
  • If it is an 8-bit (256 colors), 4-bit (16 colors) and 1-bit (monochrome) graph, the palette data is immediately followed. An array of RGBQUAD type, whose length is determined by BITMAPINFOHEADER.biClrUsed.
    Then the image data is followed (24 bitmaps are real image data, others are index data of palette).

Gray scale image refers to an image with only brightness information and no color information, just like the black-and-white photos we usually see: the brightness changes continuously from dark to light. Therefore, in order to represent the gray-scale image, it is necessary to quantify the brightness value. It is usually divided into 256 levels from 0 to 255, of which 0 is the darkest (all black) and 255 is the brightest (all white). In the method of representing color, in addition to RGB, there is also a method called YUV, which has many applications. In TV signal, we use a kind of color representation similar to YUV. In this representation method, the physical meaning of Y component is brightness. Y component contains all the information of gray-scale image. Only Y component can represent a gray-scale image.
The Gray conversion formula from RGB to YUV space is:
Gray = R0.299 + G0.587 + B*0.114
In WINDOWS, the graph that represents more than 16 bits is a little different from the graph that represents less than 16 bits; the graph that represents less than 16 bits uses a palette to select a specific color, each unit of the palette is 4 bytes, one of which is transparent; the specific pixel value stores an index, which is 1, 2, 4, 8 bits respectively. 16 bit or more images use pixels to represent colors directly.
So how to convert color image to gray image?
There is a palette in the gray-scale image. First, we need to determine the specific color value of the palette. As we mentioned earlier, the three components of a grayscale image are equal.
When converted to 8 bits, there are 256 colors in the palette, each from exactly 0 to 255, with three equal components.
When it is converted to 4 bits, 16 colors in the palette are equally divided into 255 color values at equal intervals, and all three components are equal.
When it is converted to 2 bits, there are 4 colors in the palette, with equal interval of 255 colors, and the three components are equal.
When converted to 1 bit, two colors in the palette, 0 and 255, represent black and white.
When the color is converted to grayscale, the corresponding value is calculated according to the formula, which is actually the brightness level; the brightness is from 0 to 255; because different bits have different brightness levels, the specific value of Y is as follows: y = Y / (1 < (8-converted digits));
Therefore, we need to convert it into gray-scale image and store it into a visible image as follows:
Images with more than 16 bits do not have color palette, only need to convert image data into the same gray value according to the number of bits of each point
For images below 16 bits, you need to modify the value of the palette, and modify the gray value index according to the number of bits occupied by each point.

What? I don't understand after saying so much???

  • 24 bit true color picture file header + picture header + bitmap data (the kind of picture you usually see)
  • 8-bit grayscale file information header + picture information header + palette + bitmap data (that is, the image with the effect found by the black-and-white camera)

Binary image (also known as single value image) has one bit per pixel, i.e. black and white image. The value of each pixel is not 0 or 1.
Each pixel of gray image is 8 bit, ranging from 0 to 255. It has color palette, and the pixel value is the entry of table item.
The pseudo color image has 8 bits per pixel, ranging from 0-255. It has a palette, and the pixel value is the entry of the table item.
Each pixel of true color image is 24bit, and each pixel is composed of independent R, G and B components, each of which accounts for 8bit.

What we need to do now is to convert a 24 bit bitmap into an 8-bit gray-scale image.

2, Grayscale

Header file

# ifndef BMP_H
# define BMP_H

/*bitmap-file header */
#pragma pack(1)//Single byte alignment
typedef struct tagBITMAPFILEHEADER
{
	  unsigned char  bfType[2];//file format
    unsigned int   bfSize;        // File size in bytes (2-5 bytes)
    unsigned short bfReserved1;   // Reserved, must be set to 0 (6-7 bytes)
    unsigned short bfReserved2;   // Reserved, must be set to 0 (8-9 bytes)
    unsigned int   bfOffBits;     // Offset from file header to pixel data (10-13 bytes)
}BITMAPFILEHEADER;
#pragma pack()

/*Bitmap header*/
#pragma pack(1)
typedef struct tagBITMAPINFOHEADER
{
    unsigned int    biSize;          // Size of this structure (14-17 bytes)
    long            biWidth;         // Image width (18-21 bytes)
    long            biHeight;        // Image height (22-25 bytes)
    unsigned short  biPlanes;        // Represents the plane genus of bmp picture, obviously the display has only one plane, so it is equal to 1 (26-27 bytes)
    unsigned short  biBitCount;      // The number of bits occupied by a pixel, (28-29 bytes) when biBitCount=24, the BMP image is a 24Bit true color image without palette items.
    unsigned int    biCompression;   // Describes the type of image data compression. 0 is not compressed. (30-33 bytes)
    unsigned int    biSizeImage;     // The size of pixel data, which should be equal to bfsize bfoffbits (34-37 bytes) in the file header structure above
    long            biXPelsPerMeter; // Indicates the horizontal resolution in pixels per meter. Generally 0 (38-41 bytes)
    long            biYPelsPerMeter; // Indicates the vertical resolution in pixels per meter. Generally 0 (42-45 bytes)
    unsigned int    biClrUsed;       // Describes the number of color indexes in the color table that the bitmap actually uses (if set to 0, all palette items are used). (46-49 bytes)
    unsigned int    biClrImportant;  // Indicates the number of color indexes that have important influence on image display. If it is 0, it means that they are all important. (50-53 bytes)
}BITMAPINFOHEADER;
#pragma pack()

/*Palette structure*/
#pragma pack(1)
typedef struct tagRGBQUAD
{
    unsigned char rgbBlue;   //Blue component of the color (value range 0-255)
    unsigned char rgbGreen;  //Green component of the color (value range 0-255)
    unsigned char rgbRed;    //Red component of the color (value range 0-255)
    unsigned char rgbReserved;// Reserved, must be 0
}RGBQUAD;
#pragma pack()

#endif 

Here you see the "pragma pack (1)" and "pragma pack(). Haven't you seen this? I haven't seen it before. Let's see: Structure alignment details

C file

#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include"bmp.h"

int main()
{
	unsigned char ImgData[3000][3];//Convert bitmap information into a row of pixels of RGB image stored in grayscale	
	unsigned char ImgData2[3000];//Save the pixels of the gray-scale image into a one-dimensional array
	int i, j, k;
	FILE * fpBMP, *fpGray;
	BITMAPFILEHEADER *fileHeader;
	BITMAPINFOHEADER *infoHeader;
	RGBQUAD * ipRGB;
	char filename1[20], filename2[20];

	printf("Enter image file name:");
	scanf("%s", filename1);
	if ((fpBMP = fopen(filename1, "rb")) == NULL)
	{
		printf("Failed to open picture");
		exit(0);
	}
	printf("Output image filename:");
	scanf("%s", filename2);
	if ((fpGray = fopen(filename2, "wb")) == NULL)
	{
		printf("Failed to create picture");
		exit(0);
	}
	//Request the memory space of this structure size for the defined structure variable
	fileHeader = (BITMAPFILEHEADER *)malloc(sizeof(BITMAPFILEHEADER));
	infoHeader = (BITMAPINFOHEADER *)malloc(sizeof(BITMAPINFOHEADER));
	//Read data block to file information header and picture information header from bmp file
	fread(fileHeader, sizeof(BITMAPFILEHEADER), 1, fpBMP);
	fread(infoHeader, sizeof(BITMAPINFOHEADER), 1, fpBMP);
    //Through these two programs, the information header and file header of BMP image are assigned to fileHeader and infoHeader variables, and various attributes of image can be obtained according to fileHeader and infoHeader.
	printf("The number of bits per pixel of the original picture:%d\n" ,infoHeader->biBitCount);   
    printf("Pixel data offset of each pixel of the original picture:%d\n" ,fileHeader->bfOffBits);   

    //Modify header
    //There are 11 parts in the header, and two parts need to be modified when graying	
	infoHeader->biBitCount = 8;//Convert 24 bit true color image to 8-bit gray-scale image
	infoHeader->biSizeImage = ((infoHeader->biWidth * 3 + 3) / 4) * 4 * infoHeader->biHeight;//The actual number of bytes occupied by each line of 24Bit true color picture
	//Modify file header
    //There are 5 parts in the file header, two parts need to be modified when graying
	fileHeader->bfOffBits = sizeof( BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
	fileHeader->bfSize = fileHeader->bfOffBits + infoHeader->biSizeImage;
	printf("The number of bits per pixel of the modified picture:%d\n" ,infoHeader->biBitCount);   
    printf("Each pixel data offset of the modified picture:%d\n" ,fileHeader->bfOffBits);   

	//The palette of gray image is R=G=B. the three values of true color image are not equal
	ipRGB = (RGBQUAD *)malloc(256 * sizeof(RGBQUAD));
	for (i = 0; i < 256; i++)
	{
		ipRGB[i].rgbBlue = ipRGB[i].rgbGreen = ipRGB[i].rgbRed = i;
	}
	//Read the information header, file header, BMP palette of BMP image to the new image
	fwrite(fileHeader, sizeof(BITMAPFILEHEADER), 1,   fpGray);
	fwrite(infoHeader, sizeof(BITMAPINFOHEADER), 1,   fpGray);
	fwrite(ipRGB     , sizeof(RGBQUAD)         , 256, fpGray);
	//Read RGB image pixels and convert them to grayscale values
	for (i = 0; i < infoHeader->biHeight; i++)//Line by line scanning
	{
		//The actual number of bytes occupied by each line of a 24Bit true color picture is [(biWidth*3+3)/4*4] so each line of biHeight is scanned [(biWidth*3+3)/4*4] times
		for (j = 0; j < (infoHeader->biWidth + 3) / 4 * 4; j++)//Write bitmap data part of BMP image 
		{
			for (k = 0; k < 3; k++)
				fread(&ImgData[j][k], 1, 1, fpBMP);////Read only one byte at a time and store in the array 
		}
		//The part of modifying bitmap data is mainly from the rgbRed, rgbGreen and rgbBlue components of the original color image to the gray value Y of the gray image, which can be obtained by the following formula:
		for (j = 0; j < (infoHeader->biWidth + 3) / 4 * 4; j++)  
		{
			ImgData2[j] = (int)((float)ImgData[j][0] * 0.114 +
								(float)ImgData[j][1] * 0.587 +
								(float)ImgData[j][2] * 0.299  );
		}
		//Write grayscale information
		fwrite(ImgData2, j, 1, fpGray);//Write parts of BMP image in order
	}
	free(fileHeader);
	free(infoHeader);
	free(ipRGB);
	fclose(fpBMP);
	fclose(fpGray);
	printf("bmp Background removal completed\n");
	return 0;
}

At this point, 24 bitmaps are converted to 8-bit gray-scale images.
Take a look at the effect.

To enter the path of the picture here, it is recommended to put it directly into the project directory. It's OK to put it in other places, just fill in the path of the picture. I can also type D:\c_test\bmp-cleancover .bmp. Because my picture 1.bmp is placed in the BMP cleancover folder of the c'u test on disk D.
Compare the results:


How do you feel different?

I thought what I wanted to accomplish was this effect. Consult others and tell me to binarize. It's just grayscale. Binarization means that the picture is not black or white.

3, Binarization

#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include"bmp.h"

int main()
{
  /*Variable declaration*/
  FILE *fpBMP,*fpTwoValue;//Source file fpBMP, target file fpTwoValue
  char filename1[20], filename2[20];

  BITMAPFILEHEADER *fileHeader;//bitmap-file header 
  BITMAPINFOHEADER *infoHeader;//Bitmap header
  RGBQUAD *ipRGB;//palette

  int i,j,k=0;
  unsigned char *a;//Store pixel values of each line of source map
  unsigned char b;//Store gray value or binary value of each pixel
  unsigned char *c;//Stores the binary value of each row of pixels
  

	printf("Enter image file name:");
	scanf("%s", filename1);
	if ((fpBMP = fopen(filename1, "rb")) == NULL)
	{
		printf("Failed to open picture");
		exit(0);
	}
  
	printf("Output image filename:");
	scanf("%s", filename2);
	if ((fpTwoValue = fopen(filename2, "wb")) == NULL)
	{
		printf("Failed to create picture");
		exit(0);
	}
  /********************************************************************/
  
  /*Create bitmap file header, information header, palette*/
  fileHeader=(BITMAPFILEHEADER *)malloc(sizeof(BITMAPFILEHEADER));
  infoHeader=(BITMAPINFOHEADER *)malloc(sizeof(BITMAPINFOHEADER));
  ipRGB=(RGBQUAD *)malloc(2*sizeof(RGBQUAD));
  
  /*Read in source bitmap file header and information header*/
  fread(fileHeader,sizeof(BITMAPFILEHEADER),1,fpBMP);
  fread(infoHeader,sizeof(BITMAPINFOHEADER),1,fpBMP);
  //Through these two programs, the information header and file header of BMP image are assigned to fileHeader and infoHeader variables, and various attributes of image can be obtained according to fileHeader and infoHeader.
	printf("The number of bits per pixel of the original picture:%d\n" ,infoHeader->biBitCount);   
  printf("Pixel data offset of each pixel of the original picture:%d\n" ,fileHeader->bfOffBits);   

  //Modify header
  //There are 11 parts in the header, and 4 parts need to be modified when graying	
  infoHeader->biBitCount=8;//After conversion to binary graph, the color depth changes from 24 bits to 8 bits
  infoHeader->biSizeImage=((infoHeader->biWidth+3)/4)*4*infoHeader->biHeight;//Each pixel is changed from three bytes to a single byte, and each row of pixels is aligned with four bytes
  infoHeader->biClrUsed=2;//Number of color index table, 2-value chart
  infoHeader->biClrImportant=0;//The important color index is 0, indicating that all are important
  //Modify file header
  //There are 5 parts in the document header, and two parts need to be modified when graying
  fileHeader->bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+2*sizeof(RGBQUAD);//Data area offset equal to the sum of file header, information header and index table sizes
  fileHeader->bfSize=fileHeader->bfOffBits+infoHeader->biSizeImage;//File size, equal to offset plus data area size
  ipRGB[1].rgbBlue=ipRGB[1].rgbGreen=ipRGB[1].rgbRed=ipRGB[1].rgbReserved=0;//Palette color is black with index 0
  ipRGB[0].rgbBlue=ipRGB[0].rgbGreen=ipRGB[0].rgbRed=190;//The index corresponding to white is 150-255
  ipRGB[1].rgbReserved=0;
  printf("The number of bits per pixel of the modified picture:%d\n" ,infoHeader->biBitCount);   
  printf("Each pixel data offset of the modified picture:%d\n" ,fileHeader->bfOffBits);   

  /********************************************************************/
  
  //Read the information header, file header, BMP palette of BMP image to the new image
  fwrite(fileHeader,sizeof(BITMAPFILEHEADER),1,fpTwoValue);
  fwrite(infoHeader,sizeof(BITMAPINFOHEADER),1,fpTwoValue);
  fwrite(ipRGB,2*sizeof(RGBQUAD),1,fpTwoValue);

  /*Convert color map to binary map*/
  a=(unsigned char *)malloc((infoHeader->biWidth*3+3)/4*4);//Apply the space occupied by each row of pixels in the source graph for variable a, and consider the four byte alignment problem
  c=(unsigned char *)malloc((infoHeader->biWidth+3)/4*4);//Apply the space occupied by each row of pixels in the target graph for variable c, and align the same four bytes
  
  for(i=0;i<infoHeader->biHeight;i++)//Loop through each line of the image
  {
    for(j=0;j<((infoHeader->biWidth*3+3)/4*4);j++)//Loop through each byte in each line
    {
        fread(a+j,1,1,fpBMP);//Read each byte of each line of the source graph into the memory space pointed to by variable a
    }
    for(j=0;j<infoHeader->biWidth;j++)//Cycle the pixel width times, and the four byte padding bit read in will not be calculated
    {
        b=(int)(0.114*(float)a[k]+0.587*(float)a[k+1]+0.299*(float)a[k+2]);//Every three bytes in a represent BGR component respectively, multiplying different weights and converting them into gray value
        if(160<=(int)b) //The gray value is converted to binary value, and the threshold value selected here is 160-190
           b=1;
        else b=0;
             c[j]=b;   //Store the binary value of each line
             k+=3;
    }
    fwrite(c,(infoHeader->biWidth+3)/4*4,1,fpTwoValue);//Write the binary pixel four byte fill to the file, the fill bit is not initialized, it is a random value
    k=0;
  }
  
  /*Free up memory, close files*/
  free(fileHeader);
  free(infoHeader);
  free(ipRGB);
  free(a);
  free(c);
  fclose(fpBMP);
  fclose(fpTwoValue);
  printf("bmp Background removal completed\n");
  return 0;
}

Just commission the C file to the above one. The header file does not need to be changed. Because the structural member variables of bmp images are fixed, you only need to change the parameters.

This time we can see the effect. It is obvious that the background has been removed.

Just copy the C file and H file to your program. We need to understand the principle of the program in detail. If you encounter problems, you can search on the Internet, involving the structure and file pointer. I also read a lot of information.
Alas, it's not easy to code. If passing by is helpful to you, please click "like".

Published 21 original articles, won praise 8, visited 4801
Private letter follow

Posted by robb73 on Thu, 12 Mar 2020 06:05:26 -0700