📚 Capítulo do curso: 11 — Matrizes Multidimensionais, Row-Major Order e Álgebra Linear da Especialização em Sistemas com C. Leia o capítulo antes de começar.

🖼️ Miniprojeto C 11 — Matrizes 2D e Filtro de Convolução

Álgebra linear com mapeamento Row-Major Order: aplique um filtro de convolução 3x3 sobre uma “imagem” 5x5 em tons de cinza.

⏱️ Tempo estimado 📦 Você vai entregar ✅ Pronto quando
50 min main.c a borda fica intacta e o pixel central vira 206

Diagrama do 🖼️ Miniprojeto C 11 — Matrizes 2D e Filtro de Convolução

🎯 Objetivos

📋 O problema

Uma “imagem” em tons de cinza (valores de 0 a 255) de 5x5 pixels chega ao seu processador de sinais. Você deve aplicar um filtro de suavização (blur) usando um kernel de convolução 3x3: cada pixel interno vira a média aritmética dos 9 pixels ao seu redor (incluindo ele mesmo). Os pixels de borda (linha 0/4 ou coluna 0/4) não têm os 8 vizinhos completos necessários para a convolução — nesta versão, eles são simplesmente copiados sem alteração para a matriz resultado (estratégia de “borda replicada sem convolução”).

O programa (main.c) deve imprimir a matriz original e a matriz resultante, lado a lado ou em sequência, em formato de grade legível e alinhada.

ID Requisito
RF01 O programa deve compilar sem nenhum aviso com gcc -Wall -Wextra -Wpedantic -std=c17.
RF02 Deve declarar estaticamente uma matriz int imagem[5][5] com valores entre 0 e 255.
RF03 Deve implementar aplicar_convolucao_blur, que recebe a matriz de origem e preenche uma matriz de destino usando um kernel 3x3 de média (soma dos 9 vizinhos dividida por 9).
RF04 Somente os pixels internos (índices de linha e coluna entre 1 e 3, numa matriz 5x5 com kernel 3x3) recebem a convolução; os pixels de borda são copiados sem alteração.
RF05 Deve imprimir a matriz original e a matriz resultante em formato de grade legível, com colunas alinhadas.
graph LR
    A["Matriz origem 5x5\n(imagem em tons de cinza)"] --> B["Copia a matriz\npara o destino (borda)"]
    B --> C["Para cada pixel interno\n(linhas/colunas 1 a 3):\naplica kernel 3x3"]
    C --> D["Matriz destino\n(imagem com blur)"]
    D --> E["Impressao das\nduas matrizes"]

    style B fill:#0284c7,stroke:#38bdf8,color:#ffffff
    style C fill:#0d9488,stroke:#2dd4bf,color:#ffffff
    style E fill:#059669,stroke:#34d399,color:#ffffff

🧰 Ambiente

O mesmo do Miniprojeto 01: gcc 8 ou mais novo. No Windows, rode chcp 65001 se os emojis aparecerem quebrados.


Passo 1 — Compilar o esqueleto

🎯 Objetivo: partir de um código que compila e roda, antes de escrever qualquer linha.

⌨️ Faça: crie a pasta c_mini_11, salve o código abaixo como main.c e rode:

gcc -Wall -Wextra -Wpedantic -std=c17 main.c -o app
./app          # Linux/macOS
.\app.exe      # Windows (PowerShell)

📄 src/esqueleto.c

/*
 * Miniprojeto 11: Processador de Matrizes 2D e Filtro de Convolucao
 * Capitulo 11: matrizes multidimensionais, row-major, operacoes matriciais.
 */
#include <stdio.h>
#include <stdlib.h>

#define LINHAS 5
#define COLUNAS 5

void imprimir_matriz(int matriz[LINHAS][COLUNAS]) {
    (void)matriz; // stub: apague esta linha ao implementar
    // TODO 1: percorra a matriz com dois lacos for aninhados e imprima
    // cada valor alinhado (dica: use "%4d" para alinhar colunas).
}

void aplicar_convolucao_blur(int origem[LINHAS][COLUNAS], int destino[LINHAS][COLUNAS]) {
    (void)origem; (void)destino; // stub: apague esta linha ao implementar
    // TODO 2: primeiro, copie TODOS os pixels de origem para destino
    // (isso garante que a borda fique "replicada sem alteracao").

    // TODO 3: depois, para cada pixel INTERNO (i de 1 a 3, j de 1 a 3),
    // some os 9 vizinhos (kernel 3x3 de -1 a +1 em torno de i,j) e
    // divida por 9, sobrescrevendo destino[i][j] com a media.
}

int main(void) {
    int imagem[LINHAS][COLUNAS] = {
        { 10,  10,  10,  10,  10},
        { 10, 200, 200, 200,  10},
        { 10, 200, 255, 200,  10},
        { 10, 200, 200, 200,  10},
        { 10,  10,  10,  10,  10}
    };

    printf("Matriz original (imagem 5x5):\n");
    imprimir_matriz(imagem);

    // TODO 4: declare int resultado[LINHAS][COLUNAS];, chame
    // aplicar_convolucao_blur(imagem, resultado) e imprima
    // a matriz resultado com um cabecalho identificando-a.

    return EXIT_SUCCESS;
}

✅ Checkpoint: zero avisos do gcc, e a saída é:

🖥️ Saída esperada:

Matriz original (imagem 5x5):

💡 As linhas (void)matriz; e similares silenciam o aviso de parâmetro não usado enquanto a função é um stub. Apague cada uma ao implementar a função.


Passo 2 — Imprimir a matriz (TODO 1)

🎯 Objetivo: percorrer linha × coluna e alinhar a saída.

⌨️ Faça: dois for aninhados, printf("%4d", matriz[i][j]) e uma quebra de linha ao fim de cada linha.

✅ Checkpoint: a imagem original aparece como uma grade 5×5 alinhada, com o 255 no centro.


Passo 3 — Copiar e convoluir (TODO 2, 3 e 4)

🎯 Objetivo: aplicar um kernel 3×3 sem ler valores que você mesmo acabou de escrever.

⌨️ Faça:

  1. Em aplicar_convolucao_blur, copie toda a origem para o destino. Isso deixa a borda pronta.
  2. Para i e j de 1 a LINHAS - 2, some os 9 vizinhos de origem (offsets ki e kj de -1 a +1) e grave soma / 9 em destino[i][j].
  3. No main, declare int resultado[LINHAS][COLUNAS];, chame a função e imprima o resultado com um cabeçalho.

✅ Checkpoint: a borda continua com 10. Os quatro cantos internos viram 100, as laterais internas 142 e o centro 206. Confira à mão: (8 × 200 + 255) / 9 = 1855 / 9 = 206.

🧯 Se der errado:


Passo final — Conferir com o gabarito

🎯 Objetivo: validar a saída completa e o código de retorno.

✅ Checkpoint: a saída é esta, e o código de retorno é 0 (echo $? no Linux/macOS ou echo $LASTEXITCODE no PowerShell):

🖥️ Saída esperada:

====================================================
 ⚡ Processador de Matrizes 2D e Filtro de Convolucao
====================================================

Matriz original (imagem 5x5):
  10  10  10  10  10
  10 200 200 200  10
  10 200 255 200  10
  10 200 200 200  10
  10  10  10  10  10

Matriz apos convolucao (blur 3x3, borda replicada):
  10  10  10  10  10
  10 100 142 100  10
  10 142 206 142  10
  10 100 142 100  10
  10  10  10  10  10

====================================================
 Pixel central: original=255 -> apos blur=206
====================================================

Só abra o gabarito depois de chegar até aqui:

🔑 Ver gabarito: src/main.c

📄 src/main.c

/*
 * Miniprojeto 11: Processador de Matrizes 2D e Filtro de Convolucao
 * Especializacao em Engenharia de Sistemas em C
 * Capitulo 11: matrizes multidimensionais, row-major, operacoes matriciais.
 */
#include <stdio.h>
#include <stdlib.h>

#define LINHAS 5
#define COLUNAS 5

void imprimir_matriz(int matriz[LINHAS][COLUNAS]) {
    for (int i = 0; i < LINHAS; i++) {
        for (int j = 0; j < COLUNAS; j++) {
            printf("%4d", matriz[i][j]);
        }
        printf("\n");
    }
}

void aplicar_convolucao_blur(int origem[LINHAS][COLUNAS], int destino[LINHAS][COLUNAS]) {
    /* Passo 1: copia tudo (garante que a borda fique replicada sem convolucao) */
    for (int i = 0; i < LINHAS; i++) {
        for (int j = 0; j < COLUNAS; j++) {
            destino[i][j] = origem[i][j];
        }
    }

    /* Passo 2: aplica o kernel 3x3 de media apenas nos pixels internos */
    for (int i = 1; i < LINHAS - 1; i++) {
        for (int j = 1; j < COLUNAS - 1; j++) {
            int soma = 0;
            for (int ki = -1; ki <= 1; ki++) {
                for (int kj = -1; kj <= 1; kj++) {
                    soma += origem[i + ki][j + kj];
                }
            }
            destino[i][j] = soma / 9;
        }
    }
}

int main(void) {
    int imagem[LINHAS][COLUNAS] = {
        { 10,  10,  10,  10,  10},
        { 10, 200, 200, 200,  10},
        { 10, 200, 255, 200,  10},
        { 10, 200, 200, 200,  10},
        { 10,  10,  10,  10,  10}
    };
    int resultado[LINHAS][COLUNAS];

    printf("====================================================\n");
    printf(" ⚡ Processador de Matrizes 2D e Filtro de Convolucao\n");
    printf("====================================================\n");

    printf("\nMatriz original (imagem 5x5):\n");
    imprimir_matriz(imagem);

    aplicar_convolucao_blur(imagem, resultado);

    printf("\nMatriz apos convolucao (blur 3x3, borda replicada):\n");
    imprimir_matriz(resultado);

    printf("\n====================================================\n");
    printf(" Pixel central: original=%d -> apos blur=%d\n", imagem[2][2], resultado[2][2]);
    printf("====================================================\n");

    return EXIT_SUCCESS;
}

Por que isso importa: note que aplicar_convolucao_blur primeiro copia a matriz inteira e só depois sobrescreve os pixels internos — essa ordem evita ler valores já modificados (destino) ao calcular a soma, já que a soma sempre lê de origem, nunca de destino. Misturar leitura e escrita na mesma matriz durante uma convolução é um erro clássico de processamento de imagens.


🏆 Desafio

Implemente um segundo kernel de detecção de bordas (uma versão simplificada do operador Sobel), por exemplo o kernel horizontal { {-1,0,1},{-2,0,2},{-1,0,1} }, aplicando-o com a mesma estrutura de laços aninhados (mas agora multiplicando cada vizinho pelo peso correspondente do kernel, em vez de somar tudo e dividir por 9). Compare a saída do blur (suaviza) com a saída da detecção de bordas (realça transições bruscas de intensidade). Como desafio extra, troque a estratégia de borda: em vez de copiar sem convoluir, faça “clamping” — trate um vizinho fora dos limites como se fosse igual ao pixel de borda mais próximo, permitindo convoluir também a moldura da imagem.

✅ Checkpoint: com o kernel horizontal de Sobel, a coluna central interna dá 0 (vizinhança simétrica: não há borda vertical ali). A coluna da esquerda dá 625, 870, 625 e a da direita -625, -870, -625, porque a intensidade sobe à esquerda e desce à direita. Valores fora de 0–255 são normais em detecção de bordas.


🧭 Navegação

📋 Projetos do curso de C | 📚 Teoria do Capítulo 11 | 🗂️ Hub de projetos