2.12.11 EP02_11 🏆 Perspective Correction (Homography) in a Real Image
In this activity, the goal is to apply perspective transformation (homography) to “rectify” a tilted object in a real photograph. You will work with an image of a newspaper, where the grid of a Sudoku puzzle is distorted due to the angle at which the photo was taken.
Your program must read input parameters from the terminal, load the image, compute the \(3 \times 3\) homography matrix, apply the geometric transformation, and display a global validation indicator.
- Read two integers L and C, representing the row and column dimensions (height and width) that the rectified output image should have.
- Read four coordinate pairs \((x, y)\) from the terminal, representing the four corners of the source quadrilateral (the distorted Sudoku in the original image).
- Automatically compute the four destination coordinate pairs using the provided dimensions \(L\) and \(C\), mapping the corners to the edges of the new image: \((0,0)\), \((C-1, 0)\), \((0, L-1)\), and \((C-1, L-1)\).
- Load the local image
sudoku.pngand convert it to grayscale. - Compute the homography matrix and apply the spatial transformation to the image.
- Output: Calculate and print the sum of all pixels of the resulting image.
📌 Important:
- Input file: The image
sudoku.pngmust be in the same folder as the script. The program must read it directly from disk (e.g., usingmm.read("sudoku.png")orcv2.imread). - Point Order: Ensure that the reading of the 4 source points and the generation of the 4 destination points strictly follow the same corner order: Top-Left (TL), Top-Right (TR), Bottom-Left (BL), and Bottom-Right (BR).
- Dimensions in OpenCV: Remember that functions such as
cv2.warpPerspectiveexpect the output image size in the format(width, height), which is equivalent to(C, L). - Interpolation: To ensure mathematical consistency of the pixel sum with the automatic grader, use the default bilinear interpolation (
flags=cv2.INTER_LINEAR). - Credits: The image used is “Sudoku en periódico” by Héctor Rodríguez, licensed under CC BY 2.0.
2.12.11.1 🧠 Problem Context
The homography has 8 degrees of freedom, requiring at least 4 point correspondences to be computed. Unlike affine transformations, it maps any quadrilateral to another quadrilateral, allowing lines that converge to vanishing points to become parallel again:
| Operation | Characteristic | Typical Application |
|---|---|---|
| Homography | Projection between planes | Document rectification, scanning of plates and QR Codes. |
| Inverse Mapping | Scanning from destination to source | Avoids “holes” or empty pixels in the final rectified image. |
| Warping | Spatial resampling | Correction of lens distortion and panorama stitching. |
2.12.11.2 📌 Examples
| Input | Output | Observation |
|---|---|---|
| 500 500 100 120 420 95 80 440 450 460 |
32982820 | The first two inputs are the output dimensions (\(L\) and \(C\)). The following 4 lines are the \((x, y)\) coordinates of the Sudoku corners in the original image + PAD. The output is the total sum of pixels of the rectified image. |
| 200 200 100 120 420 95 80 440 450 460 |
5277150 | Same source points as the previous example, but generating a smaller output image (\(200 \times 200\)). The pixel sum decreases proportionally due to the scale. |
2.12.11.3 Sudoku image acquisition and conversion to grayscale
Figure 2.22 shows the reading of the original image followed by its conversion to grayscale and resizing to a \(500 \times 500\) pixel matrix, preparing the data for the next step.
The perspective correction, applied at Figure 2.23 via the homography matrix, eliminates distortions caused by the camera angle and produces a frontal, regular view of the Sudoku grid.
import cv2
import numpy as np
# --- 1. Loads the saved image (sudoku.png) ---
img = mm.read("sudoku.png") # BGR, 500×500
# --- 2. Padding to avoid cutting off vertices ---
PAD = 60
img_pad = cv2.copyMakeBorder(
img, PAD, PAD, PAD, PAD,
cv2.BORDER_CONSTANT, value=[255, 255, 255]
)
# --- 3. Source points (grid corners in the expanded image) ---
pts1 = np.float32([
[100, 160], # TL
[390, 45], # TR
[200, 580], # BL
[570, 420], # BR
])
# W H
# --- 4. Destination points (front view 500×500) ---
SIZE = 500
pts2 = np.float32([
[0, 0],
[SIZE, 0],
[0, SIZE],
[SIZE, SIZE],
])
# --- 5. Homography and rectification ---
img_rect = mm.perspective_transform(img_pad, pts1, pts2, size=(SIZE, SIZE))
# --- 6. Display ---
mm.show(
[img_pad, img_rect],
titles=["Original (with padding)", "Rectified front view"],
cols=2, figsize=(10, 6), axis=True
)
%%writefile EP02_11.py
# Python codeOverwriting EP02_11.py
TestSuite("EP02_11.py").run()✔️ EP02_11.cases already exists in casos/
📋 4 case(s) loaded from casos/EP02_11.cases
🔍 Testing Python: EP02_11.py
⚠️ EP02_11.py: Empty file (fewer than 3 lines). Tests skipped.