NumPy’s np.uint8 in Python

Recently, I was working on an image processing project where memory optimization became critical. That’s when I discovered the power of NumPy’s uint8 data type. The issue was, I needed to handle thousands of pixel values efficiently without consuming excessive memory.

In this article, I’ll cover everything you need to know about np.uint8 in Python, from the basics to practical applications. So let’s dive in!

np.uint8 in Python

np.uint8 is an 8-bit unsigned integer data type in NumPy. It can store integer values from 0 to 255, which makes it perfect for applications like image processing where pixel values typically fall within this range.

When working with large datasets, using the right data type can significantly impact performance and memory usage.

Read NumPy Unique Function in Python

Why Use np.uint8?

Now, I will explain to you why to use np.uint8.

1. Memory Efficiency

One of the main advantages of using np.uint8 is its memory efficiency. Let’s compare it with other data types:

import numpy as np

# Creating arrays with different data types
uint8_array = np.zeros(1000000, dtype=np.uint8)
int32_array = np.zeros(1000000, dtype=np.int32)
float64_array = np.zeros(1000000, dtype=np.float64)

# Comparing memory usage
print(f"Memory usage of uint8 array: {uint8_array.nbytes / 1024:.2f} KB")
print(f"Memory usage of int32 array: {int32_array.nbytes / 1024:.2f} KB")
print(f"Memory usage of float64 array: {float64_array.nbytes / 1024:.2f} KB")

Output:

Memory usage of uint8 array: 976.56 KB
Memory usage of int32 array: 3906.25 KB
Memory usage of float64 array: 7812.50 KB

You can see the output in the screenshot below.

uint8 python

As you can see, uint8 uses significantly less memory than int32 or float64.

Check out Create a 2D NumPy Array in Python

2. Perfect for Image Processing

In image processing, each pixel color channel (RGB) typically ranges from 0 to 255, making uint8 the ideal data type. Here’s a simple example using a popular US landmark:

import numpy as np
import matplotlib.pyplot as plt
from skimage import data

# Create a sample image (let's pretend this is the Statue of Liberty)
sample_image = np.random.randint(0, 256, size=(200, 300, 3), dtype=np.uint8)

# Display the image
plt.imshow(sample_image)
plt.title("Random Image Using uint8")
plt.show()

# Check the data type and shape
print(f"Image data type: {sample_image.dtype}")
print(f"Image shape: {sample_image.shape}")

Create Python Arrays with np.uint8

There are several ways to create NumPy arrays with the uint8 data type in Python:

Read NumPy Normalize 0 and 1 in Python

Method 1: Use np.array() with the dtype Parameter

This method allows you to directly specify the data type when creating a NumPy array.
It’s useful when you want precise control over memory or compatibility with specific data types like uint8.

import numpy as np

# Creating a uint8 array from a list
data = [0, 127, 255]
uint8_array = np.array(data, dtype=np.uint8)
print(uint8_array)
print(f"Data type: {uint8_array.dtype}")

Output:

[  0 127 255]
Data type: uint8

You can see the output in the screenshot below.

python uint8

Using dtype inside np.array() ensures that the resulting array has the exact type you need

Check out ValueError: setting an array element with a sequence error in Python

Method 2: Use NumPy Array Creation Functions

Functions like np.zeros(), np.ones(), and np.random.randint() make it easy to create arrays with preset values.

import numpy as np

# Using zeros
zeros_array = np.zeros(5, dtype=np.uint8)

# Using ones
ones_array = np.ones(5, dtype=np.uint8)

# Using random integers
random_array = np.random.randint(0, 256, size=5, dtype=np.uint8)

print("Zeros array:", zeros_array)
print("Ones array:", ones_array)
print("Random array:", random_array)

You can see the output in the screenshot below.

Zeros array: [0 0 0 0 0]
Ones array: [1 1 1 1 1]
Random array: [154  22 165 218 101]
np.uint8

These built-in functions are ideal for initializing larger or patterned arrays with consistent data types.

Read NumPy Average Filter in Python

Method 3: Convert Existing Arrays

You can convert an existing array to uint8 using the .astype() method.
This is helpful when you start with another type (like float) and need to ensure type consistency.

import numpy as np

# Create a float array
float_array = np.array([0.9, 127.1, 255.9])

# Convert to uint8
uint8_array = float_array.astype(np.uint8)

print("Original float array:", float_array)
print("Converted uint8 array:", uint8_array)

Output:

Original float array: [  0.9 127.1 255.9]
Converted uint8 array: [  0 127 255]

Type conversion is a flexible way to transform data without recreating arrays.

Work with np.uint8: Considerations and Limitations

Now I will explain how to work with np.uint8 in NumPy, including important considerations and common limitations to watch out for.

Value Range Limitation

Since uint8 can only store values from 0 to 255, you need to be careful with operations that might result in values outside this range:

import numpy as np

# Create a uint8 array
uint8_array = np.array([250, 255], dtype=np.uint8)

# Add 10 to each element
result = uint8_array + 10
print("After adding 10:", result)
print(f"Data type after addition: {result.dtype}")

Output:

After adding 10: [4 9]
Data type after addition: uint8

Notice how 250 + 10 = 260, but since 260 > 255, it wraps around to 4 (260 – 256 = 4).

Overflow and Underflow

Overflow and underflow occur when operations result in values outside the valid range:

import numpy as np

# Create uint8 arrays
a = np.array([200], dtype=np.uint8)
b = np.array([100], dtype=np.uint8)

# Addition (overflow)
print(f"200 + 100 = {(a + b)[0]} (Overflow)")

# Subtraction (underflow)
c = np.array([10], dtype=np.uint8)
d = np.array([20], dtype=np.uint8)
print(f"10 - 20 = {(c - d)[0]} (Underflow)")

Output:

200 + 100 = 44 (Overflow)
10 - 20 = 246 (Underflow)

To avoid these issues, you can temporarily cast to a larger data type:

import numpy as np

# Create uint8 arrays
a = np.array([200], dtype=np.uint8)
b = np.array([100], dtype=np.uint8)

# Cast to int32 for the operation, then back to uint8 if needed
result = a.astype(np.int32) + b.astype(np.int32)
print(f"200 + 100 = {result[0]} (No overflow)")

# Clip values to valid range before converting back to uint8
clipped_result = np.clip(result, 0, 255).astype(np.uint8)
print(f"Clipped result: {clipped_result[0]}")

Real-World Applications of np.uint8

Let me show you some real-world examples that will help you understand np.uint8.

Read np.abs() in Python Numpy

Image Processing

The most common use of uint8 is in image processing. Here’s a simple example to adjust the brightness of an image:

import numpy as np
import matplotlib.pyplot as plt
from skimage import data

# Load a sample image
image = data.camera()  # Grayscale image of cameraman

# Convert to uint8 if not already
if image.dtype != np.uint8:
    image = image.astype(np.uint8)

# Increase brightness (add 50 to each pixel)
# We need to be careful with overflow
brighter_image = np.clip(image.astype(np.int16) + 50, 0, 255).astype(np.uint8)

# Display the images
fig, ax = plt.subplots(1, 2, figsize=(10, 5))
ax[0].imshow(image, cmap='gray')
ax[0].set_title('Original Image')
ax[1].imshow(brighter_image, cmap='gray')
ax[1].set_title('Brightened Image')
plt.show()

Data Compression

When working with large datasets, using uint8 can significantly reduce memory usage:

import numpy as np
import time

# Generate a large dataset
size = 10000000

# Using float64 (default)
start_time = time.time()
float_array = np.random.random(size)
float_time = time.time() - start_time
float_memory = float_array.nbytes / (1024 * 1024)  # MB

# Using uint8
start_time = time.time()
uint8_array = np.random.randint(0, 256, size=size, dtype=np.uint8)
uint8_time = time.time() - start_time
uint8_memory = uint8_array.nbytes / (1024 * 1024)  # MB

print(f"Float64 array: {float_memory:.2f} MB, Creation time: {float_time:.4f} seconds")
print(f"Uint8 array: {uint8_memory:.2f} MB, Creation time: {uint8_time:.4f} seconds")
print(f"Memory savings: {float_memory/uint8_memory:.1f}x")

Output:

Float64 array: 76.29 MB, Creation time: 0.1234 seconds
Uint8 array: 9.54 MB, Creation time: 0.0876 seconds
Memory savings: 8.0x

Tips for Working with np.uint8

  1. Always Check Data Range: Before converting to uint8, ensure your data falls within the 0-255 range or clip/scale it appropriately.
  2. Be Aware of Overflow/Underflow: When performing arithmetic operations, consider casting to a larger data type temporarily.
  3. Use np.clip(): This function is useful for ensuring values stay within the valid range:
   np.clip(array, 0, 255).astype(np.uint8)
  1. Use uint8 for Pixel Values: In image processing, uint8 is almost always the right choice for storing pixel values.
  2. Consider Other Data Types: If you need a wider range, consider using uint16 (0-65535) or other appropriate types based on your specific requirements.

I hope you found this guide on np.uint8 in Python helpful. Using appropriate data types like uint8 can significantly improve your application’s performance and memory efficiency, especially when working with large datasets like images or sensor readings.

You may like to read:

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.