DIP+CV · Programming Exercise

EP09_02 — 🟢 Manual Pooling (Maximum and Average)

9.10.2 EP09_02 🟢 Manual Pooling (Maximum and Average)

Between convolutional blocks, the typical architecture of a CNN interleaves pooling layers, which reduce the spatial resolution of the feature map without introducing new trainable parameters — unlike convolution, pooling has no weights: it simply summarizes each window of the input into a single value, either by a maximum or by an average, exactly as formalized in the Section “Pooling”.

You have been tasked with implementing this operation using a square sliding window, without partial overlap at the borders (only complete windows), supporting the two most common types: max (preserves the most salient value, typically used to retain strong edges and textures) and avg (smooths the region, preserving average intensity information).

9.10.2.1 📋 Implementation Guidelines

  1. Input: Read the dimensions \(H \times W\) of the input feature map and its \(H \times W\) real values.
  2. Window: Read the integers \(k\) (size of the square window \(k \times k\)) and \(s\) (stride).
  3. Type: Read a string, max or avg, indicating the type of pooling.
  4. No padding: This operation does not use padding; windows that would exceed the input border are discarded.
  5. Calculation: For each output position \((i,j)\), compute the maximum or the average of the \(k \times k\) values of the corresponding window, starting at \((i \cdot s,\, j \cdot s)\).
  6. Output dimensions: \(O_h = \lfloor (H - k)/s \rfloor + 1\) and \(O_w = \lfloor (W - k)/s \rfloor + 1\).
  7. Output: Print \(O_h\) and \(O_w\) on the first line, followed by \(O_h\) lines with \(O_w\) real values each, formatted with 4 decimal places.

9.10.2.2 📌 Computational Constraints

  • Square window: \(k \times k\), with no support for rectangular windows in this version.
  • No padding: only windows fully contained within the input are considered — dimensions that “remain” are simply discarded.
  • avg uses real division: the average is always \(\text{sum}/k^2\), even when the result has many decimal places — round only in the final formatting, according to the general guideline of the chapter.
  • Formatting: all output values with exactly 4 decimal places.

9.10.2.3 🧠 Theoretical Foundation

Element Role in the architecture
Maximum pooling Preserves the strongest activation of the window; common after convolutional layers to retain salient edges and textures
Average pooling Smooths the region, preserving average intensity; common in final layers (global average pooling)
Absence of parameters Differentiates pooling from convolution: reduces spatial resolution without additional training cost
Resolution reduction Contributes to invariance to small translations and to the reduction of the computational cost of subsequent layers

9.10.2.4 🧩 Methods from morph.py that may help

morph.py does not implement pooling with subsampling directly, but two families of operations show the same idea from another perspective, useful for checking your intuition:

  • mm.dil(f, Bc) / mm.dil0(f, B) — morphological dilation: replaces each pixel by the maximum of its neighborhood defined by the structuring element \(B\) (e.g., mm.sebox(n) for a \((2n+1)\times(2n+1)\) window). It is, conceptually, a “max-pooling without subsampling” (produces an image of the same size, rather than a reduced one).
  • mm.blur(f, N) — averaging smoothing in an \(N \times N\) window, analogous to avg-pooling, also without resolution reduction.
  • mm.readImg(h, w, dtype='float') — useful for reading the input map in floating point.

9.10.2.5 📦 Input and Output Specification (VPL)

Input:

  • Line 1: Integers \(H\) and \(W\).
  • Next \(H\) lines: \(W\) real values each.
  • Next line: Integers \(k\) and \(s\).
  • Next line: max or avg.

Output:

  • Line 1: Integers \(O_h\) and \(O_w\).
  • Next \(O_h\) lines: \(O_w\) real values each, with 4 decimal places.

9.10.2.6 📌 Examples

Input Output Observation
4 4
1 3 2 4
5 6 1 2
2 1 0 3
4 2 5 1
2 2
max
2 2
6.0000 4.0000
4.0000 5.0000
Maximum pooling, \(2\times2\) window, stride 2.
4 4
1 3 2 4
5 6 1 2
2 1 0 3
4 2 5 1
2 2
avg
2 2
3.7500 2.2500
2.2500 2.2500
Average pooling over the same windows.
🎮 Simulator: Manual Pooling 🟢 no padding, full windows

Fixed 4×4 input — adjust the window size (k), stride (s) and type, exactly the parameters that EP09_02 reads as input, and see how they change the size and values of the output.

Window (k)
Stride (s)
Type
(0,0)
Input X (4×4)
outside the window current window discarded (leftover)
Output Y (pooling)

💡 Each slider position reveals a cell of the output matrix. Dashed-gray cells in the input are "leftovers" that no window reaches — note how this happens when (H−k) is not a multiple of s. Changing k, s, or the type restarts the exploration.

Figure 9.44: Simulator EP09_02: Manual Pooling (max vs. mean, with adjustable window k and stride s)
%%writefile EP09_02.py
# Python code
Overwriting EP09_02.py
TestSuite("EP09_02.py").run()
✔️ EP09_02.cases already exists in casos/
📋 4 case(s) loaded from casos/EP09_02.cases

🔍 Testing Python: EP09_02.py
⚠️ EP09_02.py: Empty file (fewer than 3 lines). Tests skipped.