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!


