Este artigo abre o blog Processamento Digital de Imagens em C com o objetivo de oferecer ferramentas simples para manipulação de imagens.
Para os próximos artigos, pretendo abordar tópicos variados sobre processamento de imagens. Em cada trabalho, vou utilizar algoritmos e exemplos de implementações em C visando documentar as minhas experiências e também atender as necessidades reportadas pelos leitores.
O objetivo desse artigo é apresentar duas ferramentas que irei utilizar futuramente: a primeira é um conversor de imagem JPEG para tons de cinza. A segunda ferramenta faz o contrário: converte uma imagem crua (raw em tons de cinza) para JPEG.Assim, poderemos pegar um JPEG, gerar um arquivo manipulável dessa imagem, fazer alterações e gerar uma nova imagem em JPEG.Para exemplificar o uso destas ferramentas, fiz também um programa que abre uma imagem em raw a aplica um filtro de média.
O código foi testado no Ubuntu 12.04, mas acredito que não será muito difícil recompilar tudo em uma distribuição linux diferente.
Conversão de JPEG para tons de cinza
Para facilitar o trabalho, utilizei a biblioteca libjpeg. Disponibilizei aqui um pacote com os fontes do meu programa.
A biblioteca libjpeg poderá ser instalada no Ubuntu com o seguinte comando:
A biblioteca libjpeg poderá ser instalada no Ubuntu com o seguinte comando:
sudo apt-get install libjpeg-dev
Ao descompactar o arquivo jpeg_tools.tar.bz2, você terá acesso a quatro diretórios: build guardará os arquivos compilados, em src está o código do programa que aplica um filtro de média na imagem em raw, em utils estão os códigos dos conversores para JPEG e em images há uma imagem de exemplo. Para compilar, basta digitar "make".
O arquivo "utils/jpeg2raw.c" é quem faz toda a mágica. A função jpeg_decompress abre um arquivo JPEG no espaço de cores RGB e extrai a luminância (imagem em tons de cinza), que volta para a função main através da struct raw_img. Por fim, a luminância é gravada em um arquivo especificado pelo usuário.
O trexo de código abaixo mostra a conversão de RGB para tons de cinza.
#include <jpeglib.h>
/* ... */
static int jpeg_decompress(const char *file, struct raw_img *raw)
{
/* ... */
while (jdec.output_scanline < jdec.image_height) {
jpeg_read_scanlines(&jdec, row_pointer, 1);
for (i = 0; i < raw->width * raw->num_comp; i += 3) {
r = (float) row_pointer[0][i];
g = (float) row_pointer[0][i + 1];
b = (float) row_pointer[0][i + 2];
/* here convert to grayscale */
raw->img[j] = (uint8_t)
(0.2126 * r + 0.7152 * g + 0.0722 * b);
j++;
}
}
/* ... */
}
O restante do código é basicamente a configuração do libjpeg para descompactar a imagem em JPEG e a gravação do arquivo em raw.
Conversão de tons de cinza para JPEG
O processo inverso é semelhante e foi implementado no arquivo utils/raw2jpeg.c. A função jpeg_compress recebe uma imagem raw já aberta, faz a compressão para JPEG e salva no arquivo que foi especificado no parâmetro file.
static int jpeg_compress(const char *file, struct raw_img *raw)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPROW row_ptr[1];
FILE* out_jpeg;
uint8_t *image;
int row_stride;
int err;
err = 0;
image = raw->img;
out_jpeg = fopen(file, "w+");
if (out_jpeg == NULL) {
printf("Could no open '%s' filen", file);
err = -1;
goto error;
}
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, out_jpeg);
cinfo.image_width = raw->width;
cinfo.image_height = raw->height;
cinfo.input_components = IMAGE_NCHANNELS;
cinfo.in_color_space = JCS_GRAYSCALE;
cinfo.err = jpeg_std_error(&jerr);
jpeg_set_defaults(&cinfo);
jpeg_start_compress(&cinfo, TRUE);
row_stride = raw->width * IMAGE_NCHANNELS;
while (cinfo.next_scanline < cinfo.image_height) {
row_ptr[0] = &image[cinfo.next_scanline * row_stride];
jpeg_write_scanlines(&cinfo, row_ptr, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
fclose(out_jpeg);
error:
return err;
}
Como a imagem em raw não guarda informações sobre profundidade de cor e resolução da imagem, neste programa estou assumindo que cada pixel é representado por um byte (uint8_t) e a resolução deverá ser passada como parâmetro.
Resultado das transformações
Para gerar uma imagem em raw e depois converter para JPEG execute os seguintes comandos:
./build/jpeg2raw images/sapo_380x254.jpg images/grayscale.raw ./build/raw2jpeg images/grayscale.raw images/grayscale.jpeg 380 254
A imagem gerada em escala de cinza ficará no diretório images, junto com a imagem original colorida.
![]() |
| Imagem de arquivo pessoal |
Filtro de média bidimensional
Como havia dito, fiz um outro programa que aplica um filtro de médias em uma imagem em tons de cinza. O código está em src/mean_filter_2d.c. A função mean_filter_2d aplica um filtro de médias 3x3, ou seja, cada píxel é calculado como a média dos nove pixels mais próximos:
static void mean_filter_2d(uint8_t *in, uint8_t *out, size_t w, size_t h)
{
uint32_t l;
uint32_t c;
uint32_t i;
uint32_t pix;
for (l = 1; l < h; l++) {
for (c = 1; c < w; c++) {
i = (l * w) + c;
pix = (uint32_t) in[i - w - 1];
pix += (uint32_t) in[i - w];
pix += (uint32_t) in[i - w + 1];
pix += (uint32_t) in[i - 1];
pix += (uint32_t) in[i];
pix += (uint32_t) in[i + 1];
pix += (uint32_t) in[i + w - 1];
pix += (uint32_t) in[i + w];
pix += (uint32_t) in[i + w + 1];
out[i] = (uint8_t) (pix / 9);
}
}
}
Este programa recebe como parâmetros uma imagem de entrada (em raw), uma imagem de saída (também em raw) e a resolução da imagem.
Resultado do filtro
Para aplicar o filtro na imagem gerada com o comando anterior e gerar um JPEG da imagem filtrada, digite:
./build/mean_filter_2d images/grayscale.raw images/mean.raw 380 254 ./build/raw2jpeg images/mean.raw images/mean.jpeg 380 254
Para simplificar tudo isso, fiz o script run.sh que faz todo esse processo para uma imagem JPEG qualquer.
Nos próximos artigos vou trabalhar com outros tipos de filtros e o primeiro será halftoning.
Se você tem alguma sugestão, deixe um comentário!



dora teu blog
ResponderExcluirMuito bom! Quanto terá mais posts?
ResponderExcluirUltimamente estou sem tempo, mas pretendo escrever mais em breve!
ExcluirColega, muito bom seu post mas os seguintes erros estão ocorrendo ao compilar:
ResponderExcluirjpeg2raw.c:(.text+0x94): referência indefinida para `jpeg_std_error'
jpeg2raw.c:(.text+0xb4): referência indefinida para `jpeg_CreateDecompress'
jpeg2raw.c:(.text+0xcd): referência indefinida para `jpeg_stdio_src'
jpeg2raw.c:(.text+0xe1): referência indefinida para `jpeg_read_header'
jpeg2raw.c:(.text+0x1ad): referência indefinida para `jpeg_start_decompress'
jpeg2raw.c:(.text+0x22a): referência indefinida para `jpeg_read_scanlines'
jpeg2raw.c:(.text+0x371): referência indefinida para `jpeg_finish_decompress'
jpeg2raw.c:(.text+0x380): referência indefinida para `jpeg_destroy_decompress'
collect2: erro: ld returned 1 exit status
Olá Honório,
ExcluirNo seu caso, o linker não está achando as referências para as funções do libjpeg.
Você está compilando em qual sistema operacional?
No Ubuntu, instalando o pacote libjpeg-dev deve funcionar.
Em outros sistemas talvez mude alguma coisa, mas, se for o caso, procure por como instalar a biblioteca libjpeg62 ou libjpeg8.
Abraço,
Este comentário foi removido pelo autor.
ResponderExcluir