Search…

Bitmap - Khái Niệm Và Các Xử Lý Cơ Bản

09/07/20207 min read
Kiến trúc file bmp và đọc ghi file bmp bằng C/C++.

Bitmap là một tập tin định dạng ảnh khá phổ biến còn được biết đến với tên tiếng anh là Windows bitmap. Các tập tin đồ họa lưu dưới dạng ảnh bitmap thường có đuôi là .BMP hoặc .DIB. Bài viết này sẽ hướng dẫn các bạn một số thao tác cơ bản để làm quen với ảnh bitmap và các xử lí cơ bản đối với một file ảnh Bitmap.

Các thuộc tính tiêu biểu của một tập tin ảnh bitmap

Các thuộc tính tiêu biểu của một tập tin ảnh bipmap cũng như một tập tin ảnh nói chung là:

  • Số bit trên mỗi điểm ảnh (bits per pixel) thường được ký hiệu bởi n. Một ảnh BMP n-bit có 2n màu. Giá trị n càng lớn thì ảnh càng có nhiều màu, và càng rõ nét hơn. Giá trị tiêu biểu của n là 1 (ảnh đen trắng), 4 (ảnh 16 màu), 8 (ảnh 256 màu), 16 (ảnh 65536 màu) và 24 (ảnh 16 triệu màu). Ảnh BMP 24-bit có chất lượng hình ảnh trung thực nhất.
  • Chiều cao của ảnh (height), cho bởi điểm ảnh (pixel).
  • Chiều rộng của ảnh (width), cho bởi điểm ảnh.

Cấu trúc tập tin bitmap

  • Tập tin bitmap (Device Independent Bitmap) là tập tin ảnh với định dạng cơ bản nhất.
  • Tập tin hình ảnh thường không nén bằng bất kì thuật toán nào, khi lưu ảnh các điểm ảnh sẽ được ghi trực tiếp vào tập tin – một điểm ảnh sẽ được mô tả bởi một hay nhiều byte tùy thuộc vào giá trị n của ảnh. Do đó, một hình ảnh lưu dưới dạng BMP thường có kích cỡ rất lớn, gấp nhiều lần so với các ảnh được nén (chẳng hạn GIF, JPEG hay PNG).

Cấu trúc tập tin ảnh BMP bao gồm 4 phần:

  • Bitmap Header (14 bytes): Giúp nhận dạng tập tin bitmap.
  • Bitmap Information (40 bytes): Lưu một số thông tin chi tiết giúp hiển thị ảnh.
  • Color Palette (4*x bytes), x là số màu của ảnh: Định nghĩa các màu sẽ được sử dụng trong ảnh.
  • Bitmap Data: Lưu dữ liệu ảnh.

Cấu trúc Header

Cấu trúc header file

  • bfType[2]: Kí hiệu cho biết định dạng file là bitmap, là 2 kí tự "BM".
  • bfSize[4]: Kích thước file.
  • bfReserved1[2]: Không sử dụng.
  • bfReserved2[2]: Không sử dụng.
  • bfOffBits[4]: Vị trí bắt đầu của nội dung file.
struct bmfh
{ 
      char           bfType[2];
      unsigned char  bfSize[4];
      unsigned char  bfReserved1[2];
      unsigned char  bfReserved2[2]; 
      unsigned char  bfOffBits[4];
};

Cấu trúc bitmap Information

Cấu trúc header ảnh

  • biSize[4]: Kích thước phần còn lại của header ảnh.
  • biWidth[4]: Chiều rộng của bitmap.
  • biHeight[4]: Chiều cao của bitmap.
  • biPlanes[2]: Number of Planes. Set to 1.
  • biBitCount[2]: Xác định độ phân giải màu sắc của bitmap.
    • 1 (2 màu trắng đen).
    • 4 (16 màu).
    • 8 (256 màu).
    • 24 (16,7 triệu màu). 
  • stuff1[16] : Loại nén (4 bytes, kích thước ảnh 4, độ phân giải theo chiều ngang 4, dọc 4.
  • biClrUsed[4] : Số lượng màu sắc sử dụng.
  • biClrImportant[4] : Number of  “Important” color.
struct bmih 
{
     unsigned char biSize[4];
     unsigned char biWidth[4];
     unsigned char biHeight[4];
     unsigned char biPlanes[2];
     unsigned char biBitCount[2];
     unsigned char stuff1[16]; 
     unsigned char biClrUsed[4];
     unsigned char biClrImportant[4];
};

Color Palette

Color Palette định nghĩa các màu sử dụng trong ảnh:

  • Gồm nhiều bộ có kích thước 4 bytes xếp liền nhau theo cấu trúc: Blue – Green – Red – Reserved.
  • Kích thước của bảng màu (4*x bytes), x là số màu sử dụng trong ảnh.
  • Note:
    • Bảng màu của màn hình có thứ tự : Red – Green – Blue.
    • Bảng màu của bitmap có thứ tự :  Blue – Green – Red.
    • Nên khi đọc bảng màu của ảnh bitmap cần phải chuyển đổi cho đúng thứ tự.

Bitmap Data

Bitmap data dùng để lưu dữ liệu ảnh.

  • Chứa giá trị màu của các điểm ảnh trong .bmp
  • Các điểm ảnh được lưu theo thứ tự từ trái qua phải trên 1 dòng  và các dòng được lưu theo thứ tự dưới lên trên.
  •  Mỗi byte trong vùng bitmap data biểu diễn 1 hoặc nhiều điểm ảnh tùy theo số bits trên 1 pixel.

Padding bytes

Thực tế khi một mảng các pixel được nạp vào bộ nhớ, mỗi hàng phải bắt đầu tại một địa chỉ bộ nhớ mà địa chỉ đó là bội số của 4. Do ta chỉ có sử dụng 3 byte cho mỗi pixel nên mỗi dòng có thể kết thúc với địa chỉ không chia hết cho 4, vì vậy trong mỗi dòng sẽ có những padding bytes để bù đắp số byte thiếu và đảm bảo rằng kết thúc mỗi dòng địa chỉ bộ nhớ luôn là bội số của 4.

Ví dụ như ảnh có kích thước 10x14 (14 là width) thì padding sẽ là 2 vì 14 x 3 = 42 (nhân 3 vì 3 là số byte trong mỗi pixel) và 42 % 4 = 2. Còn nếu bức ảnh có kích thước 3x4 (4 là width) thì padding sẽ là 0 vì 4x3 = 12 và 12 % 4 = 0.

Các xử lí cơ bản với một tập tin bitmap

Cấu trúc pixel

struct Pix
{
	unsigned char B;
	unsigned char G;
	unsigned char R;

};

Cấu trúc bitmap

struct BitMap
{
	short  m_signature;
	long   m_reserved1;
	long   m_reserved2;

	long   m_dataOffSet;

	long   m_size;
	long   m_width;
	long   m_height;
	short  m_planes;
	short  m_bpp;

	long   m_compression;
	long   m_sizeImage;

	long   m_xPixelsPreMeter;
	long   m_yPixelsPreMeter;

	long   m_colorsUsed;
	long   m_colorsImportant;
};

Đọc 1 file bitmap

void readBMP(const char* filePath, BitMap &header, char* &data)
{
	FILE* f = fopen(filePath, "rb");
	if (!f)
	{
		printf("Cannot open file for reading!!!");
		exit(-1);
	}

	fread(&header, sizeof(header), 1, f);

	int _padding = header.m_width % 4;
	int _size = header.m_width * header.m_height * (header.m_bpp / 8) + _padding * header.m_height; //(header.m_bpp)/8 ( số kênh màu )

	data = new char[_size];
	fread(data, sizeof(char), _size, f);

	fclose(f);
}

Ghi 1 file bitmap

void writeBMP(const char* filePath, BitMap &header, char* &data)
{
	FILE* f = fopen(filePath, "wb");
	if (!f)
	{
		printf("Cannot open file for writing!!!");
		exit(-1);
	}

	fwrite(&header, sizeof(header), 1, f);

	int _padding = header.m_width % 4;
	int _size = header.m_width * header.m_height * (header.m_bpp / 8) + _padding * header.m_height;

	fwrite(data, sizeof(char), _size, f);

	fclose(f);
}

Chuyển data của 1 ảnh bitmap vào một mảng các Pixel 

Pix* convertDataToPixelArray(char* &data, BitMap &header)
{
	int _size = header.m_width * header.m_height;
	Pix* _pixels = new Pix[_size];

	int _padding = header.m_width % 4;

	char* _temp = data;

	for (int i = 0; i < header.m_height; i++)
	{
		for (int j = 0; j < header.m_width; j++)
		{
			_pixels[i * header.m_height + j].B = *(_temp++);
			_pixels[i * header.m_height + j].G = *(_temp++);
			_pixels[i * header.m_height + j].R = *(_temp++);
		}

		_temp += _padding;
	}

	return _pixels;
}

Chuyển dữ liệu từ mảng các Pixel vào data của 1 ảnh bitmap

char* convertPixelArrayToData(Pix* &pixels, BitMap &header)
{
	int _padding = header.m_width % 4;
	int _size = header.m_width * header.m_height * (header.m_bpp / 8) + _padding * header.m_height;

	char* _data = new char[_size];
	char* _temp = _data;

	for (int i = 0; i < header.m_height; i++)
	{
		for (int j = 0; j < header.m_width; j++)
		{
			*(_temp++) = pixels[i * header.m_height + j].B;
			*(_temp++) = pixels[i * header.m_height + j].G;
			*(_temp++) = pixels[i * header.m_height + j].R;
		}

		for (int k = 0; k < _padding; k++)
		{
			*(_temp++) = 0;
		}
	}

	return _data;
}

Chuyển ảnh sang trắng đen

void grayscale(Pix* &pixels, int size)
{
	for (int i = 0; i < size; i++)
	{
		int _val = (pixels[i].R + pixels[i].G + pixels[i].B) / 3;
		pixels[i].R = pixels[i].G = pixels[i].B = _val;
	}
}

Project mẫu

Load một file bitmap lên xử lí chuyển sang màu trắng đen và lưu lại ở một tập tin bitmap khác.

Đọc ghi, tạo hiệu ứng cho bmp

RWBitmap.zip

IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024