MATLAB Image Processing Toolbox to Python: Complete scikit-image Guide
Convert MATLAB Image Processing Toolbox functions to Python. imresize, imfilter, edge detection, morphology — mapped to scikit-image and OpenCV with working code.
The core substitution: scikit-image, OpenCV, and Pillow
MATLAB's Image Processing Toolbox maps to three Python libraries depending on what you're doing:
| MATLAB Toolbox | Python equivalent | When to use it | |---|---|---| | Image Processing Toolbox | scikit-image | Filtering, morphology, segmentation, feature detection | | Image Processing Toolbox | OpenCV (cv2) | Real-time processing, video, camera pipelines | | Basic image I/O | Pillow (PIL) | Reading/writing common formats, basic transforms | | Any array math on images | NumPy | Pixel math, masking, slicing |
For most scientific and research code that used MATLAB's Image Processing Toolbox, scikit-image is the closest drop-in. Install both:
`bash
pip install scikit-image opencv-python pillow numpy
`
The imports you'll use most:
`python
import numpy as np
from skimage import io, filters, morphology, feature, measure, transform
from skimage import color, exposure, segmentation
import cv2 # when you need OpenCV specifically
`
Reading, writing, and displaying images
MATLAB's imread / imshow / imwrite map directly:
`matlab
% MATLAB
img = imread('photo.png');
imshow(img);
imwrite(img, 'output.png');
`
`python
# Python — scikit-image
from skimage import io
import matplotlib.pyplot as plt
img = io.imread('photo.png') # returns numpy array, shape (H, W, C)
plt.imshow(img)
plt.axis('off')
plt.show()
io.imsave('output.png', img)
`
Key difference: MATLAB images are 1-indexed matrices; NumPy arrays are 0-indexed. A pixel at row 5, column 10 in MATLAB is img(5, 10); in Python it's img[4, 9].
Data types matter: MATLAB imread returns uint8 (0–255). scikit-image functions typically expect float images in [0, 1]. Use skimage.img_as_float to convert:
`python
from skimage import img_as_float, img_as_ubyte
img_float = img_as_float(img) # uint8 → float64 in [0.0, 1.0]
img_uint8 = img_as_ubyte(img_float) # float64 → uint8
`
Resizing, rotating, and geometric transforms
`matlab
% MATLAB
resized = imresize(img, 0.5); % scale by 0.5
resized2 = imresize(img, [256 256]); % resize to 256x256
rotated = imrotate(img, 45); % rotate 45 degrees
flipped = fliplr(img); % horizontal flip
`
`python
# Python
from skimage.transform import resize, rotate
import numpy as np
resized = resize(img, (img.shape[0]//2, img.shape[1]//2),
anti_aliasing=True)
resized2 = resize(img, (256, 256), anti_aliasing=True)
rotated = rotate(img, 45, resize=False) # resize=True to avoid clipping
flipped = np.fliplr(img) # horizontal flip — plain NumPy
`
Note: skimage.transform.resize returns a float64 array in [0, 1] by default. Use preserve_range=True to keep the original dtype and range.
For affine transforms, use skimage.transform.AffineTransform + warp, or cv2.warpAffine if you prefer OpenCV's interface.
Filtering: Gaussian, median, and custom kernels
Spatial filtering is where most image processing code spends its time:
`matlab
% MATLAB
blurred = imgaussfilt(img, 2); % Gaussian, sigma=2
med = medfilt2(img, [5 5]); % 5x5 median filter
sharpened = imsharpen(img);
custom = imfilter(img, fspecial('sobel'));
`
`python
# Python
from skimage import filters
from scipy.ndimage import median_filter, convolve
blurred = filters.gaussian(img, sigma=2) med = median_filter(img, size=5) # scipy for 3D-safe median
# Sharpening: unsharp mask sharpened = filters.unsharp_mask(img, radius=1, amount=1.0)
# Custom kernel: Sobel from skimage.filters import sobel edges = sobel(img) # already built-in
# Or apply any custom kernel with scipy:
import numpy as np
from scipy.ndimage import convolve
kernel = np.array([[-1,-1,-1],[-1,8,-1],[-1,-1,-1]])
filtered = convolve(img.astype(float), kernel)
`
Important: For color images, apply filters to each channel or convert to grayscale first:
`python
from skimage.color import rgb2gray
gray = rgb2gray(img) # returns float64 in [0, 1]
edges = filters.sobel(gray)
`
Edge detection and feature extraction
`matlab
% MATLAB
edges_canny = edge(gray_img, 'Canny');
edges_sobel = edge(gray_img, 'Sobel');
edges_prewitt = edge(gray_img, 'Prewitt');
corners = detectHarrisFeatures(gray_img);
`
`python
# Python
from skimage import feature, filters
from skimage.color import rgb2gray
gray = rgb2gray(img)
# Canny edges_canny = feature.canny(gray, sigma=1.0, low_threshold=0.1, high_threshold=0.2)
# Sobel (magnitude) edges_sobel = filters.sobel(gray)
# Prewitt edges_prewitt = filters.prewitt(gray)
# Harris corners
harris = feature.corner_harris(gray)
coords = feature.corner_peaks(harris, min_distance=5)
# coords[:, 0] are row indices, coords[:, 1] are column indices
`
For feature descriptors (SIFT, ORB), use OpenCV:
`python
import cv2
sift = cv2.SIFT_create()
kp, desc = sift.detectAndCompute(cv2.cvtColor(img, cv2.COLOR_RGB2GRAY), None)
`
Morphological operations
MATLAB's imerode, imdilate, imopen, imclose all have direct equivalents:
`matlab
% MATLAB
se = strel('disk', 5);
eroded = imerode(binary_img, se);
dilated = imdilate(binary_img, se);
opened = imopen(binary_img, se);
closed = imclose(binary_img, se);
filled = imfill(binary_img, 'holes');
`
`python
# Python
from skimage import morphology
from scipy.ndimage import binary_fill_holes
import numpy as np
# Create structuring element disk = morphology.disk(5) # equivalent to strel('disk', 5) square = morphology.square(5) # strel('square', 5)
eroded = morphology.binary_erosion(binary_img, disk)
dilated = morphology.binary_dilation(binary_img, disk)
opened = morphology.binary_opening(binary_img, disk)
closed = morphology.binary_closing(binary_img, disk)
filled = binary_fill_holes(binary_img) # scipy
`
For grayscale morphology, use morphology.erosion and morphology.dilation (without the binary_ prefix).
Region properties and connected components
MATLAB's bwconncomp, regionprops, and labelmatrix have a clean Python equivalent:
`matlab
% MATLAB
cc = bwconncomp(binary_img);
stats = regionprops(cc, 'Area', 'Centroid', 'BoundingBox');
areas = [stats.Area];
centroids = cat(1, stats.Centroid);
`
`python
# Python
from skimage import measure
labeled = measure.label(binary_img) # connected-component labeling regions = measure.regionprops(labeled)
areas = [r.area for r in regions] centroids = [r.centroid for r in regions] # (row, col) in 0-based coords bboxes = [r.bbox for r in regions] # (min_row, min_col, max_row, max_col)
# Filter by area
large = [r for r in regions if r.area > 100]
`
Every RegionProperties object exposes: area, centroid, bbox, eccentricity, perimeter, major_axis_length, minor_axis_length, and more. Check skimage.measure.regionprops docs for the full list.
Colour space conversions
`matlab
% MATLAB
gray = rgb2gray(img);
hsv_img = rgb2hsv(img);
lab_img = rgb2lab(img);
`
`python
# Python
from skimage import color
gray = color.rgb2gray(img) # float64 in [0, 1]
hsv_img = color.rgb2hsv(img)
lab_img = color.rgb2lab(img) # L in [0,100], a/b in [-128,127]
ycbcr = color.rgb2ycbcr(img)
`
Watch out for channel order: OpenCV uses BGR; scikit-image and MATLAB use RGB. When mixing the two:
`python
import cv2
img_bgr = cv2.imread('photo.png') # OpenCV reads as BGR
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # convert for skimage
`
Convert your image processing code now
Most MATLAB Image Processing Toolbox code translates to scikit-image with minimal structural changes — the functions map almost one-to-one. The main friction points are:
1. 0-based vs 1-based indexing — pixel (r, c) in MATLAB is img[r-1, c-1] in Python
2. Float vs uint8 — scikit-image functions prefer float [0, 1]; convert explicitly with img_as_float
3. Channel order — RGB everywhere (never BGR unless you're using OpenCV directly)
4. Structuring elements — strel('disk', r) → morphology.disk(r)
Paste your MATLAB image processing code into the [free converter at mtopython.com/convert](/convert) to get Python output in under a second. The converter maps imread, imresize, imfilter, imerode, imdilate, and all common toolbox functions automatically.
Free for 50 lines. No account required.
More like this, once a week
New articles on MATLAB-to-Python migration. Short, practical, no fluff — the same tone as the one you just read.