The error means you’re calling .shape on a Pillow image; convert to a NumPy array or use Pillow’s size/width/height instead.
Pillow’s Image object doesn’t expose .shape. That attribute belongs to NumPy arrays and to images loaded by OpenCV. With PNG files you’ll often hit this when mixing Pillow and NumPy or switching between OpenCV and Pillow APIs in the same script. The fix is simple: either convert the image to a NumPy array before asking for .shape, or use Pillow’s size, width, and height properties.
Why This AttributeError Pops Up
Quick check: If you opened the file with PIL.Image.open(...), you have a Pillow Image, not a NumPy array. Pillow uses size and the convenience fields width/height, while .shape lives on NumPy arrays. The Pillow docs list these fields on the Image class and do not mention .shape.
- Pillow image:
img.size→(width, height); alsoimg.width,img.height. - NumPy/OpenCV image:
arr.shape→(rows, cols, channels). OpenCV returns a NumPy array fromcv2.imread.
One more gotcha: Pillow reports size as (W, H)shape as (H, W, C)
AttributeError: 'PngImageFile' Object Has No Attribute 'Shape' — Safe Patterns That Work
Pick the pattern that matches your stack. Each one avoids the trap by using the right attribute on the right object.
Use Pillow Only (No NumPy Needed)
from PIL import Image
img = Image.open("image.png")
w, h = img.size # or: w, h = img.width, img.height
print(w, h)
Why it works: size, width, and height are the supported ways to read dimensions on a Pillow Image.
Convert Pillow Image To NumPy Before Using .shape
from PIL import Image
import numpy as np
img = Image.open("image.png")
arr = np.array(img) # or: np.asarray(img)
print(arr.shape) # (H, W, C) for RGB/RGBA
Why it works: np.array(img) turns the Pillow image into a NumPy array, which exposes .shape.
Load With OpenCV When You Want Arrays From The Start
import cv2
arr = cv2.imread("image.png", cv2.IMREAD_UNCHANGED) # arr is a NumPy array
print(arr.shape) # (H, W, C)
Why it works: cv2.imread returns a NumPy array, so .shape is valid immediately.
PNG Quirks You Should Handle Up Front
PNG files often carry transparency. That means you might get RGBA rather than RGB. If you’re moving data between libraries, match channel layout first.
- Remove alpha in Pillow:
img = img.convert("RGB")before conversion to an array. - Beware of background after drop: removing alpha will flatten to a background color; plan your pipeline accordingly.
from PIL import Image
import numpy as np
img = Image.open("logo.png").convert("RGB") # drop alpha
arr = np.array(img) # safe .shape next
print(arr.shape)
Common Mix-Ups And Exact Fixes
Use these quick swaps to kill the error without rewiring your project.
- Reading size on a Pillow image? Replace
img.shapewithimg.sizeor(img.width, img.height). - Running NumPy code after
Image.open? Insertarr = np.array(img)before any.shapecalls. - Working in OpenCV land? Load with
cv2.imreadso you start with a NumPy array. - Seeing swapped width/height? Remember: Pillow uses
(W, H); arrays use(H, W, C). Adjust the order when converting. - Alpha channel tripping code? Standardize to RGB with
.convert("RGB")beforenp.arrayif your pipeline expects 3 channels.
Reference Table: Symptoms To Fix In One Glance
| Symptom | Likely Cause | Direct Fix |
|---|---|---|
AttributeError on .shape after Image.open |
Pillow Image lacks .shape |
w,h = img.size or arr = np.array(img) |
shape shows three values; code expects two |
Color image array includes channels | h,w = arr.shape[:2] |
| Width/height seem flipped | Confusing (W,H) vs (H,W) |
Switch order when moving between APIs |
| Downstream step fails on RGBA | Alpha channel present in PNG | img = img.convert("RGB") or handle 4 channels |
| Want NumPy ops from the start | Loaded with Pillow by habit | Load with cv2.imread; get array directly |
Dimension Reads That Never Break
These patterns keep width/height logic tidy across stacks and prevent the attributeerror: 'pngimagefile' object has no attribute 'shape' from resurfacing later.
Pillow-Only Dimension Reads
from PIL import Image
img = Image.open("frame.png")
w, h = img.size # or: img.width, img.height
print(f"{w=} {h=}")
Unified Helper When Mixing Pillow And NumPy
from typing import Tuple
from PIL import Image
import numpy as np
def get_wh(x) -> Tuple[int, int]:
# Pillow Image
if isinstance(x, Image.Image):
return x.width, x.height
# NumPy-like (OpenCV, etc.)
if hasattr(x, "shape"):
h, w = x.shape[:2]
return w, h
raise TypeError("Unsupported image type")
# Usage
img = Image.open("scene.png")
print(get_wh(img)) # Pillow branch
arr = np.array(img)
print(get_wh(arr)) # NumPy branch
Why it works: The helper inspects the object and extracts dimensions with the correct field for each library. OpenCV tutorials reinforce that image data is a NumPy array with .shape.
Alpha, Modes, And Channel Counts
PNG transparency leads to mode RGBA. Some models, augmentation code, or encoders expect RGB. Standardize early to keep pipelines stable.
- Drop alpha in Pillow:
img = img.convert("RGB"). - Working with arrays: after
np.array(img), checkarr.shapeand slice channels or convert with Pillow first.
from PIL import Image
import numpy as np
img = Image.open("badge.png")
if img.mode == "RGBA":
img = img.convert("RGB")
arr = np.array(img) # safe: now shape[-1] == 3
End-To-End Examples You Can Paste In
Case A — Keep Pillow, No NumPy
from PIL import Image
img = Image.open("tile.png")
w, h = img.size
print("W×H:", w, h)
# downstream Pillow ops here
Case B — Convert To Array, Stay In NumPy/OpenCV Style
from PIL import Image
import numpy as np
img = Image.open("tile.png").convert("RGB")
arr = np.array(img)
h, w = arr.shape[:2]
print("H×W×C:", arr.shape)
# downstream NumPy ops here
Case C — Start In OpenCV
import cv2
arr = cv2.imread("tile.png", cv2.IMREAD_COLOR) # BGR array
h, w = arr.shape[:2]
print("H×W×C:", arr.shape)
OpenCV’s tutorials and guides make clear that arrays carry the pixel data and expose .shape.
Pitfall Roundup So You Don’t See The Error Again
- Pick one dimension API per object type: Pillow →
size; NumPy/OpenCV →shape. - Normalize channel layout early: convert RGBA to RGB if downstream code expects three channels.
- Keep width/height order straight: Pillow reports
(W, H), arrays store(H, W).
FAQ-Free Cheatsheet For Dimension And Loading
- Read size with Pillow only:
w, h = Image.open(path).size. - Switch to array for math:
arr = np.array(Image.open(path)), thenarr.shape. - OpenCV path:
arr = cv2.imread(path), thenarr.shape. - Handle alpha:
img.convert("RGB")when needed.
Use these patterns and the attributeerror: 'pngimagefile' object has no attribute 'shape' disappears for good while your dimension math stays correct across Pillow, NumPy, and OpenCV.
