Skip to content

Instantly share code, notes, and snippets.

@h4k1m0u
Created July 6, 2024 13:32
Show Gist options
  • Save h4k1m0u/5c404ee6fcfbb93bcbcd823cd55be669 to your computer and use it in GitHub Desktop.
Save h4k1m0u/5c404ee6fcfbb93bcbcd823cd55be669 to your computer and use it in GitHub Desktop.
Parse bitmap image to find RGB values at given row and column
#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