np.uint8 is NumPy’s unsigned 8-bit integer data type: every value uses one byte and must be a whole number from 0 to 255. That makes it the standard type for image pixels and other compact data. Create a uint8 array with np.array(values, dtype=np.uint8) or convert one with arr.astype(np.uint8), but watch out: values outside 0–255 wrap around instead of raising an error. This guide covers the uint8 range, creating and converting arrays, overflow in arithmetic (and how to avoid it), floats and NaN, images, and a table of all NumPy integer and float data types.
All examples were run with Python 3.12.5 and NumPy 2.5.3 in the Windows Command Prompt. Reference: Data types and numpy.iinfo in the NumPy documentation.
What is np.uint8?
“u” means unsigned (no negative numbers), and “int8” means an 8-bit integer. Eight bits give 28 = 256 possible values, so a uint8 holds 0 to 255. np.iinfo() reports the exact limits:
import numpy as np
pixels = np.array([0, 128, 255], dtype=np.uint8)
print(pixels, pixels.dtype)
print("bytes per value:", pixels.itemsize)
info = np.iinfo(np.uint8)
print("range:", info.min, "to", info.max)
Output:
[ 0 128 255] uint8
bytes per value: 1
range: 0 to 255
Create a uint8 array
Pass dtype=np.uint8 (or the string "uint8") to any array-creation function:
import numpy as np
a = np.array([10, 20, 30], dtype=np.uint8) # from a list
b = np.zeros((2, 3), dtype=np.uint8) # all zeros
c = np.full(4, 255, dtype="uint8") # the string name works too
d = np.arange(0, 256, 64, dtype=np.uint8)
for name, arr in [("a", a), ("b", b), ("c", c), ("d", d)]:
print(name, arr.dtype, arr.tolist())
Output:
a uint8 [10, 20, 30]
b uint8 [[0, 0, 0], [0, 0, 0]]
c uint8 [255, 255, 255, 255]
d uint8 [0, 64, 128, 192]
Convert an array to uint8 with astype()
astype(np.uint8) converts an existing array, and this is where most bugs start. Decimals are truncated, and integers outside 0–255 wrap around modulo 256 without any error. Only a Python integer that does not fit raises OverflowError:
import numpy as np
floats = np.array([0.0, 1.9, 127.5, 254.7])
print(floats.astype(np.uint8)) # decimals are cut off, not rounded
ints = np.array([100, 255, 256, 300, -1])
print(ints.astype(np.uint8)) # out of range: wraps around modulo 256
try:
np.array([256], dtype=np.uint8) # a Python int out of range is an error
except OverflowError as e:
print("OverflowError:", e)
safe = np.clip(np.round(np.array([-20.4, 99.6, 300.2])), 0, 255).astype(np.uint8)
print(safe) # round + clip first: the safe way
Output:
[ 0 1 127 254]
[100 255 0 44 255]
OverflowError: Python integer 256 out of bounds for uint8
[ 0 100 255]
round + clip pattern.For values that may be outside the range, always use np.clip(np.round(x), 0, 255).astype(np.uint8). Converting floats that are out of range (like 300.0 or -1.0) directly is undefined behavior in NumPy, so the result can differ between computers.
uint8 overflow: why 250 + 10 = 4
Arithmetic on two uint8 arrays stays uint8, so results wrap around too. A common trap is taking the absolute difference of two images: the subtraction wraps before np.abs() ever sees a negative number. Convert to a wider type first:
import numpy as np
a = np.array([250, 10], dtype=np.uint8)
b = np.array([10, 20], dtype=np.uint8)
print("a + b :", a + b) # 260 wraps to 4
print("a - b :", a - b) # 10 - 20 wraps to 246
print("np.abs(a - b) :", np.abs(a - b)) # abs does not help: the wrap already happened
wide = a.astype(np.int16) - b.astype(np.int16)
print("int16 math :", wide)
print("abs difference:", np.abs(wide).astype(np.uint8))
img = np.array([200, 240], dtype=np.uint8)
print("brighten +60 :", img + 60, "(wrapped)")
print("clipped :", np.clip(img.astype(np.int16) + 60, 0, 255).astype(np.uint8))
Output:
a + b : [ 4 30]
a - b : [240 246]
np.abs(a - b) : [240 246]
int16 math : [240 -10]
abs difference: [240 10]
brighten +60 : [ 4 44] (wrapped)
clipped : [255 255]
np.uint8 scalars
A single np.uint8 value behaves like the arrays: math with a Python int stays uint8 (with an overflow warning), and a float promotes the result to float64. Since NumPy 2, a Python int that does not fit in uint8 at all raises OverflowError. A scalar is also not iterable, which is where the “‘numpy.uint8’ object is not iterable” error comes from:
import numpy as np
import warnings
x = np.uint8(200)
print(repr(x), type(x).__name__)
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
print(x + 100) # stays uint8: 300 wraps to 44
print("warning:", caught[0].message)
try:
x + 300 # 300 does not fit in uint8 at all
except OverflowError as e:
print("OverflowError:", e)
print(type(x + 1.5).__name__, x + 1.5) # a float promotes to float64
print(int(x) + 100) # convert to a Python int for normal math
try:
for value in np.uint8(5):
pass
except TypeError as e:
print("TypeError:", e) # a scalar is not an array
Output:
np.uint8(200) uint8
44
warning: overflow encountered in scalar add
OverflowError: Python integer 300 out of bounds for uint8
float64 201.5
300
TypeError: 'numpy.uint8' object is not iterable
Convert floats to uint8 (0–1 and 0–255 data)
Image data is often stored as floats from 0.0 to 1.0. Multiply by 255 and round before converting, or 0.5 becomes 127 instead of 128. NaN has no integer value: NumPy warns and the result is undefined (it happened to be 0 on this computer, but you cannot rely on that), so replace NaNs yourself first with np.nan_to_num():
import numpy as np
import warnings
gray = np.array([0.0, 0.25, 0.5, 1.0]) # image data scaled 0..1
print((gray * 255).astype(np.uint8)) # 0.5 * 255 = 127.5 -> 127
print(np.round(gray * 255).astype(np.uint8)) # rounded: 128
with_nan = np.array([0.4, np.nan])
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
print((with_nan * 255).astype(np.uint8)) # NaN has no integer value
print("warning:", caught[0].message)
print(np.nan_to_num(with_nan * 255).astype(np.uint8)) # replace NaN with 0 first
Output:
[ 0 63 127 255]
[ 0 64 128 255]
[102 0]
warning: invalid value encountered in cast
[102 0]
Why uint8 is used for images
An RGB image is an array of shape (height, width, 3) with one uint8 per color channel. Using uint8 instead of float64 needs 8 times less memory:
import numpy as np
shape = (1080, 1920, 3) # one Full HD RGB image
for dtype in (np.uint8, np.int32, np.float32, np.float64):
arr = np.zeros(shape, dtype=dtype)
print(f"{np.dtype(dtype).name:<8} {arr.nbytes / 1024**2:8.1f} MB")
Output:
uint8 5.9 MB
int32 23.7 MB
float32 23.7 MB
float64 47.5 MB
Matplotlib, Pillow and OpenCV all expect uint8 images with values 0–255. This example builds an image directly from a uint8 array:
import numpy as np
import matplotlib.pyplot as plt
h, w = 200, 256
img = np.zeros((h, w, 3), dtype=np.uint8)
img[:, :, 0] = np.arange(w, dtype=np.uint8) # red grows from left to right
img[:, :, 2] = 255 - np.arange(w, dtype=np.uint8) # blue fades out
img[80:120, 100:156] = [255, 255, 255] # a white box
print(img.dtype, img.shape, img.min(), img.max())
plt.imshow(img) # uint8 RGB is shown as 0..255 directly
plt.title("A uint8 RGB image: values 0 to 255")
plt.axis("off")
plt.show()
Output:
uint8 (200, 256, 3) 0 255
plt.imshow().To save such an array as a picture, see save a NumPy array as a PNG with Matplotlib.
NumPy data types: uint8 compared with the others
NumPy has signed (int) and unsigned (uint) integers in 8, 16, 32 and 64 bits, plus 16-, 32- and 64-bit floats. The table below was printed by NumPy itself:
import numpy as np
print(f"{'dtype':<9}{'bytes':>6} {'min':>27} {'max':>27}")
for t in (np.int8, np.uint8, np.int16, np.uint16, np.int32, np.uint32, np.int64, np.uint64):
i = np.iinfo(t)
print(f"{np.dtype(t).name:<9}{np.dtype(t).itemsize:>6} {i.min:>27,} {i.max:>27,}")
for t in (np.float16, np.float32, np.float64):
f = np.finfo(t)
print(f"{np.dtype(t).name:<9}{np.dtype(t).itemsize:>6} {'max ~' + format(float(f.max), '.3g'):>27} precision {f.precision} digits")
print(np.array([1, 2]).dtype, np.array([1.0]).dtype, np.array([True]).dtype) # defaults
Output:
dtype bytes min max
int8 1 -128 127
uint8 1 0 255
int16 2 -32,768 32,767
uint16 2 0 65,535
int32 4 -2,147,483,648 2,147,483,647
uint32 4 0 4,294,967,295
int64 8 -9,223,372,036,854,775,808 9,223,372,036,854,775,807
uint64 8 0 18,446,744,073,709,551,615
float16 2 max ~6.55e+04 precision 3 digits
float32 4 max ~3.4e+38 precision 6 digits
float64 8 max ~1.8e+308 precision 15 digits
int64 float64 bool
np.iinfo and float limits from np.finfo.| Type | Range | Typical use |
|---|---|---|
np.uint8 | 0 to 255 | Images, bytes, small counters |
np.int8 | -128 to 127 | Small signed values |
np.int16 / np.uint16 | -32,768 to 32,767 / 0 to 65,535 | Audio samples, intermediate image math |
np.int64 | about ±9.2 × 1018 | Default integer type |
np.float32 | up to about 3.4 × 1038, 6 reliable digits | Machine learning, GPUs |
np.float64 | up to about 1.8 × 10308, 15 reliable digits | Default float type |
If you do not pass a dtype, NumPy picks int64 for whole numbers and float64 for decimals (on Windows too since NumPy 2).
Continue with these NumPy tutorials:
- Create arrays of zeros with np.zeros()
- Read a binary file into a byte array
- Save a NumPy array as a PNG image
- Calculate the average with NumPy
Frequently asked questions
What is np.uint8 in Python?
NumPy’s unsigned 8-bit integer type. Each value takes one byte and must be between 0 and 255.
What is the range of uint8?
0 to 255. np.iinfo(np.uint8) returns min=0 and max=255.
How do I convert a NumPy array to uint8?
arr.astype(np.uint8). If the values may be outside 0–255 or have decimals, use np.clip(np.round(arr), 0, 255).astype(np.uint8).
Why does uint8 subtraction give large numbers like 246?
uint8 cannot be negative, so 10 – 20 wraps around to 246. Convert to np.int16 before subtracting.
What is the difference between uint8 and int8?
uint8 stores 0 to 255; int8 stores -128 to 127. Both use one byte.
Why do I get ‘numpy.uint8’ object is not iterable?
You are looping over a single value instead of an array. Wrap it with np.atleast_1d() or check where the scalar came from.
Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn