AUTOMATIC1111/stable-diffusion-webui · error · ValueError

{tensor.shape} does not describe a BCHW tensor

Error message

{tensor.shape} does not describe a BCHW tensor

What it means

upscaler_utils.torch_bgr_to_pil_image() accepts a CHW tensor or a 4D tensor only when the batch dimension is exactly 1 (which it squeezes out). A 4D tensor with shape[0] != 1 cannot be unambiguously converted to a single PIL image, so it is rejected with this ValueError before the numpy conversion.

Source

Thrown at modules/upscaler_utils.py:27

from modules import devices, images, shared, torch_utils

logger = logging.getLogger(__name__)


def pil_image_to_torch_bgr(img: Image.Image) -> torch.Tensor:
    img = np.array(img.convert("RGB"))
    img = img[:, :, ::-1]  # flip RGB to BGR
    img = np.transpose(img, (2, 0, 1))  # HWC to CHW
    img = np.ascontiguousarray(img) / 255  # Rescale to [0, 1]
    return torch.from_numpy(img)


def torch_bgr_to_pil_image(tensor: torch.Tensor) -> Image.Image:
    if tensor.ndim == 4:
        # If we're given a tensor with a batch dimension, squeeze it out
        # (but only if it's a batch of size 1).
        if tensor.shape[0] != 1:
            raise ValueError(f"{tensor.shape} does not describe a BCHW tensor")
        tensor = tensor.squeeze(0)
    assert tensor.ndim == 3, f"{tensor.shape} does not describe a CHW tensor"
    # TODO: is `tensor.float().cpu()...numpy()` the most efficient idiom?
    arr = tensor.float().cpu().clamp_(0, 1).numpy()  # clamp
    arr = 255.0 * np.moveaxis(arr, 0, 2)  # CHW to HWC, rescale
    arr = arr.round().astype(np.uint8)
    arr = arr[:, :, ::-1]  # flip BGR to RGB
    return Image.fromarray(arr, "RGB")


def upscale_pil_patch(model, img: Image.Image) -> Image.Image:
    """
    Upscale a given PIL image using the given model.
    """
    param = torch_utils.get_param(model)

    with torch.inference_mode():
        tensor = pil_image_to_torch_bgr(img).unsqueeze(0)  # add batch dimension

View on GitHub (pinned to 82a973c043)

Solutions

  1. Iterate the batch and convert each image: for img in tensor: pil = torch_bgr_to_pil_image(img)
  2. Or select/slice one image first: torch_bgr_to_pil_image(tensor[i])
  3. If you authored the tensor, build it as CHW/B1HW from the start via pil_image_to_bgr_image

Example fix

# before
img = torch_bgr_to_pil_image(batched_bchw)  # batch=4 -> ValueError
# after
images = [torch_bgr_to_pil_image(t) for t in batched_bchw]
Defensive patterns

Strategy: type-guard

Validate before calling

import torch
def to_bchw_of_one(t: torch.Tensor) -> torch.Tensor:
    if t.ndim == 3:
        return t.unsqueeze(0)
    if t.ndim == 4 and t.shape[0] == 1:
        return t
    raise ValueError(f'{t.shape} is not CHW or B1HW')

Type guard

def is_single_image_tensor(t: torch.Tensor) -> bool:
    return t.ndim == 3 or (t.ndim == 4 and t.shape[0] == 1)

Prevention

When it happens

Trigger: Passing a Bx3xHxW BGR tensor with batch size 2+ into an upscaler path (e.g. custom upscale code or an extension calling upscale_with_model helpers that round-trip through torch_bgr_to_pil_image), instead of looping over the batch.

Common situations: Extensions batch-processing multiple images through single-image upscaler utilities; code migrated from img2img batching where tensors naturally carry a real batch dimension.

Related errors


AI-assisted analysis of AUTOMATIC1111/stable-diffusion-webui@82a973c043 (2026-08-14). Data as JSON: /api/errors/a6a1a31c82e4ca1e. Report an issue: GitHub.