Um padrão “aberto” e simples de armazenamento de imagens

O que vou descrever aqui é um padrão de armazenamento de imagens conhecido como raw portable pixel map” (PPM file format). Encontrei isso quando fui estudar as bibliotecas libavformat, libavutil e libavcodec, que conpõem o ffmpeg. Eu queria extrair os frames de um vídeo qualquer e usá-los como texturas num pequeno teste feito com SDL e OpenGL (algum dia posto por aqui!). Ao invés de texturas estáticas, eu queria usar os frames, entendeu?

Passei por algumas decepções quando tentei criar um código exemplo: A documentação das libs é bem pobre de detalhes; Os exemplos que achei usam funções que já são obsoletas e, no primeiro teste, os frames eram decodificados de forma errada! Já consegui colocar o treco funcionando corretamente, mas vou me conter em apenas uma função deste código aqui: A geração de imagens a partir dos frames (isto é, de um buffer que já contém os pixels de um frame).

O formato raw PPM é uma mistura de texto e dados binários. Ele é bem simples: consiste de uma linha com a assinatura “P6”, outra com os tamnhos horizontal e vertical da imagem e uma terceira linha com o valor máximo de cada um dos componentes de um pixel. Um pixel, aqui, é definido como 3 componentes: R, G e B, nesta ordem. A próxima linha (a quarta) nada mais é do que um array, binário, com a sequência RGBRGBRGB… Para simplificar, imagine que temos um buffer de bytes (do tipo uint8_t) que contenha o array dos valores dos pixels e queremos gerar o arquivo .PPM. A função fica mais ou menos assim:

void SaveFrame(uint8_t *buffer, int width, int height, int frame)
{
  FILE *fout;
  char fname[48];

  /* NOTA: Claro que o diretório ./images/ tem que existir, né? */
  snprintf(fname, 48, "images/frame%d.ppm", frame); 

  fout = fopen(fname, "wb");
  if (fuot != NULL)
  {
    fprintf(fout, "P6\n%d %d\n255\n", width, height);

    /* Se o tamanho do buffer for MUITO grande ( ≥ 682 Mpixels, em ambientes x86-64),
       você pode subtituir a chamada abaixo para: 

       int y;
       for (y = 0; y < height; y++)
         fwrite(buffer+y*width*3, width*3, 1, fout);
    */
    fwrite(buffer, width*height*3, 1, fout);
    fclose(fout);
  }
}

O motivo de multiplicar width por 3 é que uma linha tem 3 bytes por pixel (RGB, lembra?).

Bem simples, eh?! É mais simples do que gerar JPEG ou PNG! Os arquivos ficam maiores, mas a imagem pode ser vista com qualquer image viewer decente.

Anúncios

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s