EDI+VA · Esercizio di Programmazione

EP02_11 — 🏆 Correzione della Prospettiva (Omografia) su Immagine Reale

2.12.11 EP02_11 🏆 Correzione della Prospettiva (Omografia) su Immagine Reale

In questa attività, l’obiettivo è applicare la trasformazione prospettica (omografia) per “raddrizzare” un oggetto inclinato in una fotografia reale. Lavorerai con l’immagine di un giornale, dove la griglia di un gioco di Sudoku è distorta a causa dell’angolo con cui è stata scattata la foto.

Il tuo programma deve leggere i parametri di input dal terminale, caricare l’immagine, calcolare la matrice di omografia \(3 \times 3\), applicare la trasformazione geometrica e visualizzare un indicatore globale di validazione.

  • Leggi due interi L e C, che rappresentano le dimensioni di righe e colonne (altezza e larghezza) che l’immagine rettificata di uscita deve avere.
  • Leggi quattro coppie di coordinate \((x, y)\) dal terminale, che rappresentano i quattro angoli del quadrilatero di origine (il Sudoku distorto nell’immagine originale).
  • Calcola automaticamente le quattro coppie di coordinate di destinazione utilizzando le dimensioni \(L\) e \(C\) fornite, mappando gli angoli agli estremi della nuova immagine: \((0,0)\), \((C-1, 0)\), \((0, L-1)\) e \((C-1, L-1)\).
  • Carica l’immagine locale sudoku.png e convertila in scala di grigi (grayscale).
  • Calcola la matrice di omografia e applica la trasformazione spaziale all’immagine.
  • Output: Calcola e stampa la somma di tutti i pixel dell’immagine risultante.

📌 Importante:

  • File di input: L’immagine sudoku.png deve trovarsi nella stessa cartella dello script. Il programma deve leggerla direttamente dal disco (es: usando mm::read("sudoku.png") o cv2.imread).
  • Ordine dei Punti: Assicurati che la lettura dei 4 punti di origine e la generazione dei 4 punti di destinazione seguano rigorosamente lo stesso ordine degli angoli: Superiore-Sinistro (TL), Superiore-Destro (TR), Inferiore-Sinistro (BL) e Inferiore-Destro (BR).
  • Dimensioni in OpenCV: Ricorda che funzioni come cv2.warpPerspective si aspettano la dimensione dell’immagine di uscita nel formato (larghezza, altezza), che equivale a (C, L).
  • Interpolazione: Per garantire la coerenza matematica della somma dei pixel con il correttore automatico, utilizza l’interpolazione bilineare standard (flags=cv2.INTER_LINEAR).
  • Crediti: L’immagine utilizzata è “Sudoku en periódico” di Héctor Rodríguez, sotto licenza CC BY 2.0.

2.12.11.1 🧠 Contesto del Problema

L’omografia ha 8 gradi di libertà, richiedendo almeno 4 corrispondenze di punti per essere calcolata. A differenza delle trasformazioni affini, mappa qualsiasi quadrilatero in un altro quadrilatero, permettendo che le linee che convergono verso punti di fuga tornino ad essere parallele:

Operazione Caratteristica Applicazione Tipica
Omografia Proiezione tra piani Rettifica di documenti, scansione di targhe e codici QR.
Mappatura Inversa Scansione dalla destinazione all’origine Evita “buchi” o pixel vuoti nell’immagine finale rettificata.
Warping Ricampionamento spaziale Correzione della distorsione delle lenti e montaggio di panorami (stitching).

2.12.11.2 📌 Esempi

Input Output Osservazione
500
500
100 120
420 95
80 440
450 460
32982820 I primi due input sono le dimensioni di uscita (\(L\) e \(C\)). Le 4 righe successive sono le coordinate \((x, y)\) degli angoli del Sudoku nell’immagine originale + PAD. L’output è la somma totale dei pixel dell’immagine rettificata.
200 200
100 120
420 95
80 440
450 460
5277150 Stessi punti di origine dell’esempio precedente, ma generando un’immagine di uscita più piccola (\(200 \times 200\)). La somma dei pixel si riduce proporzionalmente a causa della scala.

2.12.11.3 Acquisizione dell’immagine del sudoku e conversione in livelli di grigio

La Figura 2.22 mostra la lettura dell’immagine originale seguita dalla conversione in tonalità di grigio e dal ridimensionamento a una matrice di \(500 \times 500\) pixel, preparando i dati per la fase successiva.

La correzione di prospettiva, applicata nella Figura 2.23 tramite matrice di omografia, elimina le deformazioni causate dall’angolo della telecamera e produce una visione frontale e regolare della griglia del Sudoku.

%%writefile tmp/fig_02_sudoku_original.cpp
#define MM_OUT "tmp/fig_02_sudoku_original.png"
//| label: fig-02-sudoku-original
//| fig-cap: "Aquisição da imagem de um Sudoku à esquerda. À direita, conversão para tons de cinza e redimensionamento. Crédito: Héctor Rodríguez de Guardamar, Espanha (CC BY 2.0)."
//| echo: false
//| output: true

#include "morph.hpp"
#include <iostream>
#include <vector>
#include <filesystem>

int main() {
    std::string url = "https://upload.wikimedia.org/wikipedia/commons/e/e7/Sudoku_en_peri%C3%B3dico.jpg";
    mm::Image sudoku_rgb = mm::read(url);
    mm::Image sudoku_gray = mm::gray(sudoku_rgb);
    mm::Image sudoku_small = mm::resize(sudoku_gray, 500, 500);
    mm::write(sudoku_small, "sudoku.png");   // consumida pela célula fig-02-sudoku2

    mm::show(std::vector<mm::Image>{sudoku_rgb, sudoku_small}, MM_OUT, std::vector<std::string>{"Original RGB", "Cinza 500x500"}, 2);

    
// [pdi:panel-io] auto-generated — do not edit by hand
std::filesystem::create_directories("tmp");
mm::write(sudoku_rgb, "tmp/fig_02_sudoku_original_0.png");
mm::write(sudoku_small, "tmp/fig_02_sudoku_original_1.png");
// [pdi:panel-io:end]
return 0;
}
Overwriting tmp/fig_02_sudoku_original.cpp
!g++ -I. -std=c++17 tmp/fig_02_sudoku_original.cpp -o tmp/fig_02_sudoku_original \
  && ./tmp/fig_02_sudoku_original \
  && test -f "tmp/fig_02_sudoku_original.png" \
  || echo "⚠ mm::show não gravou tmp/fig_02_sudoku_original.png"
terminate called after throwing an instance of 'std::runtime_error'
  what():  mm::read: falha ao decodificar 'https://upload.wikimedia.org/wikipedia/commons/e/e7/Sudoku_en_peri%C3%B3dico.jpg'
/bin/bash, linha 1: 322827 Abortado                (imagem do núcleo gravada) ./tmp/fig_02_sudoku_original
⚠ mm::show não gravou tmp/fig_02_sudoku_original.png
try:
    mm.show(
        [
            mm.read("tmp/fig_02_sudoku_original_0.png"),
            mm.read("tmp/fig_02_sudoku_original_1.png"),
        ],
        titles=[
            'Original RGB',
            'Cinza 500x500',
        ],
        cols=2,
    )
except Exception as _e:
    print("figura indisponivel nesta trilha (C++): " + repr(_e) + " tmp/fig_02_sudoku_original_0.png (ver a versao Python)")
Figura 2.22: Aquisição da imagem de um Sudoku à esquerda. À direita, conversão para tons de cinza e redimensionamento. Crédito: Héctor Rodríguez de Guardamar, Espanha (CC BY 2.0).
%%writefile tmp/fig_02_sudoku2.cpp
#define MM_OUT "tmp/fig_02_sudoku2.png"
// label: fig-02-sudoku2
// fig-cap: "Correção de perspectiva por homografia 3×3: imagem com *padding* e a vista frontal retificada, via *mm::perspective_transform* (solve 8×8 dos 4 pontos + mapeamento inverso projetivo)."
// echo: true
// output: true
#include "morph.hpp"
#include <vector>
#include <filesystem>

int main() {
    // 1. Imagem gravada pela célula anterior (sudoku.png, 500×500, cinza)
    mm::Image img = mm::read("sudoku.png");

    // 2. Padding para não cortar os vértices da grade (mm::pad preenche com 0)
    int PAD = 60;
    mm::Image img_pad = mm::pad(img, PAD);

    // 3. Cantos da grade na imagem expandida — TL, TR, BL, BR
    const double pts1[4][2] = {{100, 160}, {390, 45}, {200, 580}, {570, 420}};

    // 4. Cantos de destino: vista frontal SIZE×SIZE
    int SIZE = 500;
    const double pts2[4][2] = {{0, 0}, {SIZE, 0}, {0, SIZE}, {SIZE, SIZE}};

    // 5. Homografia pts1 -> pts2 e retificação
    mm::Image img_rect = mm::perspective_transform(img_pad, pts1, pts2, SIZE, SIZE);

    // 6. Exibição
    mm::show(
        std::vector<mm::Image>{img_pad, img_rect},
        MM_OUT,
        std::vector<std::string>{"Original (com padding)", "Vista frontal retificada"},
        2
    );

    
// [pdi:panel-io] auto-generated — do not edit by hand
std::filesystem::create_directories("tmp");
mm::write(img_pad, "tmp/fig_02_sudoku2_0.png");
mm::write(img_rect, "tmp/fig_02_sudoku2_1.png");
// [pdi:panel-io:end]
return 0;
}
Overwriting tmp/fig_02_sudoku2.cpp
!g++ -I. -std=c++17 tmp/fig_02_sudoku2.cpp -o tmp/fig_02_sudoku2 \
  && ./tmp/fig_02_sudoku2 \
  && test -f "tmp/fig_02_sudoku2.png" \
  || echo "⚠ mm::show não gravou tmp/fig_02_sudoku2.png"
tmp/fig_02_sudoku2.cpp: In function ‘int main()’:

tmp/fig_02_sudoku2.cpp:23:41: warning: narrowing conversion of ‘SIZE’ from ‘int’ to ‘double’ [-Wnarrowing]

   23 |     const double pts2[4][2] = {{0, 0}, {SIZE, 0}, {0, SIZE}, {SIZE, SIZE}};

      |                                         ^~~~

tmp/fig_02_sudoku2.cpp:23:55: warning: narrowing conversion of ‘SIZE’ from ‘int’ to ‘double’ [-Wnarrowing]

   23 |     const double pts2[4][2] = {{0, 0}, {SIZE, 0}, {0, SIZE}, {SIZE, SIZE}};

      |                                                       ^~~~

tmp/fig_02_sudoku2.cpp:23:63: warning: narrowing conversion of ‘SIZE’ from ‘int’ to ‘double’ [-Wnarrowing]

   23 |   const double pts2[4][2] = {{0, 0}, {SIZE, 0}, {0, SIZE}, {SIZE, SIZE}};

      |                                                             ^~~~



tmp/fig_02_sudoku2.cpp:23:69: warning: narrowing conversion of ‘SIZE’ from ‘int’ to ‘double’ [-Wnarrowing]

   23 | const double pts2[4][2] = {{0, 0}, {SIZE, 0}, {0, SIZE}, {SIZE, SIZE}};

      |                                                                 ^~~~



[1] Original (com padding)

[2] Vista frontal retificada
try:
    mm.show(
        [
            mm.read("tmp/fig_02_sudoku2_0.png"),
            mm.read("tmp/fig_02_sudoku2_1.png"),
        ],
        titles=[
            'Original (com padding)',
            'Vista frontal retificada',
        ],
        cols=2,
    )
except Exception as _e:
    print("figura indisponivel nesta trilha (C++): " + repr(_e) + " tmp/fig_02_sudoku2_0.png (ver a versao Python)")
Figura 2.23: Correção de perspectiva por homografia 3×3: imagem com padding e a vista frontal retificada, via mm::perspective_transform (solve 8×8 dos 4 pontos + mapeamento inverso projetivo).
🎮 Simulatore EP02_11: Prospettiva del Sudoku Omografia 3×3 · CC BY 2.0
📷 Originale (Espansa) — Trascina gli Angoli Foto: Héctor Rodríguez · CC BY 2.0
✅ Corretta (400×400) — Vista Frontale
Caricamento immagine del Sudoku...
Figura 2.24: Simulatore EP02_11: Correzione della Prospettiva del Sudoku (Omografia 3×3 con Ricampionamento Bilineare)
%%writefile EP02_11.cpp
// your solution
Overwriting EP02_11.cpp
TestSuite("EP02_11.cpp").run()
✔️ EP02_11.cases esiste già in casos/
📋 4 caso/i caricato/i da casos/EP02_11.cases

🔍 Test di C++: EP02_11.cpp
⚠️ EP02_11.cpp: file vuoto (meno di 3 righe). Test saltati.