Нарезки (Slicing) в Python списках и Numpy

Нарезка - это расширение синтаксиса индексации с использованием квадратных скобок. Она широко используется для доступа к диапазонам (интервалам) элементов. Простой пример, есть одномерный список:

>>> a = [0123456789]
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Нужно выделить из него список со средними элементами. Это можно сделать так:

>>> i1 = len(a)//4
>>> i2 = len(a)*3//4
>>> print(i1,i2)

2 7

>>> a[i1:i2]

[2, 3, 4, 5, 6]

Синтаксис для создания нарезки:

[начало:конец:шаг]

Если шаг равен единице, то можно использовать только начало и конец, как в предыдущем примере. Указав шаг равный 2, можно получить список из кратных 2-ке элементов:

>>> a[0:10:2]

[0, 2, 4, 6, 8]

При этом можно не указывать начало и конец, тогда они по умолчанию примут размер начала и конца списка:

>>> a[::2]

[0, 2, 4, 6, 8]

Размер шага можно указывать равным -1, тогда получим копию оригинального списка в обратном порядке:

>>> a[::-1]

[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

Нарезку можно использовать и для удаления всех элементов списка, вызвав del a[:], но нагляднее все-таки использовать метод clear()

Аналогичный подход работает и для многомерных списков. Рассмотрим на примере массивов Numpy. Создадим пустой двухмерный массив Numpy (считай изображение):

import numpy as np
import matplotlib.pyplot as plt
image = np.zeros((200,200))

С помощью нарезки создадим прямоугольник:

image[50:150,100:150] +=1
plt.imshow(image)
plt.show()
2023-01-06_16-01-57

Из этого изображения с помощью нарезки мы можем создать более мелкое изображение:

image = image[50:150,50:150]
2023-01-06_16-05-52

Снова очистим изображение и нарисуем на нем решетку:

image[::50,:] +=1
image[:,::50] +=1
2023-01-06_16-08-37

Посмотрим, а можно ли такую же вещи провернуть с контурами. Для этого подключим OpenCV и создадим изображение Uint8.

image = np.zeros((200,200), np.uint8)
image[50:150,100:150] +=255
 
import cv2
# Изображение изначально бинаризированное, поэтому не нужно бинаризировать
contours, hierarchy = cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
image2 = np.zeros((200,200))
cv2.drawContours(image2,contours,0,255,1)

plt.imshow(image2)
plt.show()
2023-01-06_16-20-05

На изображении контур. Он всего один, поэтому выберем его и посмотрим содержимое:

>>> contour = contours[0]
>>> contour
array([[[100, 50]], [[100, 149]], [[149, 149]], [[149, 50]]], dtype=int32)

Здесь видны 4 точки контура. Оставим только три с помощью нарезки:

>>> contour = contour[0:3]
>>> contour

array([[[100, 50]], [[100, 149]], [[149, 149]]], dtype=int32)

Ну и снова выведем на экран:

image2 = np.zeros((200,200))
cv2.drawContours(image2,[contour],0,255,1)

plt.imshow(image2)
plt.show()
2023-01-06_16-23-03

Как видим, все работает и на контурах. 

Выводы:
  • Нарезки очень удобны способ манипулирования с данными, который применяется в списках и массивах различного типа;
  • небольшой недостаток - для части разработчиков Python это может привести к затруднению понимания кода и соответственно более сложную поддержку.