Convert Normals
Translate a normal map between BAE, MiDaS and Standard
- normals
- optional_fill
- IMAGE
Normal maps encode surface direction as color, but different tools don't agree on exactly how - which axis maps to which RGB channel, which way is "up." That means a normal map generated by one estimator can read as subtly (or completely) wrong when handed to a tool expecting a different convention. Convert Normals exists purely to translate between them, and it's the natural companion to Depth To Normals from this same pack, which outputs in whichever of these conventions you pick at generation time.
Why the conventions differ, and why it matters
Standard, BAE, and MiDaS are three different color-space conventions for the same underlying information. BAE is worth knowing specifically because it's the convention the standard ControlNet normal-map preprocessor uses - if you're feeding a normal map into ControlNet's normal conditioning, BAE is generally the convention it expects. Get the convention wrong and you don't get an error, you get a plausible-looking but wrong result: surfaces that appear to face the wrong direction, relighting that looks subtly off, ControlNet conditioning that guides generation toward the wrong geometry without any obvious failure to point at.
Inputs and outputs that matter
normals- your source normal map.input_mode/output_mode-BAE,MiDaS, orStandard. Setinput_modeto whatever convention your normal map was actually generated in, andoutput_modeto whatever convention the tool downstream expects.scale_XY(0-100, default 1) - scales the X/Y (surface-tilt) component of the normal independently from Z. Useful for exaggerating or flattening surface detail without touching how flat regions are represented.normalize(boolean, default true) - re-normalizes each normal vector to unit length after conversion, which keeps the math correct; there's rarely a reason to turn this off.fix_black(boolean, default true) - patches invalid/black regions - undefined normals from sky or unestimated areas - that would otherwise decode as a flat, wrong-direction normal instead of just being absent.optional_fill(optionalIMAGE) - supply your own fallback image for the regionsfix_blackcatches, instead of accepting whatever default fill the node uses.
Output: a single IMAGE.
Installing it
ComfyUI Manager, search ComfyUI Image Filters, or:
cd ComfyUI/custom_nodes
git clone https://github.com/spacepxl/ComfyUI-Image-Filters
Restart afterward. No model weights - it's a color-space remap, not an estimator. The pack's recurring opencv version-conflict issue applies here as much as anywhere else in the pack; the repo's import_error_install.bat handles it, or manually keep only opencv-contrib-python after removing other opencv variants.
Where people get burned
Guessing input_mode instead of knowing it is the most common mistake - if you don't know which convention your normal map came from (a lot of estimators don't say clearly), guessing wrong produces a result that looks almost right, which is worse than an obvious failure because it's easy to miss. If you generated the map with Depth To Normals from this same pack, you already know its output_mode - use that exact value as input_mode here.
The other one: leaving fix_black off when your source normal map has genuine undefined regions, like sky in an outdoor depth map. Those regions decode as a flat normal facing a specific fixed direction rather than "no data," which can visibly distort relighting or ControlNet conditioning in exactly the areas that should have been ignored.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| normals | IMAGE | — | |
| input_mode | COMBO | 3 options: BAE, MiDaS, Standard | |
| output_mode | COMBO | 3 options: BAE, MiDaS, Standard | |
| scale_XY | FLOAT | 1.0000–100 | — |
| normalize | BOOLEAN | true | — |
| fix_black | BOOLEAN | true | — |
| optional_fillopt | IMAGE | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |