Archivo

Entradas Etiquetadas ‘colores’

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;

?>

#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

Jugando con ImageMagick (III): Colecciones, texto, y unión de efectos

Lunes, 6 de Septiembre de 2010 Gaspar Fernández Sin comentarios

El tercer post de la serie, anteriormente hablábamos de:

  1. Dimensiones, Captura, Color y Efectos
  2. Color (continuación) y Rotación

Juntando imágenes en una sola

Ahora, la cosa se anima, veremos cómo recopilar varias imágenes en el mismo archivo de imagen; habréis visto algún ejemplo en la entrega II, en los que muestro varias imágenes juntas:

variasPara ello usamos montage:

$ montage -tile 4×2 -geometry 200×160+1+0 taza1.jpg taza2.jpg taza3.jpg taza4.jpg hamburgueson1.jpg hamburgueson2.jpg hamburgueson3.jpg hamburgueson4.jpg varias.jpg

Donde, con el parámetro tile, especificamos la agrupación de las imágenes ancho x alto (4×2 en este caso) y con geometry, el tamaño de cada imagen 200×160 de máximo, con una separación de 1 pixel a los lados y ninguno verticalmente.

La imagen conserva su aspecto, pero de alto ocupa 160 pixels por lo que existe una pequeña separación.

Añadiendo texto

outSin imagen original, simplemente un texto en un archivo de imagen. Obtenido de la siguiente forma:

$ convert -background black -fill red -gravity center -font Verdana -pointsize 20 -size 200×80 caption:’Rojo sobre negro’ rojosobrenegro.png

Donde:

  • -background especifica el color de fondo
  • -fill especifica el color de primer plano
  • -gravity es la alineación, centrada, tanto horizontal como verticalmente.
  • -font indica el tipo de letra
  • -pointsize indica el tamaño de la letra
  • -size indica el tamaño de la imagen (porque no vamos a utilizar ninguna imagen de origen).
  • caption:’Texto’ con ello indicamos el texto

out1

$ convert -size 300×120 xc:rgb\(50,50,70\) -fill rgb\(90,150,200\) -gravity SouthEast -font helvetica -pointsize 25 -draw ‘text 10 2 “Poesía Binaria”‘ poesia_binaria.jpg

Donde incluimos el texto en una determinada posición: 10×2 desde la esquina inferior derecha (SouthEast), tipo de letra helvetica y tamaño 25; de fondo (50,50,70) y de color (90,150,200).

out2Ahora, aprovecharemos un poco más las capacidades de la orden draw. Para ello, dibujaremos un cuadro en negro dentro de la imagen (antena.jpg de dimensiones 314×300) a modo de marco (verde); luego trazaremos una línea horizontal de color blanco (rojo) en el borde del cuadro que acabamos de dibujar y tras ello escribiremos un texto (azul).

$ convert antena_2.jpg -gravity SouthEast -font helvetica -pointsize 25 -fill black -draw ‘rectangle 0,270 314,300′ -fill white -draw ‘line 0,270 314,270′ -draw ‘text 13 1 “Mi antena Wifi”‘ out.jpg

out3Ahora introduciremos el texto directamente en la imagen, pero para facilitar la lectura incluiremos una sobra en el texto.

$ convert antena_2.jpg -gravity North -font helvetica -pointsize 25 -fill black -draw ‘text 0 0 “Mi antena Wifi”‘ -fill white -draw ‘text 1 1 “Mi antena Wifi”‘ out.jpg

Podemos incluir de forma opcional -blur 1×1 (por ejemplo) para difumirar el texto que hace de sombra y suavizarla.

Juntando los efectos disponibles

Uno de los mejores ejemplos es el siguiente: http://www.imagemagick.org/Usage/thumbnails/ aunque encontramos cientos de webs con efectos preparados para ImageMagick:

Colores y posicionamiento en terminales Linux (como conio.h en DOS)

Viernes, 29 de Mayo de 2009 blakeyed 2 comentarios

A veces, es un poco difícil que alguien que sólo conoce conio.h se pase a Linux, más que nada, porque se puede utilizar ncurses, pero hay que cambiar un poco de mentalidad para poder trabajar con la nueva biblioteca.

Por eso, hace unos meses creé unas cuantas funciones que se podían utilizar para ir reemplazándolas poco a poco. Funcionan de forma muy parecida a conio.h (en los colores). Se basan en códigos ANSI, y las podemos utilizar para cualquier programa rápido en que necesitemos utilizar colores en terminal. ncurses es muy potente y si vamos a complicarnos un poco más la vida, mejor utilizar esta segunda.

Cuando estuve implementando estas funciones, tuve un problema, a la hora de leer información del terminal sin mostrarla por pantalla, me basé en: wsize.c de Stephen J. Friedl. También podéis encontrar información sobre los códigos ANSI aquí.

He incluído la opción subrayado (UNDERLINE) y un par de funciones que nos calculan las dimensiones de la pantalla (ya que los terminales pueden ser redimensionados y ser enormes).

Descargar stermp - Simple Terminal Play (stermp.c, stermp.h)

Os pongo aquí el código del ejemplo (conio.c)

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
#include <stdio.h>
#include "stermp.h"

int main()
{
    int pp, fn;
    clrscr();
    for (pp=BLACK; pp<=WHITE; pp++)
        {
            gotoxy(5, 20+pp);
            for (fn=BLACK; fn<DARKGRAY; fn++)
                {
                    textcolor(pp);
                    textbackground(fn);
                    printf("COLOR\t");
                }
        }
    restore_color();
    textcolor(RED+UNDERLINE);
    gotoxy(51, 5);
    printf("Posición X: %d\n", wherex());
    printf("Posición Y: %d\n", wherey());
    printf("Pantalla %dx%d", screenwidth(), screenheight());

}

Visita otras webs de la red