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.