Created
July 6, 2024 13:32
-
-
Save h4k1m0u/5c404ee6fcfbb93bcbcd823cd55be669 to your computer and use it in GitHub Desktop.
Parse bitmap image to find RGB values at given row and column
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <math.h> | |
/** | |
* Parse bitmap image to find RGB values at given row and column | |
* Bitmap format: https://en.wikipedia.org/wiki/BMP_file_format | |
* Tested input bitmap image saved in Gimp without color space information and in 24bits format | |
* For debug | |
* Show only first 100 body bytes: `od --endian=little -t x1 -j 54 -N 100 rgb.bmp` | |
* Show only header: `od --endian=little -t x1 -N 54 rgb.bmp` | |
* How to build: | |
* `gcc -lm read_bitmap.c` | |
*/ | |
int main(int argc, char* argv[]) { | |
if (argc != 4) { | |
printf("USAGE: %s BMP_IMAGE X Y\n", argv[0]); | |
printf("Given X, Y relative to upper-left corner"); | |
return 1; | |
} | |
const char* path_bmp = argv[1]; | |
FILE* file_bmp = fopen(path_bmp, "rb"); | |
if (file_bmp == NULL) { | |
perror("ERROR"); | |
return 1; | |
} | |
char* endptr_col; | |
const char* col_str = argv[2]; | |
long int col = strtol(col_str, &endptr_col, 10) ; | |
if (endptr_col == col_str) { | |
printf("Wrong format for column\n"); | |
fclose(file_bmp); | |
return 1; | |
} | |
char* endptr_row; | |
const char* row_str = argv[3]; | |
long int row = strtol(row_str, &endptr_row, 10) ; | |
if (endptr_row == row_str) { | |
printf("Wrong format for row\n"); | |
fclose(file_bmp); | |
return 1; | |
} | |
/* bitmap file header table */ | |
typedef struct { | |
int16_t magic_number; | |
int32_t file_size; | |
int16_t reserved1; | |
int16_t reserved2; | |
int32_t offset; | |
} MetadataFile; | |
MetadataFile metadata_file; | |
// magic number (in little endian) | |
fread(&metadata_file.magic_number, sizeof(metadata_file.magic_number), 1, file_bmp); | |
printf("magic number: %x\n", metadata_file.magic_number); | |
char b = metadata_file.magic_number & 0xff; | |
char m = metadata_file.magic_number >> 8; | |
char magic_number[3] = { b, m, '\0' }; | |
if (strcmp(magic_number, "BM") != 0) { | |
printf("Not a valid bitmap file\n"); | |
fclose(file_bmp); | |
return 1; | |
} | |
fread(&metadata_file.file_size, sizeof(metadata_file.file_size), 1, file_bmp); | |
fread(&metadata_file.reserved1, sizeof(metadata_file.reserved1), 1, file_bmp); | |
fread(&metadata_file.reserved2, sizeof(metadata_file.reserved2), 1, file_bmp); | |
fread(&metadata_file.offset, sizeof(metadata_file.offset), 1, file_bmp); | |
printf("filesize in bytes: %d\n", metadata_file.file_size); | |
printf("offset in bytes: %d\n", metadata_file.offset); | |
/* bitmap information header table */ | |
typedef struct { | |
int32_t size_header; | |
int32_t width_image; | |
int32_t height_image; | |
int16_t n_color_planes; | |
int16_t n_bits_per_pixel; | |
int32_t compression_method; | |
int32_t n_bytes_body; // includes padding | |
int32_t resolution_h; | |
int32_t resolution_v; | |
int32_t n_colors; | |
int32_t n_important_colors; | |
} MetadataImage; | |
MetadataImage metadata_image; | |
fread(&metadata_image.size_header, sizeof(metadata_image.size_header), 1, file_bmp); | |
printf("size header in bytes: %d\n", metadata_image.size_header); | |
fread(&metadata_image.width_image, sizeof(metadata_image.width_image), 1, file_bmp); | |
fread(&metadata_image.height_image, sizeof(metadata_image.height_image), 1, file_bmp); | |
printf("width x height: %d x %d\n", metadata_image.width_image, metadata_image.height_image); | |
fread(&metadata_image.n_color_planes, sizeof(metadata_image.n_color_planes), 1, file_bmp); | |
fread(&metadata_image.n_bits_per_pixel, sizeof(metadata_image.n_bits_per_pixel), 1, file_bmp); | |
fread(&metadata_image.compression_method, sizeof(metadata_image.compression_method), 1, file_bmp); | |
fread(&metadata_image.n_bytes_body, sizeof(metadata_image.n_bytes_body), 1, file_bmp); | |
printf("n_color_planes: %d\n", metadata_image.n_color_planes); | |
printf("n_bits_per_pixel in bits: %d\n", metadata_image.n_bits_per_pixel); | |
printf("compression_method: %d\n", metadata_image.compression_method); | |
printf("n_bytes_body: %d\n", metadata_image.n_bytes_body); | |
if (row < 0 || row >= metadata_image.height_image) { | |
printf("Given row beyond image range\n"); | |
fclose(file_bmp); | |
return 1; | |
} | |
if (col < 0 || col >= metadata_image.width_image) { | |
printf("Given column beyond image range\n"); | |
fclose(file_bmp); | |
return 1; | |
} | |
fread(&metadata_image.resolution_h, sizeof(metadata_image.resolution_h), 1, file_bmp); | |
fread(&metadata_image.resolution_v, sizeof(metadata_image.resolution_v), 1, file_bmp); | |
fread(&metadata_image.n_colors, sizeof(metadata_image.n_colors), 1, file_bmp); | |
fread(&metadata_image.n_important_colors, sizeof(metadata_image.n_important_colors), 1, file_bmp); | |
printf("res_h: %d, res_v: %d, n_colors: %d, n_important_colors: %d\n", | |
metadata_image.resolution_h, | |
metadata_image.resolution_v, | |
metadata_image.n_colors, | |
metadata_image.n_important_colors | |
); | |
// calculate padding by row in bytes (n_bytes must be a multiple of four) | |
int n_bytes_pixel = metadata_image.n_bits_per_pixel / 8; | |
int n_bytes_row_no_pad = metadata_image.width_image * n_bytes_pixel; | |
int n_bytes_padding = ceil(n_bytes_row_no_pad / 4.0) * 4 - n_bytes_row_no_pad; | |
printf("n_bytes_padding: %d\n", n_bytes_padding); | |
/* image body */ | |
uint8_t* buffer = malloc(sizeof(uint8_t) * metadata_image.n_bytes_body); | |
size_t n_read = fread(buffer, sizeof(uint8_t), metadata_image.n_bytes_body, file_bmp); | |
printf("n_read: %ld\n", n_read); | |
// origin at lower left corner and last coord at opposite corner in bmp file | |
int x = col; | |
int y = (metadata_image.height_image - 1) - row; | |
// get rgb value (stored in BGR format) | |
int n_bytes_row = n_bytes_row_no_pad + n_bytes_padding; | |
int i_byte = y * n_bytes_row + x * n_bytes_pixel; | |
printf("n_bytes_row: %d\n", n_bytes_row); | |
printf("i_byte: %d\n", i_byte); | |
uint8_t blue = buffer[i_byte]; | |
uint8_t green = buffer[i_byte + 1]; | |
uint8_t red = buffer[i_byte + 2]; | |
printf("rgb at (%ld, %ld): (%u, %u, %u)\n", col, row, red, green, blue); | |
free(buffer); | |
fclose(file_bmp); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment