Publi

Salvando archivos de imagen BMP en C

linux_detergenteHace tiempo hablé de la lectura de archivos BMP en C y puse algún ejemplo. Pero falta lo más importante, poder guardar de nuevo las imágenes, tras aplicar un filtro o generar una imagen desde cero y exportarla. Para ello he creado la siguiente función:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void SaveBMP(char *filename, bmpInfoHeader *info, unsigned char *imgdata)
{
  bmpFileHeader header;
  FILE *f;
  uint16_t type;
 
  f=fopen(filename, "w+");
  header.size=info->imgsize+sizeof(bmpFileHeader)+sizeof(bmpInfoHeader);
  /* header.resv1=0; */
  /* header.resv2=1; */
  /* El offset será el tamaño de las dos cabeceras + 2 (información de fichero)*/
  header.offset=sizeof(bmpFileHeader)+sizeof(bmpInfoHeader)+2;
  /* Escribimos la identificación del archivo */
  type=0x4D42;
  fwrite(&type, sizeof(type),1,f);
  /* Escribimos la cabecera de fichero */
  fwrite(&header, sizeof(bmpFileHeader),1,f);
  /* Escribimos la información básica de la imagen */
  fwrite(info, sizeof(bmpInfoHeader),1,f);
  /* Escribimos la imagen */
  fwrite(imgdata, info->imgsize, 1, f);
  fclose(f);
}

En el siguiente ejemplo, utilizando la carga de un archivo de imagen (por ejemplo la que ilustra el post convertida a BMP), podemos hacer un sencillo algoritmo para pasarla a blanco y negro:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>       /* round() */

typedef struct bmpFileHeader
{
  uint32_t size;
  uint16_t resv1;
  uint16_t resv2;
  uint32_t offset;
} bmpFileHeader;

typedef struct bmpInfoHeader
{
  uint32_t headersize;      /* DIB header size */
  uint32_t width;
  uint32_t height;
  uint16_t planes;         /* color planes */
  uint16_t bpp;            /* bits per pixel */
  uint32_t compress;
  uint32_t imgsize;    
  uint32_t bpmx;        /* X bits per meter */
  uint32_t bpmy;        /* Y bits per meter */
  uint32_t colors;      /* colors used */
  uint32_t imxtcolors;      /* important colors */
} bmpInfoHeader;

void SaveBMP(char *filename, bmpInfoHeader *info, unsigned char *imgdata);
unsigned char calculaColorMedio(unsigned char *pixel);
unsigned char *LoadBMP(char *filename, bmpInfoHeader *bInfoHeader);
bmpInfoHeader *createInfoHeader(unsigned w, unsigned h, unsigned ppp);

int main()
{
  bmpInfoHeader info;  
  unsigned char *img;
  unsigned char color[3];
  unsigned char media;
  int i, j;
  img=LoadBMP("Linux_Detergente.bmp", &info);

  for (i=0; i<info.height; i++)
    {
      for (j=0; j<info.width; j++)
    {
      media=calculaColorMedio(&img[3*(j+i*info.width)]);
      img[3*(j+i*info.width)]=media;
      img[3*(j+i*info.width)+1]=media;
      img[3*(j+i*info.width)+2]=media;

    }
    }
  SaveBMP("res3.bmp", &info, img);

  free(img);
 
}

unsigned char calculaColorMedio(unsigned char *pixel)
{
  unsigned media = (*pixel + *(pixel+1) + *(pixel+2)) / 3;

  return (unsigned char) media;
}

unsigned char *LoadBMP(char *filename, bmpInfoHeader *bInfoHeader)
{

  FILE *f;
  bmpFileHeader header;
  unsigned char *imgdata;
  uint16_t type;
  f=fopen (filename, "r");
  /* handle open error */
  fread(&type, sizeof(uint16_t), 1, f);
  if (type !=0x4D42)
    {
      fclose(f);
      return NULL;
    }
  fread(&header, sizeof(bmpFileHeader), 1, f);

  printf ("size: %u\n", header.size);
  printf ("offs: %u\n", header.offset);
  fread(bInfoHeader, sizeof(bmpInfoHeader), 1, f);
  printf ("header size:      %d\n", bInfoHeader->headersize);
  printf ("image width:      %d\n", bInfoHeader->width);
  printf ("image height:     %d\n", bInfoHeader->height);
  printf ("colour planes:    %d\n", bInfoHeader->planes);
  printf ("bpp:              %d\n", bInfoHeader->bpp);
  printf ("compress:         %d\n", bInfoHeader->compress);
  printf ("imgage size:      %d\n", bInfoHeader->imgsize);
  printf ("bpmx:             %d\n", bInfoHeader->bpmx);
  printf ("bpmy:             %d\n", bInfoHeader->bpmy);
  printf ("colors:           %d\n", bInfoHeader->colors);
  printf ("important colors: %d\n", bInfoHeader->imxtcolors);
  imgdata=(unsigned char*)malloc(bInfoHeader->imgsize);
  fseek(f, header.offset, SEEK_SET);
  printf("leido: %d\n", fread(imgdata, bInfoHeader->imgsize,1, f));
  fclose(f);

  return imgdata;
}

bmpInfoHeader *createInfoHeader(unsigned w, unsigned h, unsigned ppp)
{
  bmpInfoHeader *ih = malloc(sizeof(bmpInfoHeader));

  ih->headersize=sizeof(bmpInfoHeader);
  ih->width=w;
  ih->height=h;
  ih->planes=1;
  ih->bpp=24;
  ih->compress=0;
  ih->imgsize=w*h*3;        /* 3 bytes por pixel w*h pixels */
  ih->bpmx=(unsigned)round((double)ppp*100/2.54);
  ih->bpmy=ih->bpmx;        /* Misma resolución vertical y horiontal */
  ih->colors=0;
  ih->imxtcolors=0;

  return ih;
}

void SaveBMP(char *filename, bmpInfoHeader *info, unsigned char *imgdata)
{
  bmpFileHeader header;
  FILE *f;
  uint16_t type;
 
  f=fopen(filename, "w+");
  header.size=info->imgsize+sizeof(bmpFileHeader)+sizeof(bmpInfoHeader);
  /* header.resv1=0; */
  /* header.resv2=1; */
  /* El offset será el tamaño de las dos cabeceras + 2 (información de fichero)*/
  header.offset=sizeof(bmpFileHeader)+sizeof(bmpInfoHeader)+2;
  /* Escribimos la identificación del archivo */
  type=0x4D42;
  fwrite(&type, sizeof(type),1,f);
  /* Escribimos la cabecera de fichero */
  fwrite(&header, sizeof(bmpFileHeader),1,f);
  /* Escribimos la información básica de la imagen */
  fwrite(info, sizeof(bmpInfoHeader),1,f);
  /* Escribimos la imagen */
  fwrite(imgdata, info->imgsize, 1, f);
  fclose(f);
}

Tendremos que compilarlo con -lm para incluir la biblioteca matemática (usada para hacer round() con los colores), debe generar un fichero res.bmp con la imagen en blanco y negro.

Nota: El algoritmo utilizado para hacer la imagen en blanco y negro es la media de las 3 componentes, hay muchos algoritmos para hacer esto, ya iré mostrando algunos en el futuro.

Nota2: He incluido la etiqueta #tuentiContest aquí aunque no tenga mucho que ver por el reto 14, donde teníamos que leer un archivo BMP.

Foto: Jacob Köhler

También podría interesarte....

There are 15 comments left Ir a comentario

  1. Pingback: Bitacoras.com /

  2. Ezequiel /
    Usando Mozilla Firefox Mozilla Firefox 15.0.1 en Ubuntu Linux Ubuntu Linux

    Hola!
    Gracias por compartir el código, me fue muy útil.
    Pero le encontré un pequeño error…
    Tiene que ver con el valor de imgsize que se está almancenando. Si lees las especificaciones de bmp, las filas tienen que tener una cantidad de bytes divisible por 4, caso contrario se le agrega un padding.
    La forma correcta sería
    ih->imgsize = ((ancho*3-1)/4 +1)*4*alto;

    Dicha formula está obtenida de determinar cuantos bloques de S bytes se requieren para almacenar N bytes, quedando en forma generica
    (N-1)/S + 1
    Luego eso se multiplica por el tamaño del bloque (S=4 en nuestro caso) y se tiene el tamaño de la fila.

    Saludos!

    1. admin / Post Author
      Usando Mozilla Firefox Mozilla Firefox 15.0.1 en Ubuntu Linux Ubuntu Linux

      Gracias Ezequiel,

      Cuando tenga un rato voy a experimentar un poco con lo que me has dicho y cambio un poco el post.

  3. Pingback: Generando imágenes en C, sólo por diversión, empezando desde cero (Parte I) – Poesía Binaria /

  4. john /
    Usando Google Chrome Google Chrome 105.0.0.0 en Windows Windows NT

    Really nice and interesting post. I was looking for this kind of information and enjoyed reading this one.
    Decking Bendigo

  5. gemma /
    Usando Google Chrome Google Chrome 106.0.0.0 en Windows Windows NT

    I just wanted to let you know that I recently discovered your website and that I think it’s extremely useful and instructive. In the future, I’d like to read more of your blogs wordle answer today.

  6. Byrd Franklin /
    Usando Google Chrome Google Chrome 108.0.0.0 en Windows Windows NT

    Fantastically written and informative article. This is exactly the sort of thing I’ve been seeking for, and it was a pleasure to read. word hurdle

  7. poppy miklas /
    Usando Google Chrome Google Chrome 108.0.0.0 en Windows Windows NT

    I admit, I haven’t been on this site for a long time… however, This is an important topic, which has helped to make people more aware of these issues.
    backrooms

  8. Marimar White /
    Usando Google Chrome Google Chrome 109.0.0.0 en Windows Windows NT

    But the most important thing is missing, being able to save the images again, after applying a filter or generating an image from scratch and exporting it. . Anyway, if you are looking for a gold jewelry store near me, just hit us up!

  9. cut mover /
    Usando Google Chrome Google Chrome 110.0.0.0 en Windows Windows NT

    I have read many other blogs, but yours is the only one that has persuaded me, and I hope that you will continue to share more insightful content with readers in the future.

  10. rekenmachine online /
    Usando Google Chrome Google Chrome 112.0.0.0 en Windows Windows NT

    De rekenmachine kan als de beste alle basisberekeningen zoals optellen, aftrekken, vermenigvuldigen, delen, worteltrekken en procentberekeningen uitvoeren.

  11. Purity Test /
    Usando Google Chrome Google Chrome 112.0.0.0 en Windows Windows NT

    Rice Purity Test is a 100-question survey that assesses the participants’ supposed degree of innocence in worldly matters.

  12. Instander download /
    Usando Google Chrome Google Chrome 112.0.0.0 en Windows Windows NT

    Instander APK is a highly sought-after variant of Instagram, but it is not readily available on the Google Play store. To gain access to its additional features, you will need to download this modded version. In this post, I’ll outline the steps to download and install Instander APK so that you can make the most out of your Instagram experience. Fortunately, this exceptional app can be downloaded from this website completely free of charge.

  13. Lottery Sambad Old Result /
    Usando Google Chrome Google Chrome 112.0.0.0 en Windows Windows NT

    Nagaland State Lottery Sambad (লটারি সংবাদ) Result Today of Dear Lottery Morning, Day, and Evening / Night Live Ticket Draw on Lottery Sambad.

  14. own the win /
    Usando Google Chrome Google Chrome 113.0.0.0 en Windows Windows NT

    Ang OkBet ay isa sa mga nangungunang online sportsbook sa Pilipinas na mayroong tayaan para sa mga pinakasikat na mga laro!!! Want to know more about the site? visit here —>> sportsbook

Leave a Reply