How to open an image in Python

Learn how to open an image in Python. This guide covers various methods, tips, real-world applications, and how to debug common errors.

How to open an image in Python
Published on: 
Tue
Mar 3, 2026
Updated on: 
Wed
Apr 1, 2026
The Replit Team

Python makes it easy to open images for tasks like data visualization, machine learning, and web development. Its libraries offer powerful yet simple tools to handle image files efficiently.

In this article, you'll explore several techniques to open and display images. You'll find practical tips, real-world applications, and advice to debug common issues you might encounter.

Using the Image module from PIL/Pillow

from PIL import Image

img = Image.open('sample.jpg')
img.show()
print(f"Image format: {img.format}, Size: {img.size}, Mode: {img.mode}")--OUTPUT--Image format: JPEG, Size: (800, 600), Mode: RGB

The Pillow library’s Image.open() function is the standard for opening image files. It’s efficient because it doesn’t load the entire image into memory immediately. Instead, it creates an object that holds key metadata. For a quick preview, the show() method opens the image using your system's default viewer, which is great for debugging.

You can also inspect useful properties directly from the image object:

  • format: Identifies the file type, such as JPEG or PNG.
  • size: Returns the image’s dimensions in pixels as a tuple.
  • mode: Specifies the color mode, like RGB for color images.

Basic image opening techniques

Beyond Pillow, you'll find other libraries are often better suited for specific tasks, particularly in computer vision and scientific imaging.

Opening images with cv2.imread() in OpenCV

import cv2

img = cv2.imread('sample.jpg')
cv2.imshow('Image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()--OUTPUT--# No text output, displays image in a window

OpenCV is a powerhouse for computer vision, and its cv2.imread() function is your entry point. It loads an image directly into a NumPy array, making it immediately ready for numerical processing—a key difference from Pillow's approach.

Displaying the image involves a few steps:

  • cv2.imshow() opens a new window to show the image.
  • cv2.waitKey(0) is essential. It pauses execution and waits for any key press.
  • cv2.destroyAllWindows() closes the window cleanly afterward.

Displaying images with Matplotlib

import matplotlib.pyplot as plt
import matplotlib.image as mpimg

img = mpimg.imread('sample.jpg')
plt.imshow(img)
plt.axis('off')
plt.show()--OUTPUT--# No text output, displays image in a matplotlib window

Matplotlib is a go-to for data visualization, and it handles images just as easily. The mpimg.imread() function reads your image into an array, which is exactly what Matplotlib's plotting functions expect. You then use plt.imshow() to render the image data within a plot figure.

  • To keep the focus on the image, plt.axis('off') hides the default plot axes for a cleaner look.
  • Finally, plt.show() opens a window to display the result.

Using io.imread() from scikit-image

from skimage import io

img = io.imread('sample.jpg')
print(f"Image shape: {img.shape}")
print(f"Data type: {img.dtype}")--OUTPUT--Image shape: (600, 800, 3)
Data type: uint8

Scikit-image is another powerful library for scientific image analysis. Much like OpenCV and Matplotlib, its io.imread() function reads an image directly into a NumPy array, making it ready for algorithmic work.

  • The shape property reveals the image's dimensions as a tuple of (height, width, channels).
  • dtype shows the data type of the array's elements—in this case, uint8, which represents pixel values from 0 to 255.

Advanced image handling techniques

Beyond opening local files one by one, you'll often need to handle images from URLs, convert them for analysis, or process them in batches.

Opening images from URLs

import requests
from PIL import Image
from io import BytesIO

response = requests.get('https://example.com/sample.jpg')
img = Image.open(BytesIO(response.content))
print(f"Image size: {img.size}")--OUTPUT--Image size: (800, 600)

You can open images directly from the web without saving them to a file first. This process uses the requests library to download the image data as raw bytes. Since Pillow's Image.open() can't directly handle raw bytes, you need a way to bridge the gap.

  • The io.BytesIO class creates an in-memory binary stream from the downloaded content.
  • This stream acts like a temporary file, allowing Image.open() to read the image data directly from memory.

Converting images to numpy arrays

import numpy as np
from PIL import Image

img = Image.open('sample.jpg')
img_array = np.array(img)
print(f"Array shape: {img_array.shape}")
print(f"Value range: {img_array.min()} to {img_array.max()}")--OUTPUT--Array shape: (600, 800, 3)
Value range: 0 to 255

For many image processing tasks, you need to work with pixel data as a numerical grid. While libraries like OpenCV load images as arrays by default, a Pillow Image object requires a simple conversion. Just pass the image object to np.array() to get a NumPy array ready for mathematical operations.

This array gives you direct access to the raw pixel data:

  • The shape property reveals the dimensions as (height, width, channels).
  • The value range, often 0 to 255, represents the pixel intensities.

Processing multiple images with glob

import glob
from PIL import Image

image_files = glob.glob('images/*.jpg')
for file in image_files[:3]: # First 3 images
img = Image.open(file)
width, height = img.size
print(f"{file}: {width}x{height}")--OUTPUT--images/img1.jpg: 800x600
images/img2.jpg: 1024x768
images/img3.jpg: 640x480

The glob module is perfect for batch processing. Instead of listing every file by hand, you can use glob.glob() to find all pathnames that match a specific pattern. It’s an efficient way to gather all your image files into a single list.

  • The pattern 'images/*.jpg' finds all files ending with .jpg in the images directory.
  • You can then loop through the resulting list to open and process each image with Pillow.

Move faster with Replit

Replit is an AI-powered development platform that comes with all Python dependencies pre-installed, so you can skip setup and start coding instantly. This lets you move from learning individual techniques to building complete applications faster. Describe what you want to build, and Agent 4 handles everything from writing the code to connecting APIs and deploying it live.

Instead of just opening images, you can describe a complete tool you want to build:

  • An image resizer that processes an entire folder of JPEGs, standardizing them to a specific width for a web gallery.
  • A utility that fetches an image from a URL, converts it to a NumPy array, and applies a filter before saving it.
  • A metadata extractor that scans a directory of images and generates a CSV report with each file’s name, dimensions, and color mode.

Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.

Common errors and challenges

When opening images in Python, you might encounter a few common issues, but they're usually straightforward to fix.

  • Fixing FileNotFoundError when opening images
  • Handling color channel differences between cv2 and PIL
  • Troubleshooting MemoryError with large images

Fixing FileNotFoundError when opening images

The FileNotFoundError is a classic stumbling block. It means Python can't locate the image file at the path you provided, usually because of a simple typo or because your script is running from a different directory. Here's what it looks like:

from PIL import Image

img = Image.open('sample.jpg')
img.show()
# FileNotFoundError: [Errno 2] No such file or directory: 'sample.jpg'

This error happens because the script is looking for sample.jpg in the same directory it's running from. If the file is located elsewhere, Python won't find it. The code below shows a reliable way to fix this.

from PIL import Image
import os

# Construct proper path to the image
current_dir = os.path.dirname(os.path.abspath(__file__))
img_path = os.path.join(current_dir, 'sample.jpg')
img = Image.open(img_path)
img.show()

To solve this, you can construct an absolute path that's independent of your current working directory. The os.path.abspath(__file__) function gets the full path to your script, and os.path.dirname() extracts its directory. By combining this with the filename using os.path.join(), you create a reliable path to sample.jpg. This approach makes your code more portable, as it will always find the image relative to the script's location, no matter where you run it from.

Handling color channel differences between cv2 and PIL

A frequent pitfall when combining cv2 and Pillow is their conflicting color channel order. OpenCV reads images in BGR (Blue, Green, Red) format, while Pillow expects RGB. This mismatch results in swapped colors, as the following code demonstrates.

import cv2
from PIL import Image

# OpenCV loads in BGR order
cv2_img = cv2.imread('sample.jpg')
# Convert directly to PIL (colors will look wrong)
pil_img = Image.fromarray(cv2_img)
pil_img.save('wrong_colors.jpg')

The Image.fromarray() function doesn't automatically account for the BGR format, so it misinterprets the color data. This results in an image with swapped red and blue channels. The code below shows the correct conversion.

import cv2
from PIL import Image

# OpenCV loads in BGR order
cv2_img = cv2.imread('sample.jpg')
# Convert BGR to RGB before creating PIL image
rgb_img = cv2.cvtColor(cv2_img, cv2.COLOR_BGR2RGB)
pil_img = Image.fromarray(rgb_img)
pil_img.save('correct_colors.jpg')

The fix is to explicitly convert the color channels before passing the image to Pillow. Use the cv2.cvtColor() function with the cv2.COLOR_BGR2RGB flag to switch the image array from OpenCV's BGR format to RGB. Once converted, Image.fromarray() can correctly interpret the data, preserving the original colors. Keep this in mind anytime you're moving image data between OpenCV and other libraries like Pillow or Matplotlib.

Troubleshooting MemoryError with large images

A MemoryError can stop your script cold when you're working with high-resolution photos or panoramic shots. This happens because the image data is too large to fit into your computer's available memory, especially during operations like format conversion.

The following code demonstrates how a seemingly simple operation like convert('RGB') can trigger this issue when applied to a massive image file.

from PIL import Image

# Trying to process a very large image
large_img = Image.open('huge_panorama.jpg')
# Converting directly to RGB can cause memory issues
rgb_img = large_img.convert('RGB')
rgb_img.save('processed.jpg')

The convert('RGB') method creates a full copy of the image in memory. With a huge file, this duplication exhausts available RAM and triggers the error. The following code demonstrates a more memory-efficient way to manage this.

from PIL import Image

# Process large image with size control
large_img = Image.open('huge_panorama.jpg')
# Resize before converting to reduce memory usage
resized_img = large_img.resize((large_img.width//2, large_img.height//2))
rgb_img = resized_img.convert('RGB')
rgb_img.save('processed.jpg')

To avoid a MemoryError, resize the image before performing memory-intensive operations like convert('RGB'). The resize() method creates a smaller copy, significantly reducing the amount of data that needs to be processed. This simple step prevents your script from running out of RAM when handling high-resolution photos or large image batches. It's a proactive way to manage resources, especially in environments with limited memory.

Real-world applications

Beyond simply opening files, these techniques unlock practical applications for everything from copyright protection to creative design analysis.

Creating a text watermark for copyright protection with PIL

With Pillow’s ImageDraw module, you can programmatically write text directly onto an image, which is perfect for adding a copyright notice.

from PIL import Image, ImageDraw, ImageFont

img = Image.open('photo.jpg')
draw = ImageDraw.Draw(img)
font = ImageFont.truetype('arial.ttf', 36)
draw.text((20, 20), "Copyright 2023", fill=(255, 255, 255), font=font)
img.save('watermarked.jpg')
print("Watermark added successfully")

This script overlays text onto an image using Pillow's drawing tools. It starts by creating a drawing context on the opened image with ImageDraw.Draw(), which prepares the image for modifications like adding shapes or text.

  • The ImageFont.truetype() function loads a specific font file and size for your text.
  • draw.text() then writes your string at the specified (x, y) coordinates, using the loaded font and a fill color.

Finally, img.save() commits your changes to a new file, leaving the original untouched.

Analyzing color composition for graphic design

You can quickly quantify a design’s overall color palette by calculating the average red, green, and blue values across all its pixels.

By converting the image into a NumPy array with np.array(), you transform its visual data into a numerical grid. This array is structured in three dimensions: height, width, and color channels. This structure makes it simple to isolate all the red pixel values using slicing, like img_array[:,:,0], and then compute their average with np.mean().

Repeating this for the green and blue channels gives you a high-level summary of the image’s color composition. These average values reveal the dominant tint, which is useful for analyzing brand consistency or understanding the overall mood of a design.

from PIL import Image
import numpy as np

img = Image.open('design_sample.jpg')
img_array = np.array(img)
red_avg = np.mean(img_array[:,:,0])
green_avg = np.mean(img_array[:,:,1])
blue_avg = np.mean(img_array[:,:,2])
print(f"Average RGB values: R={red_avg:.1f}, G={green_avg:.1f}, B={blue_avg:.1f}")

This code efficiently distills an image down to its core color profile. It uses Pillow for what it does best—opening image files—and then passes the data to NumPy for high-speed numerical analysis.

  • NumPy's array structure lets you access entire color channels at once.
  • The np.mean() function then quickly averages the pixel values for red, green, and blue.

The result is a simple but powerful summary of the image's overall color balance, printed in a clean, formatted string.

Get started with Replit

Turn these techniques into a working application. Describe what you want to build to Replit Agent, like “a tool that watermarks all JPEGs in a folder” or “an app that calculates an image’s average color.”

The Agent then writes the necessary code, debugs it, and deploys the final app, handling the entire workflow for you. Start building with Replit.

Build your first app today

Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.

Get started for free

Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.