Archivo

Archivo para Junio, 2011

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

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 #20 : The Clumsy Programmer

Tenemos un viejo Debian de 32bits y hemos borrado nuestro home, además hemos perdido las claves para descifrar nuestros datos, ¡tenemos que recuperarla!

Este reto, casi nadie lo completamos, y en principio yo pensaba que era más difícil de lo que era. Romper un RSA de 1024 es algo muy lento, y vi que algunos lo resolvían en menos de 20 minutos. Cuando me enteré me arrepentí de no haber pasado un poco más de tiempo buscando información en lugar de abandonar, la verdad es que llevaba un ritmo de sueño muy raro esa semana, y preferí dormir un poco.

Para resolverlo, teníamos que tener en cuanta la época, no nos decían cuando, pero era un viejo Debian, era de 32bits y estábamos utilizando openssl. Parece ser que allá por 2008 se descubrió un fallo en la versión de OpenSSL que instalaba Debian (y derivados), en la que los números aleatorios necesarios para la clave RSA, en lugar de ser a través de /dev/random eran a través de la PID de OpenSSL, con lo cual tenemos muy pocas posibilidades teniendo en cuenta que muchas veces, las PID no es muy alta (más de 500 y menos de 15000, vamos generalmente), es decir, tenemos un número muy acotado de posibles claves RSA a utilizar, por lo que podemos hacer un script en bash para probarlas todas.

Para más información, podéis visitar:

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:52 : 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 #19

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 #19 : The Caesar Salad

Un mensaje cifrado con 4 métodos distintos, todo acompañado de una adivinanza
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 #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

Esteganografía: Inserción en el LSB en PHP (Solucionando el Challenge 17 en #tuentiContest)

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

La esteganografía es el arte de introducir una información dentro de otra de forma oculta, ya sea texto dentro de imagen, audio dentro de imagen, imagen dentro de audio, imagen dentro de imagen, lo que nos dé la imaginación.

Hace unos meses, en Código para llevar leí un par de posts interesantes sobre este tema, donde se proponía una implementación en Python ( I y II )

En este caso, se trata de descifrar un texto oculto dentro de una imagen PNG. ¿ Por qué PNG ? Sobre todo porque es un estándar bastante extendido de compresión de imágenes sin pérdidas, podría ser también un JPEG LS, aunque se ve menos.
La técnica utilizada para “ocultar” el mensaje se basa en modificar el bit menos significativo de cada componente de cada pixel de la imagen, y es que para el ojo humano, en una gama de 256 niveles de rojo, por ejemplo, le resulta muy pero que muy difícil (por no decir imposible), distinguir entre dos niveles muy próximos, así que nos aprovechamos de eso, utilizaremos el bit menos significativo (LSB) de cada componente para escribir nuestro mensaje, y a efectos prácticos no nos daremos cuenta visualmente de esto. Además, podremos encerrar un carácter ASCII por cada 2.66 pixels (3componentes por pixel, 8 bits por carácter).

En el reto, el mensaje estaba encerrado sólo en la primera línea de la imagen, por lo que sólo leeremos esa línea, es fácil extender el código a varias líneas o columnas, incluso.

Vamos a por un poco de código de cómo descifrar el mensaje:

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
<?php
// Esta función cogerá las componentes RGB del pixel situado en
// $x, $y
function getcolor($img, $x, $y)
{
  $rgb = imagecolorat($img, $x, $y);
  $r = ($rgb >> 16) & 0xFF;
  $g = ($rgb >> 8) & 0xFF;
  $b = $rgb & 0xFF;
 
  return array($r, $g, $b);
}

// Convierte una cadena de unos y ceros a ASCII, cogiendo de 8 en 8
function _bin2asc($str)
{
  $len = strlen($str);
 
  for ($i=0;$i<$len;$i+=8)
  {
    $ch=bindec(substr($str,$i,8));
    $data[]=$ch;
  }
  return $data;
}

// Extrae los datos de los LSB de cada componente
function decod($i)
{
  $tx=imagesx($i);
  $ty=imagesy($i);
  // Eliminar la línea de abajo para extraer información de toda la imagen
  $ty=1;

  $data='';
  for ($y=0; $y<$ty; $y++)
    {
      for ($x=0; $x<$tx; $x++)
    {
      $cdat=getcolor($i, $x, $y);

      $data.=($cdat[0]&1).($cdat[1]&1).($cdat[2]&1);
    }
    }
  return $data;
}

$img=imagecreatefrompng($tnam);

$d=decod ($img);
echo $d;
?>

Es cierto que esto habría que optimizarlo mucho, ya que estamos almacenando valores binarios en una cadena, ocuparía mucho y tardaríamos tiempo en la reconstrucción ya que _bin2asc() trabaja haciendo contantemente substr(). Podríamos por ejemplo reconstruir el mensaje a medida que vamos obteniendo bits, almacenando en un buffer hasta 8 bits. Aunque yo creo que este ejemplo se entiende mejor, y por otra parte, dada la naturaleza del concurso había que investigar muchas posibilidades, por lo que era mejor hacer un código menos óptimo y más rápido de hacer.

Por otra parte, para solucionar el reto del #tuentiContest, era importante colocar los bits en orden, es decir, los bits extraídos no están es este orden, teníamos que investigar y hacer pruebas, en lugar de estar en RGB (como en el ejemplo superior), están en BGR.

A continuación, aunque no era necesario para el reto, incluyo mi solución, un programa que extrae la información de la entrada estándar, la decodifica (base64_decode), la pasa a un archivo PNG y abrimos ese archivo desde PHP que leeremos. A continuación teníamos que devolver un fragmento del código extraído, por lo tanto delimitamos el texto y devolvemos. Aunque sólo bastaba con hacer un programa que escribiera el código.

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
<?php
// Convierte una cadena de unos y ceros a ASCII, cogiendo de 8 en 8
function _bin2asc($str)
{
  $len = strlen($str);
 
  for ($i=0;$i<$len;$i+=8){
    $ch=bindec(substr($str,$i,8));

      $data[]=$ch;

  }
  return $data;
}

function decod($i)
{
  $tx=imagesx($i);
  $ty=imagesy($i);

  $ty=1;

  $data='';
  for ($y=0; $y<$ty; $y++)
    {
      for ($x=0; $x<$tx; $x++)
    {
      $cdat=getcolor($i, $x, $y);

      $data.=($cdat[2]&1).($cdat[1]&1).($cdat[0]&1);
    }
    }
  return $data;
}

// Esta función cogerá las componentes RGB del pixel situado en
// $x, $y
function getcolor($img, $x, $y)
{
  $rgb = imagecolorat($img, $x, $y);
  $r = ($rgb >> 16) & 0xFF;
  $g = ($rgb >> 8) & 0xFF;
  $b = $rgb & 0xFF;
 
  return array($r, $g, $b);
}

$fp=fopen("php://stdin","r");

$line='';
while(!feof($fp))
  {
    $line.=fread($fp,5192);
  }

$tnam=tempnam("/tmp", "tuenticontest_");
file_put_contents($tnam, base64_decode($line));
$img=imagecreatefrompng($tnam);

$d=decod ($img);

$b=_bin2asc($d);

$frase='';
for ($i=0; $i<count($b); $i++)
  $frase.=chr($b[$i]);
//echo $frase;
$pre='following code: ';
$code=substr($frase, strpos($frase,$pre)+strlen($pre));
$clean=substr($code, 0, strpos($code, '.'));
echo $clean;

?>

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 #16

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 #16 : The Bus Stations

Nos dan una serie de rutas de autobús entre varias ciudades, con su capacidad de autobuses por día, también nos dicen una ciudad de origen y destino y tenemos que decir cuántos autobuses como máximo podemos poner en marcha cada día.
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:50 : Añadido mirror a la solución de @Rosapolis
Actualización 2011/07/19 17:55 : Añadida la solución de @lagunex

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

Visita otras webs de la red