DIP+CV · Programming Exercise

EP01_10 — 📄 Metadata: Reading a PGM File

1.19.13 EP01_10 📄 Metadata: Reading a PGM File

In this activity, you must read an image file in PGM (Portable Gray Map) format and extract its dimensions from the header.

  • The PGM (P2) format is a plain text (ASCII) file that stores grayscale images.
  • The file has a header structured as follows:
  1. Version: The identifier P2.
  2. Comments: Optional lines starting with # (should be ignored).
  3. Dimensions: Two integers representing Width and Height.
  4. Maximum: An integer representing the maximum intensity (usually 255).
  • After the header, the pixel data follows.

📌 Important:

  • File Reading: You must open the file indicated in the example using Python’s open() function.
  • Output Order: Contrary to the order present in the file, the expected output must be in tuple format: (Height, Width, Channels).
  • Since PGM files are grayscale, the number of Channels is always 1.
  • See an interactive simulator for this question at Figure 1.20 (adjust the dimensions to see how the ASCII header is generated).

1.19.13.1 🧠 Understanding the PGM Format

The PGM format is one of the simplest for image processing. Because it is plain text, it allows you to view the metadata and even the pixel values by opening the file in a notepad:

Component Example Meaning
Magic Number P2 Identifies that it is a PGM in text format (ASCII).
Comment # CREATOR... Informational line ignored by the processor.
Dimensions 397 343 397 columns (Width) and 343 rows (Height).
Intensity 255 Defines the value of pure white (scale from 0 to 255).

1.19.13.2 📋 Task (VPL specification)

Input:

No keyboard input. The program must read the file "aula01fig03b.pgm" present in the execution directory.

Output:

A tuple containing (Height, Width, 1).

1.19.13.3 📌 Examples

File Name Expected Output Note
“aula01fig03b.pgm” (343, 397, 1) Note the order inversion: Height first
📄 Simulator EP01_10: PGM File Structure ASCII P2 & Format (H, W, C)

Change the width (W) and height (H) dimensions to observe the dynamic assembly of the PGM header and the tuple format of the resulting array in Python (Rows × Columns × Channels).

IMAGE DEFINITIONS
💡 Note the convention: The PGM header declares first W H (Width × Height), while the array in Python/NumPy reports the tuple as (H, W, C) (Rows × Columns × Channels).
FILE CONTENT (.PGM)
P2
# CREATOR: UFABC PDI / EP01_10
397 343
255
120 134 210 0 85 255 ...
Output of Function mm.readImg (Array Format):
(343, 397, 1)
Figure 1.20: Simulator EP01_10: PGM File Structure (ASCII P2 Header and Mapping to Python Tuple)

📝 Note

The file required for this EP will be automatically downloaded from the repository using the following code:

%%writefile tmp/mm_out_2.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <filesystem>

// Compile: g++ -std=c++17 download.cpp -o download

namespace fs = std::filesystem;

bool downloadFile(const std::string& url, const std::string& filename) {
    // Use curl command to download the file
    std::string command = "curl -s -A 'Mozilla/5.0' -o \"" + filename + "\" \"" + url + "\"";
    int result = std::system(command.c_str());
    return result == 0;
}

int main() {
    const std::string BASE_URL = "https://raw.githubusercontent.com/fzampirolli/pdi-vc/master/all/cap01/imagens";
    const std::string file = "aula01fig03b.pgm";

    std::vector<std::string> files = {file};

    for (const auto& f : files) {
        if (!fs::exists(f)) {
            std::string url = BASE_URL + "/" + f;
            try {
                if (downloadFile(url, f)) {
                    std::cout << "✅ Arquivo baixado: " << f << std::endl;
                } else {
                    std::cout << "❌ Erro: Não encontrado em " << url << std::endl;
                }
            } catch (const std::exception& e) {
                std::cout << "❌ Erro: " << e.what() << " em " << url << std::endl;
            }
        }
    }

    return 0;
}
Overwriting tmp/mm_out_2.cpp
!g++ -I. -std=c++17 tmp/mm_out_2.cpp -o tmp/mm_out_2 \
  && ./tmp/mm_out_2
%%writefile EP01_10.cpp
// your solution
Overwriting EP01_10.cpp
TestSuite("EP01_10.cpp").run()
✔️ EP01_10.cases already exists in casos/
📋 1 case(s) loaded from casos/EP01_10.cases

🔍 Testing C++: EP01_10.cpp
⚠️ EP01_10.cpp: Empty file (fewer than 3 lines). Tests skipped.