Archivo

Archivo para la categoría ‘Imagen Digital’

Generar texto e implantarlo en una imagen desde PHP

Viernes, 10 de Febrero de 2012 Gaspar Fernández 1 comentario

En ocasiones, se puede presentar la necesidad de introducir en nuestros proyectos una imagen cuyo contenido sea un texto, y sobre todo necesitamos integrarlo dentro de nuestro contenido, intentando que no se note que es una imagen, a no se que seleccionen el contenido para copiarlo.

Una de las principales utilidades de esto es evitar el SPAM al escribir nuestra dirección de e-mail en una web. Muchos clientes prefieren enviar un e-mail en lugar de utilizar un formulario de contacto y tenemos que satisfacerles, sin arriesgar la seguridad de nuestro sistema, en el sentido de que hay cientos de motores que se dedican a rastrear webs en busca de direcciones de e-mail (lo cual es muy sencillo de hacer con expresiones regulares, por ejemplo).

Tenemos que estudiar bien cómo hacemos el script, ya que la entrada de datos es esencial. Sería improductivo hacer un script cuya llamada fuera:

1
<img src="scriptGenerador.php?texto=midireccion@miservidor.com" />

Ya que un motor se enteraría de que eso es una dirección de correo igualmente. Por lo que yo sugiero algunas opciones:

  • Para simplificar, podemos codificar el texto, por ejemplo, en base 64, el e-mail anterior quedaría así:bWlkaXJlY2Npb25AbWlzZXJ2aWRvci5jb20 y sería más complicado de detectar, aunque es nuestra responsabilidad hacer la conversión. Además, si en cualquier momento queremos identificar el texto que pusimos, en cierto punto del código debemos hacer la conversión inversa. Por otra parte, aunque es difícil que algún motor se dedique a convertir el texto en base64 a binario de nuevo, es posible.
  • Otra posibilidad es, si sólo vamos a escribir un sólo texto de esta forma, implantarlo dentro del archivo php que genera la imagen, como una variable del propio código, así no debemos escribir nada cuando llamemos al script. Si tenemos que cambiar el texto, basta con editar el script
  • Si, por el contrario, vamos a escribir varios mensajes, podemos asignar una clave a cada uno, por ejemplo, un número o una cadena de texto corta, y en nuestro script hacer corresponder ese número con el texto en cuestión.

Aquí vamos a hacer un ejemplo de la tercera opción. Aunque podemos extender el ejemplo usando bases de datos (si tenemos que introducir muchos mensajes, o muchas direcciones de e-mail diferentes), aquí vamos a crear un array en PHP para hacer corresponder la clave introducida con el valor (el texto que de verdad escribimos).

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
<?php
  // Con esta función calculamos la anchura, altura del texto y dónde está la baseline de nuestro texto
  // con el tipo de letra seleccionado.
function dimensions($ldef, $texto)
{
  // Calcular el ancho
  $box=imagettfbbox ($ldef['size'], 0, $ldef['font'], $texto);
  $width = abs(abs($box[2]) - abs($box[0]));

  // Calcular el alto y la baseline
  $box=imagettfbbox ($ldef['size'], 0, $ldef['font'], 'ILyjgq'); // Tiene caracteres que no terminan en la baseline
  $height = abs($box[7] - $box[1]);
 
  $baseline = abs($box[5]);

  return array($width, $height, $baseline);
}

// Definimos un array con los posibles textos que vamos a presentar
$textArray=array("ejemplo", "de un fragmento de texto", 'gaspy@mi_servidor.com');


// Definimos el tipo de letra y el tamaño
$letra = array ('font'  => './LinLibertine_Bd.ttf',
        'size'  => 12,
        'color' => array(0,0,0),
        );

// Definimos el texto a presentar
$textID  = (isset($_GET['texto']))?$_GET['texto']:0;
$text = $textArray[$textID];

header('Content-Type: image/png');

// Creamos la imagen, calculando primero sus dimensiones dependiendo del tamaño del texto
$box_size=dimensions($letra, $text);
$im = imagecreatetruecolor($box_size[0], $box_size[1]);

// Creamos dos colores, y definimos el color que actuará como transparente
$background = imagecolorallocate($im, 255, 255, 255);
$fontColor  = imagecolorallocate($im, $letra['color'][0], $letra['color'][1], $letra['color'][2]);
imagecolortransparent($im, $background);

// Ponemos el fondo de la imagen transparente
imagefilledrectangle($im, 0, 0, $box_size[0], $box_size[1], $background);


// Creamos el texto
imagettftext($im, $letra['size'], 0, 0, $box_size[2], $fontColor, $letra['font'], $text);

// Generamos la salida
imagepng($im);
imagedestroy($im);
?>

Para el ejemplo he utilizado la fuente Linux Libertine Bold cuyo fichero ttf es LinLibertine_Bd.ttf, en realidad podemos utilizar cualquier fuente de la que dispongamos el fichero TTF.
El script que vemos, si lo llamamos desde el navegador con el parámetro textID=1 mostrará una imagen con el texto “de un fragmento de texto” dibujado dentro con el tipo de letra especificado anteriormente.

Ahora hacemos el siguiente archivo html:

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
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Ejemplo de texto</title>
    <style text="text/css">
      <!--
     @font-face {
     font-family: "Linux Libertine";
     src: url(LinLibertine_Bd.ttf);
     }
     body{
     background: #eee;
     font: 12pt Linux Libertine,sans;
     }

     .texto{
     vertical-align:bottom;
     margin: 0;
     padding: 0;
     }
    -->
    </style>
    <meta name="author" content="Gaspar Fernández">
  </head>

  <body>
    <h1>Ejemplo de texto</h1>

    Esto es un ejemplo de inserción <img src="texto.php?textID=1" class="texto"/> incluyendo una imagen en PHP
   
    <hr>
    <address>
      <a href="http://totaki.com/poesiabinaria">Gaspar Fernández</a>,
    </address>
  </body>
</html>

Con la parte CSS:

1
2
3
4
@font-face {
     font-family: "Linux Libertine";
     src: url(LinLibertine_Bd.ttf);
     }

Estamos definiendo el fichero que se descargará para visualizar el tipo de letra, así nos aseguramos de que sea el mismo tipo de letra (el de la imagen y el de nuestro texto). El resultado debería ser algo parecido a esto:
textexample

Salvando archivos de imagen BMP en C

Jueves, 21 de Julio de 2011 Gaspar Fernández Sin comentarios

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

C.I. X: Creando un Framework PHP, Microsoft y Nokia a lo suyo, mejores momentos para publicar, clones libres de juegos

Domingo, 3 de Julio de 2011 Gaspar Fernández Sin comentarios

Una pequeña recopilación de enlaces de estos días:

Recopilación de soluciones para los retos de #tuentiContest . Challenge #18

Jueves, 30 de Junio de 2011 Gaspar Fernández Sin comentarios

Últimamente he hablado acerca del I concurso de programación de Tuenti. Un concurso de programación Online que se llevó acabo durante la semana pasada (del 13 al 20 de Junio, muy mala fecha).

Podéis ver los enunciados de todos los problemas, con ejemplos sobre la entrada y salida (aunque a veces no hay que hacerles mucho caso) en la web oficial del concurso, pero en Vidas Concurrentes lo encontramos todo en español.

Challenge #18 : The Riddle

Nos mandan una imagen en base64 por la stdin (ya lo toman como costumbre) y nos dan además, una adivinanza. Se trataba de extraer texto de la imagen, pero sólo de ciertas partes, es decir, teníamos que extraer la información por columnas, siempre que éstas no fueran verdes.
Soluciones:

Si no estás en la lista y quieres plantear tu solución, deja un comentario con tu link !

Actualización 2011/07/03 01:49 : Añadida solución de @frisco82
Actualización 2011/07/03 13:52 : Añadida solución de @Rosapolis

Recopilación de soluciones para los retos de #tuentiContest . Challenge #17

Miércoles, 29 de Junio de 2011 Gaspar Fernández Sin comentarios

Últimamente he hablado acerca del I concurso de programación de Tuenti. Un concurso de programación Online que se llevó acabo durante la semana pasada (del 13 al 20 de Junio, muy mala fecha).

Podéis ver los enunciados de todos los problemas, con ejemplos sobre la entrada y salida (aunque a veces no hay que hacerles mucho caso) en la web oficial del concurso, pero en Vidas Concurrentes lo encontramos todo en español.

Challenge #17 : The ¿? Porblem

También conocido como “Just do it.” por las palabras de su enunciado. Lo que no nos decían es que nos mandaban una imagen PNG en base64 por la entrada estándar. Si la abríamos como texto, después de muchos datos binarios podíamos leer un texto: “There is more data in this image than meets the eye“, pensemos ahora en esteganografía, por ahí iban los tiros.
Soluciones:

Si no estás en la lista y quieres plantear tu solución, deja un comentario con tu link !

Actualización 2011/07/03 01:49 : Añadida solución de @frisco82
Actualización 2011/07/03 01:59 : Añadida mi solución (@blakeyed)
Actualización 2011/07/03 13:52 : Añadida solución de @Rosapolis

#tuentiContest Solución al Challenge 15 . Reto del artista en C

Martes, 28 de Junio de 2011 Gaspar Fernández 8 comentarios

Planteo una solución en C para este reto, en el que nos dan un tamaño de lienzo (ancho, alto) y número de rectángulos de colores que dibujaremos, a continuación nos pasarán por cada color, el número del color y las coordenadas del rectángulo a dibujar. Teniendo en cuenta que el origen está en (0,0), debemos devolver la superficie de cada color, ordenada por color. Eso sí, si hay algún color que no sea visible, no debemos mostrarlo, ese es uno de los detalles de los que no informaban en el reto.

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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/**
*************************************************************
* @file artist.c
* @brief Challenge 15
*
* @author Gaspar Fernández <blakeyed@totaki.com>
* @version
* @date 17 jun 2011
* Historial de cambios:
*
*************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* Huge string */
#define STRSIZE 1000
/* #define debug */

typedef struct TRect
{
  int lx, ly, ux, uy, color;
} TRect;

int extrae_argumentos_dd(char *orig, char *delim, char ***args);
void fill_image(int *img, int width, int color, int x1, int y1, int x2, int y2);
void new_rect(int *img, int width, int height, int color, int lx, int ly, int ux, int uy);
void see_image(int *img, int width, int height);
char *trim2(char *s, const char *trimChars);
void define_rect(TRect *r, int lx, int ly, int ux, int uy, int color);
void draw_rect(int *img, int width, int height, const TRect *r);
void swap(TRect* a, TRect* b);
void quicksort(TRect* izq, TRect* der);
int count_color(int *img, int width, int height, int color);

int main(int argc, char *argv[])
{
  char **args;
  int control;
  char bigstr[STRSIZE];
  int width;
  int height;
  int colors;
  int c;
  unsigned *img;        /* Lots of colors available */
  TRect *rectangles;
 
  int i;
  /* Read until EOF in stdin */
  while (fgets(bigstr, STRSIZE, stdin)!=NULL)
    {
      control=extrae_argumentos_dd(trim2(bigstr,"\n\r "), " ", &args);
      if (control<=3)
    continue;

      width=atoi(args[0]);
      height=atoi(args[1]);
      colors=atoi(args[2]);
     
      if (control!=colors*5+3)
    {
      printf("Wrong number of parameters: %d; colors=%d\n", control, colors);
      /* for (c=0; c<control; c++) */
      /*   printf("P%d=%s\n", c, args[c]); */
      continue;
    }

      /* Everything's ok */

      img=malloc(sizeof(int)*height*width);
      fill_image(img, width, 1, 0,0, width-1, height-1);
      rectangles=malloc(sizeof(TRect)*colors);
      /* control=(control-3)/5; */
      for (c=3, i=0; c<control; c+=5, i++)
          {
      define_rect(&rectangles[i], atoi(args[c+0]),atoi(args[c+1]),atoi(args[c+2]),atoi(args[c+3]),atoi(args[c+4]));
          }
      quicksort(&rectangles[0], &rectangles[colors-1]);
      for (i=0; i<colors; i++)
    {
      draw_rect(img, width, height, &rectangles[i]);
    }

#ifdef debug
      see_image(img, width, height);
#endif
      printf ("%d %d", 1, count_color(img, width, height, 1));

      for (i=0; i<colors; i++)
    {
      control=count_color(img, width, height, rectangles[i].color);
      if (control>0)
        printf (" %d %d", rectangles[i].color, control);
    }
      printf("\n");

      free(rectangles);
      free(img);
    }

 
  return EXIT_SUCCESS;
}


int count_color(int *img, int width, int height, int color)
{
  int i, j;
  int s=0;
  for (i=0; i<height; i++)
    {
      for (j=0; j<width; j++)
    {
      if (img[i*width+j]==color)
        s++;
    }
    }
  return s;
}


void define_rect(TRect *r, int lx, int ly, int ux, int uy, int color)
{
  r->lx=lx;
  r->ly=ly;
  r->ux=ux;
  r->uy=uy;
  r->color=color;
}

void draw_rect(int *img, int width, int height, const TRect *r)
{
  new_rect(img, width, height, r->color, r->lx, r->ly, r->ux, r->uy);
}

void new_rect(int *img, int width, int height, int color, int lx, int ly, int ux, int uy)
{

#ifdef debug
  printf("LI: %d,%d | UR: %d, %d\n", lx, ly, ux, uy);
#endif

  fill_image(img, width, color, lx, height-uy, ux-1, height-ly-1);
 
}

void fill_image(int *img, int width, int color, int x1, int y1, int x2, int y2)
{
  int i, j;
#ifdef debug
   printf("%d,%d -> %d,%d\n", x1,y1, x2,y2);
#endif
  for (i=y1; i<=y2; i++)
    {
      for (j=x1; j<=x2; j++)
    {
      img[i*width+j]=color;
    }
    }
 
}

void see_image(int *img, int width, int height)
{
  int i, j;

  for (i=0; i<height; i++)
    {
      for (j=0; j<width; j++)
    {
      printf("%2d ", img[i*width+j]);
    }
      printf("\n");
    }
}

/* An old function I made */
int extrae_argumentos_dd(char *orig, char *delim, char ***args)
{
  char *tmp;
  int num=0;
  /* Reservamos memoria para copiar la candena ... pero la memoria justa */
  char *str= malloc(strlen(orig)+1);
  char **aargs;

  strcpy(str, orig);

  aargs=malloc(sizeof(char**));

  tmp=strtok(str, delim);
  do
    {
      aargs[num]=malloc(sizeof(char*));

      /*       strcpy(aargs[num], tmp); */
      aargs[num]=tmp;
      num++;

      /* Reservamos memoria para una palabra más */
      aargs=realloc(aargs, sizeof(char**)*(num+1));

      /* Extraemos la siguiente palabra */
      tmp=strtok(NULL, delim);
    } while (tmp!=NULL);

  *args=aargs;
  return num;
}

char *trim2(char *s, const char *trimChars)
{
  char *start = s;

  /* Nos comemos los caracteres al principio */
  while(*start && strpbrk((const char*)start, trimChars)==start)
    ++start;

  char *i = start;
  char *end = start;

  /* Nos comemos los caracteres al final */
  while(*i)
  {
    if( strpbrk(i++, trimChars)!=i-1 )
      end = i;
  }

  /* Coloramos el perminador */
  *end = 0;

  return start;
}

/* to order colors */
void quicksort(TRect* izq, TRect* der)
{
        if(der<izq) return;
        TRect pivot=*izq;
        TRect* ult=der;
        TRect* pri=izq;
 
        while(izq<der)
        {
                while(izq->color<=pivot.color && izq<der+1) izq++;
                while(der->color>pivot.color && der>izq-1) der--;
                if(izq<der) swap(izq,der);
        }
        swap(pri,der);
        quicksort(pri,der-1);
        quicksort(der+1,ult);
}
 
void swap(TRect* a, TRect* b)
{
        TRect temp=*a;
        *a=*b;
        *b=temp;
}

Para el parseo de la cadena, he utilizado las funciones trim2() y extrae_argumentos_dd(), con lo que podremos extraer sin problemas cada uno de los miembros de la cadena inicial.

Para ahorrar parámetros a la hora de pasarlos de un lado a otro (y me refiero a las coordenadas), he hecho el struct TRect, con el que trabajaremos más cómodamente, sólo hay que llamar a define_rect() para insertar en el registro la información. Además de la función draw_rect() que cambiará el origen de coordenadas a la esquina superior izquierda, que es con la que normalmente trabajamos.

También hay una función de depuración see_image(), a la que llamaba en mis pruebas para ver cómo quedaría el lienzo, la verdad es que era de mucha ayuda, ya que visualizando los datos todo es mucho más fácil.

Por último, como la gente de Tuenti nos tenía acostumbrados a datos de entrada totalmente diferentes en las pruebas de los algoritmos, incluí un quicksort de todos los colores, por si nos los daban desordenados, no fue el caso, pero ahí queda.

Recopilación de soluciones para los retos de #tuentiContest . Challenge #15

Martes, 28 de Junio de 2011 Gaspar Fernández 2 comentarios

Últimamente he hablado acerca del I concurso de programación de Tuenti. Un concurso de programación Online que se llevó acabo durante la semana pasada (del 13 al 20 de Junio, muy mala fecha).

Podéis ver los enunciados de todos los problemas, con ejemplos sobre la entrada y salida (aunque a veces no hay que haerles mucho caso) en la web oficial del concurso, pero en Vidas Concurrentes lo encontramos todo en español.

Challenge #15 : The Robot

Construimos el software de un robot artista, éste dibujará cuadrados sobre un lienzo, y los colores irán numerados desde el 1. Como entrada nos darán el tamaño del lienzo, el número de cuadrados a dibujar y las coordenadas de cada cuadrado (sabiendo que el 0,0 está en la esquina inferior izquierda), la salida debe ser la superficie ocupada por cada color, ordenada por el número de color.
Soluciones:

Si no estás en la lista y quieres plantear tu solución, deja un comentario con tu link !

Actualización 2011/06/29 09:35 : Añadida mi solución, @blakeyed
Actualización 2011/07/03 01:49 : Añadida solución de @frisco82
Actualización 2011/07/03 13:45 : Añadida solución de @Rosapolis
Actualización 2011/07/19 17:50 : Añadida solución de @lagunex

Recopilación de soluciones para los retos de #tuentiContest . Challenge #14

Lunes, 27 de Junio de 2011 Gaspar Fernández 2 comentarios

Últimamente he hablado acerca del I concurso de programación de Tuenti. Un concurso de programación Online que se llevó acabo durante la semana pasada (del 13 al 20 de Junio, muy mala fecha).

Podéis ver los enunciados de todos los problemas, con ejemplos sobre la entrada y salida (aunque a veces no hay que haerles mucho caso) en la web oficial del concurso, pero en Vidas Concurrentes lo encontramos todo en español.

Challenge #14 : Colors are beautiful

Nos dan una imagen para que la carguemos, y luego nos dirán una componente y un número de línea, para que hagamos la suma de esa componente a lo largo de toda la línea
Soluciones:

Si no estás en la lista y quieres plantear tu solución, deja un comentario con tu link !

Actualización 2011/06/28 08:30 : Añadida solución de @theom3ga
Actualización 2011/06/28 08:35 : Añadida solución de @ricardclau
Actualización 2011/07/03 01:49 : Añadida solución de @frisco82
Actualización 2011/07/03 13:45 : Añadida solución de @Rosapolis
Actualización 2011/07/14 14:55 : Añadida solución de @lagunex

#tuentiContest solución del Challenge 14 en C. Colours are beautiful

Lunes, 27 de Junio de 2011 Gaspar Fernández Sin comentarios

Presento aquí mi solución al reto 14, en el que nos pasan una imagen en BMP, y por la stdin nos dan una componente (R, G, B) y un número de línea. Tenemos que hacer la suma de los valores de esa componente a lo largo de la línea especificada, y luego sumar 1.

Para ello, vamos a valernos del post dedicado a la lectura de archivos BMP en C, esta será una de las aplicaciones que vamos a hacer.

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
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#define STRSIZE 1000

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;

/* old function I made */
unsigned char *LoadBMP(char *filename, bmpInfoHeader *bInfoHeader);
int calculate_sum(bmpInfoHeader *info, unsigned char *img, char rgb, int line);

int main()
{
  bmpInfoHeader info;
  unsigned char *img;
  char bigstr[STRSIZE];
  int line_number;

  img=LoadBMP("trabaja.bmp", &info);

  while (fgets(bigstr, STRSIZE, stdin)!=NULL)
    {
      line_number=info.height-atoi(bigstr+1)-1; /* bmps are inverted. Line 0 is count*/
      printf("%d\n", calculate_sum(&info, img, bigstr[0],line_number));
    }

  return 0;
}

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);

  fread(bInfoHeader, sizeof(bmpInfoHeader), 1, f);
  imgdata=(unsigned char*)malloc(bInfoHeader->imgsize);
  fseek(f, header.offset, SEEK_SET);
  fread(imgdata, bInfoHeader->imgsize,1, f);
  fclose(f);

  return imgdata;
}

int calculate_sum(bmpInfoHeader *info, unsigned char *img, char rgb, int line)
{
  int i;
  int sum=0;
  int comp;
  char *bgr="BGR";
  char *tcomp=strchr(bgr, rgb);
  if (tcomp==NULL)
    return 0;

  comp=tcomp-bgr;

  for (i=0; i<info->width; i++)
    {
      sum+=img[3*(i+line*info->width)+comp];
    }
 
  return sum+1;
}

Ya que las cabeceras y la función LoadBMP() ya están analizadas en el post anterior. Analizaremos las funciones calculate_sum() y la principal.

En calculate_sum(), lo que hacemos es primero, obtener el número de la componente a extraer, ya que B=0, G=1, R=2, hallaremos la posición del carácter en la cadena, y si éste existe (tcomp!=NULL), en la variable comp almacenaremos dicha posición (como strchr devuelve un puntero a la parte de la cadena correspondiente, sólo tenemos que restar con la cadena con la que comparamos.
Luego, hacemos un for para recorrer todos los puntos de la línea, pero sólo leyendo los datos de la componente seleccionada. En cada iteración incrementaremos la variable sum para que nos devuelva la suma total de todos los pixels.

En la función principal, lo único que hacemos es cargar en memoria el BMP, y leer por la stdin la entrada propuesta Wxxx, donde W es la componente a leer (R,G,B) y xxx es el número de línea a leer. Desde main() invertimos la línea (como ya dijimos en los BMP, la imagen está boca abajo), por lo que en lugar de leer la línea Y, debemos leer la línea [ ALTO - Y - 1 ] (restamos 1 porque la primera línea es la 0).

Tal vez para hacer la función calculate_sum() más general, debería haber hecho la conversión de línea dentro de ésta (la conversión es algo interno), y la suma de 1 (es algo que nos pide el problema).

Leyendo archivos de imagen en formato BMP en C

Domingo, 26 de Junio de 2011 Gaspar Fernández Sin comentarios

n1214332197_30461081_3311160

Hoy vamos a practicar a leer una imagen desde un archivo BMP desde C. Aunque existen muchas APIs disponibles que son capaces de hacerlo, y mucho mejor que lo que voy a plantear (puesto que nos limitaremos a BMPs sin compresión y a 24bits por pixel), es un buen ejercicio para leer archivos con un formato especificado y documentado.

Para este tipo de archivos, tendremos dos cabeceras disponibles, la primera será la cabecera de fichero, y la segunda, la cabecera de información de imagen, que las definimos aquí:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
typedef struct bmpFileHeader
{
  /* 2 bytes de identificación */
  uint32_t size;        /* Tamaño del archivo */
  uint16_t resv1;       /* Reservado */
  uint16_t resv2;       /* Reservado */
  uint32_t offset;      /* Offset hasta hasta los datos de imagen */
} bmpFileHeader;

typedef struct bmpInfoHeader
{
  uint32_t headersize;      /* Tamaño de la cabecera */
  uint32_t width;               /* Ancho */
  uint32_t height;          /* Alto */
  uint16_t planes;                  /* Planos de color (Siempre 1) */
  uint16_t bpp;             /* bits por pixel */
  uint32_t compress;        /* compresión */
  uint32_t imgsize;     /* tamaño de los datos de imagen */
  uint32_t bpmx;                /* Resolución X en bits por metro */
  uint32_t bpmy;                /* Resolución Y en bits por metro */
  uint32_t colors;              /* colors used en la paleta */
  uint32_t imxtcolors;      /* Colores importantes. 0 si son todos */
} bmpInfoHeader;

Todos los campos tienen un comentario que los explica, pero los detallaré algo más:

  • De primeras nos encontramos dos bytes de identificación que no he introducido en el registro, y es así por la alineación de variables. Es mejor leerlos a parte y luego leer el struct que os presento.
  • size (en FileHeader) es el tamaño del archivo completo, lo podremos usar para comprobar si hay más datos al final del archivo, o para ver si el archivo está cortado, es decir, como comprobación de errores
  • offset (en FileHeader), es la distancia en bytes desde que termina la cabecera de información hasta que empiezan los datos. No le vamos a hacer mucho caso a esto, sólo que puede ser que el archivo tenga más información entre la cabecera y los datos.
  • headersize (en InfoHeader), será el tamaño de la cabecera, dado que hay varias versiones del formato, ésta puede ser mucho más grande, aunque nosotros sólo leeremos siempre 40bytes.
  • planes (en InfoHeader), son los planos de color. Sólo está documentado el valor 1 de este campo, por tanto será el único que tratemos.
  • bpp (en InfoHeader), son los bits por pixel de la imagen, para el ejemplo, siempre serán 24, ya que no soportamos imágenes con otra profundidad de color.
  • compress (en InfoHeader), para este ejemplo será siempre 0, ya que no trataremos compresión.
  • imgsize (en InfoHeader), será el tamaño de los datos de imagen, al igual que size nos puede servir para control de errores, y para ver cuánta memoria vamos a reservar, aunque este tamaño, para el ejemplo será ancho*alto*3 (24bits por pixel)
  • bpmx, bpmy en (InfoHeader), será la resolución de la imagen en pixels por metro.
  • colors, imxtcolors en (InfoHeader), valdrán 0 en este ejemplo, ya que no usaremos imágenes con paleta de colores.

Ya estamos preparados para leer el archivo de imagen con 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
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
unsigned char *LoadBMP(char *filename, bmpInfoHeader *bInfoHeader)
{

  FILE *f;
  bmpFileHeader header;     /* cabecera */
  unsigned char *imgdata;   /* datos de imagen */
  uint16_t type;        /* 2 bytes identificativos */

  f=fopen (filename, "r");
  if (!f)
    return NULL;        /* Si no podemos leer, no hay imagen*/

  /* Leemos los dos primeros bytes */
  fread(&type, sizeof(uint16_t), 1, f);
  if (type !=0x4D42)        /* Comprobamos el formato */
    {
      fclose(f);
      return NULL;
    }

  /* Leemos la cabecera de fichero completa */
  fread(&header, sizeof(bmpFileHeader), 1, f);

  /* Leemos la cabecera de información completa */
  fread(bInfoHeader, sizeof(bmpInfoHeader), 1, f);

  /* Reservamos memoria para la imagen, ¿cuánta?
     Tanto como indique imgsize */

  imgdata=(unsigned char*)malloc(bInfoHeader->imgsize);

  /* Nos situamos en el sitio donde empiezan los datos de imagen,
   nos lo indica el offset de la cabecera de fichero*/

  fseek(f, header.offset, SEEK_SET);

  /* Leemos los datos de imagen, tantos bytes como imgsize */
  fread(imgdata, bInfoHeader->imgsize,1, f);

  /* Cerramos */
  fclose(f);

  /* Devolvemos la imagen */
  return imgdata;
}

Esta función devolverá los datos de la imagen como resultado de la función, y a través de un parámetro de entrada/salida, devolveremos también el infoHeader.
Lo que hacemos es simplemente leer las cabeceras, aunque para la primera cabecera, como dijimos antes, leemos primero los 2 bytes identificativos del formato de archivo, éstos tienen que valer 0×4D42 (MB), aunque como los datos están en little-endian (el byte menos significativo primero), lo leeremos al revés (BM), que corresponde a uno de los formatos de imagen que podemos alojar dentro de un BMP.

Por último, vamos a probar todo esto con un ejemplo, para no complicar las cosas con APIs gráficas, o cálculos de histogramas, se me ocurrió hacer una pequeña representación de la imagen en la consola. Para este código, creé una imagen con ImageMagick de la siguiente manera:

$ convert -background red -fill blue -font /usr/share/fonts/truetype/ttf-liberation/LiberationMono-Bold.ttf -pointsize 72 “label:Poesía Binaria” +matte poesia.bmp

+matte nos servirá para eliminar el canal alfa por defecto de la imagen.

Ahora, en nuestro código incluimos los struct y la función anteriores con un par de cosas más:

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
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

typedef struct bmpFileHeader
{
  /* 2 bytes de identificación */
  uint32_t size;        /* Tamaño del archivo */
  uint16_t resv1;       /* Reservado */
  uint16_t resv2;       /* Reservado */
  uint32_t offset;      /* Offset hasta hasta los datos de imagen */
} bmpFileHeader;

typedef struct bmpInfoHeader
{
  uint32_t headersize;      /* Tamaño de la cabecera */
  uint32_t width;       /* Ancho */
  uint32_t height;      /* Alto */
  uint16_t planes;          /* Planos de color (Siempre 1) */
  uint16_t bpp;             /* bits por pixel */
  uint32_t compress;        /* compresión */
  uint32_t imgsize;     /* tamaño de los datos de imagen */
  uint32_t bpmx;        /* Resolución X en bits por metro */
  uint32_t bpmy;        /* Resolución Y en bits por metro */
  uint32_t colors;      /* colors used en la paleta */
  uint32_t imxtcolors;      /* Colores importantes. 0 si son todos */
} bmpInfoHeader;

unsigned char *LoadBMP(char *filename, bmpInfoHeader *bInfoHeader);
void DisplayInfo(bmpInfoHeader *info);
void TextDisplay(bmpInfoHeader *info, unsigned char *img);

int main()
{
  bmpInfoHeader info;
  unsigned char *img;

  img=LoadBMP("poesia.bmp", &info);
  DisplayInfo(&info);
  TextDisplay(&info, img);

  return 0;
}

void TextDisplay(bmpInfoHeader *info, unsigned char *img)
{
  int x, y;
  /* Reducimos la resolución vertical y horizontal para que la imagen entre en pantalla */
  static const int reduccionX=6, reduccionY=4;
  /* Si la componente supera el umbral, el color se marcará como 1. */
  static const int umbral=90;
  /* Asignamos caracteres a los colores en pantalla */
  static unsigned char colores[9]=" bgfrRGB";
  int r,g,b;

  /* Dibujamos la imagen */
  for (y=info->height; y>0; y-=reduccionY)
    {
      for (x=0; x<info->width; x+=reduccionX)
    {
      b=(img[3*(x+y*info->width)]>umbral);
      g=(img[3*(x+y*info->width)+1]>umbral);
      r=(img[3*(x+y*info->width)+2]>umbral);

      printf("%c", colores[b+g*2+r*4]);
    }
      printf("\n");
    }
}

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

  FILE *f;
  bmpFileHeader header;     /* cabecera */
  unsigned char *imgdata;   /* datos de imagen */
  uint16_t type;        /* 2 bytes identificativos */

  f=fopen (filename, "r");
  if (!f)
    return NULL;        /* Si no podemos leer, no hay imagen*/

  /* Leemos los dos primeros bytes */
  fread(&type, sizeof(uint16_t), 1, f);
  if (type !=0x4D42)        /* Comprobamos el formato */
    {
      fclose(f);
      return NULL;
    }

  /* Leemos la cabecera de fichero completa */
  fread(&header, sizeof(bmpFileHeader), 1, f);

  /* Leemos la cabecera de información completa */
  fread(bInfoHeader, sizeof(bmpInfoHeader), 1, f);

  /* Reservamos memoria para la imagen, ¿cuánta?
     Tanto como indique imgsize */

  imgdata=(unsigned char*)malloc(bInfoHeader->imgsize);

  /* Nos situamos en el sitio donde empiezan los datos de imagen,
   nos lo indica el offset de la cabecera de fichero*/

  fseek(f, header.offset, SEEK_SET);

  /* Leemos los datos de imagen, tantos bytes como imgsize */
  fread(imgdata, bInfoHeader->imgsize,1, f);

  /* Cerramos */
  fclose(f);

  /* Devolvemos la imagen */
  return imgdata;
}

void DisplayInfo(bmpInfoHeader *info)
{
  printf("Tamaño de la cabecera: %u\n", info->headersize);
  printf("Anchura: %d\n", info->width);
  printf("Altura: %d\n", info->height);
  printf("Planos (1): %d\n", info->planes);
  printf("Bits por pixel: %d\n", info->bpp);
  printf("Compresión: %d\n", info->compress);
  printf("Tamaño de datos de imagen: %u\n", info->imgsize);
  printf("Resolucón horizontal: %u\n", info->bpmx);
  printf("Resolucón vertical: %u\n", info->bpmy);
  printf("Colores en paleta: %d\n", info->colors);
  printf("Colores importantes: %d\n", info->imxtcolors);
}

Compilamos el código anterior y nos queda algo como esto:
pbinaria

Lo más raro es la forma de acceder a cada pixel (podremos simplificar más adelante), y es que la imagen está almacenada boca abajo, por lo que la primera línea que leamos será la última que se debe mostrar, por otra parte, los colores están en formato BGR, y, generalmente para acceder a cada pixel lo podremos hacer con:

img[3*(x+y*info->width)+comp]

Teniendo en cuenta, como ya dijimos que la coordenada y va desde la última línea a la primera, y que:

  • Si queremos leer la componente azul, comp=0
  • Si queremos leer la componente verde, comp=1
  • Si queremos leer la componente roja, comp=2

Más adelante, iré publicando algunas aplicaciones prácticas de todo esto, y pronto, una función parecida a LoadBMP que se encargue de salvar los archivos y así empezar a probar filtros gráficos.

Actualizado: 2011/06/26 13:00 : Añadida la información de las componentes de color al final

Visita otras webs de la red