📚 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.
Á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 |
int matriz[LINHAS][COLUNAS]) e entender seu mapeamento row-major na memória.for aninhados em profundidade (linha × coluna × kernel-linha × kernel-coluna) mantendo o código legível.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
O mesmo do Miniprojeto 01: gcc 8 ou mais novo. No Windows, rode chcp 65001 se os emojis aparecerem quebrados.
🎯 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.
🎯 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.
🎯 Objetivo: aplicar um kernel 3×3 sem ler valores que você mesmo acabou de escrever.
⌨️ Faça:
aplicar_convolucao_blur, copie toda a origem para o destino. Isso deixa a borda pronta.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].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:
142 virou outro número): a soma está lendo de destino, que já foi parcialmente sobrescrito. A soma tem que ler sempre de origem.0 e leu origem[-1][...], fora da matriz.🎯 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:
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.
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.
📋 Projetos do curso de C | 📚 Teoria do Capítulo 11 | 🗂️ Hub de projetos