diff --git a/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/_category_.json b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/_category_.json
new file mode 100644
index 000000000..3b9af70b1
--- /dev/null
+++ b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/_category_.json
@@ -0,0 +1,10 @@
+{
+ "label": "mergeSmallRegionsInPlace.h",
+ "position": 3,
+ "link": {
+ "type": "generated-index",
+ "title": "Merging Small Pixel Regions",
+ "description": "A single-pass C++ routine that finds connected-color regions in an RGBA image, computes per-region bounding-box metadata, and replaces pixels belonging to small regions by sampling colors from adjacent large regions (effectively merging small islands into nearby large blobs).",
+ "slug": "/reference/wasm/modules/image/mergeSmallRegionsInPlace"
+ }
+}
diff --git a/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/complexity-and-memory.md b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/complexity-and-memory.md
new file mode 100644
index 000000000..1375201e6
--- /dev/null
+++ b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/complexity-and-memory.md
@@ -0,0 +1,22 @@
+---
+id: complexity-and-memory
+title: Complexity and Memory
+sidebar_label: Complexity and Memory
+sidebar_position: 6
+description: A breakdown of the time complexity, memory usage, and cache behavior of the mergeSmallRegionsInPlace function.
+---
+
+$$
+\text{Let } N = width \times height
+$$
+
+## Time
+The flood-fill visits each pixel once and tests 4 neighbours ($$4 \cdot N$$), so the dominant cost is $$O(N)$$ with a small constant
+(neighbour checks and byte comparisons). The merge pass is another similar $$O(N)$$ scan, so overall $$O(N)$$.
+
+## Memory
+The implementation allocates a `labels` array of $N$ integers and a `regions` vector whose size equals number of components
+(at most $$N$$). So memory is $$O(N)$$ additional to the image buffer.
+
+## Cache behaviour
+BFS queue can cause random access inside large components; using a scanline two-pass connected-component algorithm (or union-find) can improve cache locality and throughput on big images.
diff --git a/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/connected-components.md b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/connected-components.md
new file mode 100644
index 000000000..4ebc33f45
--- /dev/null
+++ b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/connected-components.md
@@ -0,0 +1,120 @@
+---
+id: connected-components
+title: Connected Components & Small Region Merging
+sidebar_label: Connected Components
+sidebar_position: 3
+description: >
+ Step-by-step explanation of the connected-component labeling algorithm (mergeSmallRegionsInPlace) with flood-fill,
+ small-region removal, and merging into larger neighboring regions. Includes mathematical background and visual examples.
+---
+
+## High-Level Algorithm
+
+1. **Labeling / connected-component flood-fill**: iterate pixels, perform a breadth-first (queue) flood-fill
+whenever an unlabeled pixel is found. Two pixels are considered connected if they are 4-neighbours (up, down, left, right)
+and their RGBA values are exactly equal.
+
+
+
+
+ Center pixel (being flood-filled)
+
+
+
+ Other pixels (unlabeled neighbor candidates)
+
+
+
+ Other pixels (unlabeled non-neighbor candidates)
+
+
+
+
+
+
+2. While flood-filling, `Region` metadata is collected (`size`, `minX`, `maxX`, `minY`, `maxY`) and labeled according to an index (`labels`).
+3. After all components are labeled and regions metadata computed,
+iterate pixels again. For a pixel whose region is considered *small*
+(fails `isBigEnough(minArea,minWidth,minHeight)`),
+check its four immediate neighbors. If any neighbor belongs to a *big* region,
+copy that neighbor's RGBA into the small pixel (effectively assigning the small pixel to the big region;
+over time, the small region is consumed by *bigger neighboring regions*).
+
+:::note
+This merges only small-region pixels which are adjacent to large regions.
+The order of iteration means small pixels near large regions are captured first —
+the implementation stops at first qualifying neighbour.
+:::
+
+## Mathematical Background
+
+### Connected components
+
+The algorithm computes **connected components** on a planar grid using 4-connectivity.
+Formally, we can describe the image as a function:
+$$
+\begin{align*}
+I &: \mathbb{Z}^2 \to \mathcal{C} \\
+(x, y) &\mapsto I(x, y)
+\end{align*}
+$$
+
+:::important
+- $I$ is the image function.
+- $\mathbb{Z}^2$ is the set of all integer pairs $(x, y)$ representing pixel coordinates.
+- $\mathcal{C}$ is the set of all possible RGBA values:
+ $$
+ \mathcal{C} = \{ (R, G, B, A) \mid R,G,B,A \in [0,255] \}
+ $$
+- $I(x, y) \in \mathcal{C}$ is the color of the pixel at coordinates $(x, y)$.
+
+> In simple terms, each pixel at position $(x, y)$ has a color given by $I(x, y)$.
+:::
+
+Two pixels, $$p=(x,y)$$ and $$q=(x',y')$$, are **4-adjacent** if $$|x-x'| + |y-y'| = 1$$.
+A connected component is a maximal set of pixels, $$S$$, such that any two pixels in $$S$$ are connected by a path of 4-adjacent pixels with identical colors.
+
+A flood-fill (BFS / DFS) computes these components exactly.
+
+### Bounding box & geometric heuristics
+
+For each component we compute an axis-aligned bounding box with integer coordinates:
+$$
+[minX,maxX]\times[minY,maxY]
+$$
+The bounding-box width and height are:
+$$
+\begin{align*}
+W &= maxX - minX + 1 \\
+H &= maxY - minY + 1
+\end{align*}
+$$
+
+:::tip
+See the source code in the [Region struct](https://github.com/Ryan-Millard/Img2Num/blob/main/src/wasm/modules/image/src/mergeSmallRegionsInPlace.cpp)
+to understand how this is used.
+:::
+
+The area (component size) is simply the number of pixels in the component, $$|S|$$.
+The heuristics used to classify *small* vs *big* regions rely on thresholds on both area and bounding box dimensions.
+This avoids keeping long thin noise (e.g. a long 1-pixel-wide arm) even if its area is above `minArea`.
+
+### Why 4-connectivity, not 8?
+
+4-connectivity treats diagonally touching pixels as disconnected.
+This is stricter and avoids connecting components that only meet at a corner.
+Depending on the data, 8-connectivity (connect diagonally too) may be preferred — see the [Variants & Improvements page](../variants-and-improvements).
+
+### What does merging mean here?
+
+The code does not merge region graphs with union operations. Instead, it performs a **pixel-wise recoloring** of small-region pixels to the color of an adjacent big region. That has the practical effect of attaching each small pixel to a neighboring large region. This is a cheap and local merge — it won’t always choose the most semantically correct neighbor if multiple are present.
diff --git a/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/faq.md b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/faq.md
new file mode 100644
index 000000000..8f9b6aa3a
--- /dev/null
+++ b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/faq.md
@@ -0,0 +1,363 @@
+---
+id: faq
+title: FAQ
+sidebar_label: FAQ
+sidebar_position: 10
+description: Frequently asked questions related to the mergeSmallRegionsInPlace function.
+---
+
+# Frequently Asked Questions
+
+## What problem does `mergeSmallRegionsInPlace` solve?
+
+This function removes **tiny connected regions** in an RGBA image by merging them into neighboring, sufficiently large regions **in-place**.
+
+It is intended for post-processing steps after:
+- k-means color quantization
+- segmentation
+
+Or as a pre-processing step before:
+- image-to-vector (SVG) pipelines
+
+Small regions often appear as visual noise and make downstream geometry extraction harder.
+
+## What exactly is a “region” in this context?
+A **region** is a *4-connected component* of pixels where:
+- Each pixel is connected via **up, down, left, or right**
+- All pixels have **exactly the same RGBA values**
+
+Diagonal adjacency **does not** count.
+
+## Why use 4-connectivity instead of 8-connectivity?
+4-connectivity:
+- Matches most raster algorithms (flood-fill, contour tracing)
+- Avoids diagonal “corner-touch” artifacts
+- Produces cleaner, grid-aligned regions
+
+Using 8-connectivity would merge diagonally touching pixels that visually are not part of the same region.
+
+## Why is the image indexed as `idx(x, y, width) * 4`?
+
+Each pixel consists of **4 bytes**: `R`, `G`, `B`, `A`
+
+So the linear index into the pixel buffer is:
+
+$$
+(\text{y} \cdot \text{width} + \text{x}) \times 4
+$$
+
+
+Visual Example: 5×5 RGBA Image Indexing
+
+
+
+
+
+:::note Assumptions this layout has
+- Row-major order
+- No padding between rows
+:::
+
+## What does `sameColor(...)` check?
+
+It compares **all four RGBA channels** for equality:
+
+- Red
+- Green
+- Blue
+- Alpha
+
+Two pixels are considered connected **only if all channels match exactly**.
+
+This is important for correctness when alpha is meaningful (e.g. transparency).
+
+## Why does the algorithm do two passes?
+
+The function is split into **two distinct phases**:
+
+1. **Labeling pass**
+ - Flood-fills each region
+ - Assigns a label to every pixel
+ - Computes region metadata (area + bounding box)
+
+2. **Merge pass**
+ - Iterates pixels again
+ - Replaces pixels belonging to “small” regions
+ - Copies color from a neighboring “big enough” region
+
+This separation keeps the logic simpler and avoids modifying regions while still discovering them.
+
+## What makes a region “big enough”?
+
+A region must satisfy **all three** conditions:
+
+- `size >= minArea`
+- `width() >= minWidth`
+- `height() >= minHeight`
+
+Where:
+- `size` = number of pixels
+- `width()` = bounding box width
+- `height()` = bounding box height
+
+This prevents:
+- Thin lines
+- Long but narrow artifacts
+- Small blobs
+
+## Why use a bounding box instead of checking shape quality?
+
+Bounding boxes are:
+- Fast to compute
+- Memory cheap
+- Conservative
+
+They don’t capture holes or concavities, but they are a good heuristic for filtering obvious noise.
+
+There is a TODO noting that **internal gaps** could reduce effective width/height even if the bounding box looks valid.
+
+## Can a region pass the bounding box test but still be “bad”?
+
+Yes.
+
+Example:
+- A hollow ring
+- A U-shaped region
+- A region with internal gaps
+
+The bounding box may be large, but the actual filled area might be sparse.
+
+This implementation prioritizes speed and simplicity over perfect geometric validity.
+
+## Why are regions merged pixel-by-pixel instead of as a whole?
+
+Because:
+- The function operates **in-place**
+- It avoids reallocating buffers
+- It keeps memory usage predictable
+
+Each pixel independently:
+- Checks its neighbors
+- Copies the color of a valid region
+
+This makes the merge phase linear and simple.
+
+## Why only check immediate neighbors during merging?
+
+Only **4 immediate neighbors** are checked because:
+- The merge should respect spatial adjacency
+- Copying from distant pixels could create visual artifacts
+
+This ensures merges look natural and locally consistent.
+
+## What happens if a small region has no big neighbors?
+
+Nothing.
+
+Pixels in that region remain unchanged.
+
+This avoids:
+- Arbitrary color assignment
+- Unexpected long-range merges
+
+If this is undesirable, a second pass or fallback strategy can be added.
+
+## Does this update region metadata after merging?
+
+No.
+
+Once the merge phase starts:
+- `regions` is treated as read-only
+- Labels may change per pixel
+- Region sizes are **not recomputed**
+
+This is intentional to keep runtime linear and avoid cascading changes.
+
+## What is the time complexity?
+
+Overall complexity is **linear**:
+
+- Flood-fill labeling: $O(n)$
+- Merge pass: $O(n)$
+
+Where $$ n = \text{width} \times \text{height} $$
+
+Each pixel is:
+- Visited once in flood-fill
+- Checked against at most 4 neighbors
+
+## What is the memory overhead?
+
+Additional memory used:
+- `labels`: one `int` per pixel
+- `regions`: one entry per connected component
+- BFS queue (temporary)
+
+No additional image buffers are allocated.
+
+## Why use BFS (`std::queue`) instead of DFS?
+
+BFS:
+- Avoids deep recursion
+- Prevents stack overflow on large regions
+- Has predictable memory usage
+
+DFS would require either recursion (unsafe) or an explicit stack (no advantage here).
+
+## Can this function be parallelized?
+
+Not easily in its current form.
+
+Reasons:
+- Flood-fill has data dependencies
+- Label assignment is sequential
+- Merge step mutates shared data
+
+Parallel versions would require:
+- Tiled processing
+- Boundary reconciliation
+- More complex region merging logic
+
+## Is this suitable for SVG generation pipelines?
+
+Yes — especially as a **cleanup step** before:
+- Boundary tracing
+- Polygon extraction
+- Path simplification
+
+Removing small regions early greatly simplifies vector geometry later.
+
+## What are common improvements or extensions?
+
+Possible enhancements include:
+- Detecting holes inside regions
+
+
+
+- Merging based on color similarity instead of equality
+
+
+
+- Multi-pass merging
+- Choosing the *largest neighboring region* instead of the first valid one
+- Detecting localised width/height - regions often have large tentacle-like protrusions.
+
+The current design favors **clarity and predictability** over heuristics.
+
+## Is this function deterministic?
+
+Yes.
+
+Given the same input image and parameters, the output is always identical.
+
+No randomness is involved.
+
+## When should I *not* use this?
+
+Avoid this function if:
+- You need exact topology preservation
+- You rely on diagonal connectivity
+- You require sub-pixel or fuzzy color matching
+
+It is designed for **grid-aligned, exact-color regions**.
+
+# Summary
+
+`mergeSmallRegionsInPlace` is a fast, predictable cleanup step for raster segmentation pipelines.
+It trades geometric perfection for simplicity, performance, and ease of reasoning — which is often exactly what you want before vectorization.
diff --git a/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/limitations-and-pitfalls.md b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/limitations-and-pitfalls.md
new file mode 100644
index 000000000..45e7e2dcd
--- /dev/null
+++ b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/limitations-and-pitfalls.md
@@ -0,0 +1,40 @@
+---
+id: limitations-and-pitfalls
+title: Limitations & Pitfalls
+sidebar_label: Limitations & Pitfalls
+sidebar_position: 7
+description: Limitations and pitfalls commonly encountered when using the mergeSmallRegionsInPlace function.
+---
+
+## Exact RGBA equality
+The algorithm treats colors as equal only when all 4 bytes match exactly.
+If you need fuzzy color merging (e.g. colors within a Euclidean distance in RGB), you must replace `sameColor` with a colour-distance test.
+:::note Extending functionality
+This could be extended in the future by allowing callers to pass a custom equality-checker function.
+:::
+
+## Only 4-connectivity
+Components connected only diagonally will be considered separate. If your semantics require 8-connectivity, adapt the neighbour set.
+
+## Local merge heuristic
+A small region is colored using the first adjacent large neighbour encountered.
+If a small island touches multiple large regions, the chosen one depends on neighbour scan order
+(right, left, down, up in the reference code). This can occasionally lead to confusion as it is not an `intelligent check`.
+
+## Holes / concavities
+The bounding-box test may be fooled by shapes with large bounding boxes but containing many holes.
+**The TODO in the source code remains valid**: you could compute convex-hull, morphological closing,
+or compute the ratio `size / (width*height)` (occupancy) to detect sparse shapes.
+```cpp title="The TODO comment"
+// TODO: check for gaps inside regions - its possible their dimensions are fine,
+// but inner gaps reduce effective width and height
+```
+
+## Order sensitivity
+Since pixels are recolored in place and have their `labels` updated when a merge is done,
+subsequent small pixels that were adjacent to that pixel may now see a different neighbour label;
+this actually helps the merge flood (small pixels adjacent to a merged pixel can be recoloured to the same large region),
+but it means the behaviour is implementation-order dependent.
+
+## Performance
+Allocation of `std::vector` and `std::queue` can be optimized for very large images.
diff --git a/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/overview.md b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/overview.md
new file mode 100644
index 000000000..2f6842a8b
--- /dev/null
+++ b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/overview.md
@@ -0,0 +1,67 @@
+---
+id: overview
+title: mergeSmallRegionsInPlace — Overview
+sidebar_label: Overview
+sidebar_position: 2
+---
+
+This page documents the algorithm, the math and topology behind the mergeSmallRegionsInPlace function,
+the provided implementation, usage examples, complexity, limitations, and suggested improvements.
+
+## Why this exists
+
+Small spurious pixel clusters (`"speckles"` or thin "arms") are common after colour quantization, k-means-based color clustering,
+thresholding, or other segmentation steps. They make vectorization (e.g. converting raster -> SVG)
+and any downstream shape-based processing noisy.
+This routine cleans those small clusters by merging them into neighboring, larger regions so the image looks cleaner and shapes are more blob-like.
+
+Think of it as a domain-specific `despeckle` that uses connected-component analysis and a simple adjacency-based merge rule.
+
+The merge rule is important as it avoids leaving gaps in the image (a problem faced by several other similar libraries).
+
+## When to use it
+
+- With an RGBA image stored in a tightly-packed `uint8_t*` pixels buffer (4 bytes per pixel, row-major).
+- To remove very small connected components while preserving large components.
+- You are OK with replacing a small-region pixel by the color of an *adjacent* large region.
+
+:::important
+Not suitable when you need to preserve small but semantically important details (e.g. text strokes),
+or when color matching should be fuzzy (this implementation checks exact RGBA equality).
+:::
+
+## Function signature & API
+
+```cpp
+void mergeSmallRegionsInPlace(
+ uint8_t *pixels, // pointer to RGBA buffer, length = width * height * 4
+ int width, // image's width
+ int height, // image's height
+ int minArea, // minimum pixel count for a region to be "big enough"
+ int minWidth, // minimum bounding-box width for a region to be "big enough"
+ int minHeight // minimum bounding-box height for a region to be "big enough"
+);
+```
+
+### Parameters
+
+- `pixels` — row-major RGBA buffer with 4 bytes per pixel:
+
+$$
+\text{pixels} = [ \overbrace{255}^{R},\overbrace{255}^{G},\overbrace{255}^{B},\overbrace{255}^{A}, \overbrace{0}^{R},\overbrace{0}^{G},\overbrace{0}^{B},\overbrace{255}^{A}, \cdots ]
+$$
+
+- `width`, `height` — image dimensions in pixels.
+- Evaluation Criteria:
+ - `minArea` — threshold on the number of pixels in a connected component; components with `size < minArea` are considered too small.
+ - `minWidth`, `minHeight` — thresholds on the component's bounding-box width/height. A component is considered big enough when **all three** conditions pass (area and both bbox dimensions). This helps avoid long thin components being considered large just by area.
+
+### Preconditions
+
+* `pixels != nullptr` and `width>0` and `height>0`.
+* The buffer length must be at least `width * height * 4` bytes.
+
+### Postconditions
+
+* The `pixels` buffer may be modified in-place: small regions will have their pixels recolored to match an adjacent large region (if found).
+* The function uses an internal label map and region metadata; it does not return labels.
diff --git a/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/testing-and-debugging-suggestions.md b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/testing-and-debugging-suggestions.md
new file mode 100644
index 000000000..19b6b8550
--- /dev/null
+++ b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/testing-and-debugging-suggestions.md
@@ -0,0 +1,23 @@
+---
+id: testing-and-debugging-suggestions
+title: Testing & Debugging Suggestions
+sidebar_label: Testing & Debugging Suggestions
+sidebar_position: 9
+description: Testing and debugging suggestions for the mergeSmallRegionsInPlace function.
+---
+
+## Synthetic test images
+Create synthetic test images that exercise corner cases:
+- Single pixel islands
+- Long 1-pixel-wide arms (test minWidth/minHeight)
+- Two large regions separated by thin small islands
+- Diagonal-touching shapes (to test 4 vs 8 connectivity)
+
+## Visualization of labels map
+Visualize the `labels` map (map labels to colors) to ensure connected components are being formed as expected.
+
+## Unit tests
+Assert that the number of pixels of a known large blob is unchanged; assert that small isolated pixels have been recolored.
+
+## Instrumentation
+Collect histogram of region sizes to choose appropriate `minArea`.
diff --git a/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/variants-and-improvements.md b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/variants-and-improvements.md
new file mode 100644
index 000000000..be81f8985
--- /dev/null
+++ b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/variants-and-improvements.md
@@ -0,0 +1,58 @@
+---
+id: variants-and-improvements
+title: Variants & Improvements
+sidebar_label: Variants & Improvements
+sidebar_position: 8
+description: Several ways mergeSmallRegionsInPlace could be improved or customized.
+---
+
+This section explains several ways to improve or customize the behavior of `mergeSmallRegionsInPlace`.
+
+:::tip Limitations & Pitfalls
+Also see the previous section, [Limitations & Pitfalls](../limitations-and-pitfalls), for a better understanding of the below.
+:::
+
+## 8-connectivity
+
+Change the neighbor offsets from 4 to 8 directions (include diagonals). This connects diagonally touching pixels.
+
+## Two-pass connected-component
+
+> A.K.A. scanline or union-find labeling
+
+A two-pass algorithm is usually faster (lower memory footprint and better cache locality).
+It scans rows, assigns provisional labels and records equivalences, then resolves equivalences and computes final region stats in the second pass.
+
+## Use colour distance instead of exact equality
+
+```cpp title="Replace sameColor with something like the below"
+inline bool similarColor(const uint8_t *img, int w, int h, int x1, int y1, int x2, int y2, int tol) {
+ int i1 = idx(x1,y1,w)*4; int i2 = idx(x2,y2,w)*4;
+ int dr = int(img[i1]) - img[i2];
+ int dg = int(img[i1+1]) - img[i2+1];
+ int db = int(img[i1+2]) - img[i2+2];
+ return (dr*dr + dg*dg + db*db) <= tol*tol;
+}
+```
+
+This treats colors within `tol` distance as identical.
+
+## Merge-by-nearest-large-region (instead of neighbour sampling)
+
+For each small region, find the nearest big region (e.g. by computing region adjacency graph or distance transform)
+and recolor the whole small region at once. This reduces order dependence and makes merging decisions global.
+
+## Use morphological operations to fill small gaps
+
+Applying a morphological closing (dilate then erode) before connected-component labeling can fill thin gaps and reduce speckles without per-component heuristics.
+
+## Occupancy / solidity test
+
+Compute $solidity = \frac{size}{width \times height}$
+
+If `solidity` is low (i.e., bounding box large but region sparse), treat as small/noisy and merge.
+
+## Parallel labeling
+
+Use segmented image tiling with boundary stitching for multicore scaling or specialized GPU approaches.
+
diff --git a/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/walkthrough-and-usage.md b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/walkthrough-and-usage.md
new file mode 100644
index 000000000..9a423ef10
--- /dev/null
+++ b/docs/docs/reference/wasm/modules/image/mergeSmallRegionsInPlace/walkthrough-and-usage.md
@@ -0,0 +1,104 @@
+---
+id: walkthrough-and-usage
+title: Walkthrough & Usage
+sidebar_label: Walkthrough & Usage
+sidebar_position: 4
+description: Code walkthrough of mergeSmallRegionsInPlace and how it can be used in real code.
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Line-by-line code walk-through
+
+:::tip
+See the source code ([mergeSmallRegionsInPlace.h](https://github.com/Ryan-Millard/Img2Num/blob/main/src/wasm/modules/image/include/mergeSmallRegionsInPlace.h) and
+[mergeSmallRegionsInPlace.cpp](https://github.com/Ryan-Millard/Img2Num/blob/main/src/wasm/modules/image/src/mergeSmallRegionsInPlace.cpp))
+for the full source listing. Here we explain the important parts.
+:::
+
+- `struct Pixel { int x,y; };` — small POD for BFS queue.
+- `inline int idx(int x, int y, int width)` — maps (x,y) to a linear index into `labels` (not into pixel bytes; bytes index uses `*4`).
+This makes it simpler to index 2D data in a 1D array.
+- `sameColor(...)` — compares 4 bytes (in RGBA form) at two pixel coordinates for exact equality.
+ :::caution It uses *exact* equality
+ Compression or anti-aliased edges will produce many colors that are visually similar but not equal.
+ :::
+- `struct Region` — collects `size`, `minX`, `maxX`, `minY`, `maxY`, provides convenience `width()`, `height()`, and `isBigEnough(...)`.
+- **Flood-fill labeling loop** — For every unlabeled pixel, perform a BFS:
+
+
+
+ 1. Push initial pixel
+ 2. Mark its `labels[...] = nextLabel`
+ 3. Add pixel to region: `r.add(x,y)`
+ 4. While queue non-empty
+ - Pop and consider 4 neighbours
+ - If their label is `-1` and `sameColor(...)` holds
+ - label them and push to queue
+ 5. Now done with nth region:
+ - Add region to list of regions: `regions.push_back(r)`
+ - Increment `nextLabel`
+
+
+
+ ```mermaid
+ flowchart TD
+ A[Start: For every pixel] --> B{Is pixel unlabeled?}
+ B -- No --> A
+ B -- Yes --> C[Push initial pixel to queue]
+ C --> D[Mark labels = nextLabel]
+ D --> E[Add pixel to region r]
+ E --> F{Queue not empty?}
+ F -- No --> G[Region done: add r to regions list]
+ G --> H[Increment nextLabel]
+ H --> A
+ F -- Yes --> I[Pop pixel from queue]
+ I --> J[Check 4 neighbours]
+ J --> K{Neighbour unlabeled AND sameColor?}
+ K -- No --> F
+ K -- Yes --> L[Label neighbour = nextLabel]
+ L --> M[Push neighbour to queue]
+ M --> F
+
+ ```
+
+
+
+- **Merge phase**: iterate every pixel; if its region is too small (`isBigEnough(...) == false`),
+check the 4 immediate neighbours; if any neighbour is in a different label `nl` and `regions[nl].isBigEnough(...)`
+is true, copy the neighbour's color bytes into the small pixel and set its label to `nl`.
+ :::note
+ The merge phase uses the `labels` array to pick neighbour region IDs and the `regions` metadata to determine which regions are "big".
+ :::
+
+## Example usage
+
+```cpp title="Load an image with stb_image, run merge, write out with stb_image_write"
+#include "mergeSmallRegionsInPlace.h"
+#include
+#include
+
+int main() {
+ int w,h,comp;
+ unsigned char *img = stbi_load("segmented.png", &w, &h, &comp, 4);
+ if (!img) return 1;
+
+ // Remove tiny islands smaller than 50px and require bbox at least 3x3
+ mergeSmallRegionsInPlace(img, w, h, 50, 3, 3);
+
+ stbi_write_png("cleaned.png", w, h, 4, img, w*4);
+ stbi_image_free(img);
+ return 0;
+}
+```
+:::tip
+Tweak the thresholds to match your use-case.
+:::
+