Судя по количеству закладок на первой части, работа моя — не зряшная.
В прошлый раз разбирали скучное открывание-закрывание картинки, в этот раз засунем в неё руки поглубже:
Доступ к пикселям и работа с ними.
Масштабирование картинки.
Обрезка.
Отражение.
import cv2
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
Первой картинкой пойдут уже знакомые шашечки:
Доступ к отдельным пикселям
Изображение в OpenCV — матрица numpy, а значит, для доступа к пикселю будем использовать нотацию матриц: [r, c]. Первое значение — строка, второе — колонка. Не забывайте о том, что индексация начинается с нуля!
Для доступа к самому первому пикселю обратимся к элементу матрицы (изображения, то бишь) с индексами 0 и 0:
# Читаем картинку как чб
cb_img = cv2.imread("checkerboard_18x18.png",0)
# Выводим массив, представляющий картинку
print(cb_img)
# Выводим значение самого первого пикселя (верх-лево)
print(cb_img[0,0])
# Выводим значение первого пикселя слева от чёрной зоны
print(cb_img[0,6])
С доступом разобрались, поиграем со значениями.
Изменение пикселей
Просто переназначаем пиксель:
cb_img[0,0] = 255
Что-то похожее мы сделали в прошлый раз с каналами.
Продублируем картинку, и накидаем в неё серых пикселей:
cb_img_copy = cb_img.copy() # копируем загруженное изображение
cb_img_copy[2,2] = 200 # пиксель в ячейке [2,2] будет равен 200
cb_img_copy[2,3] = 200 # и так далее
cb_img_copy[3,2] = 200
cb_img_copy[3,3] = 200
# То же самое другими словами:
# cb_img_copy[2:3,2:3] = 200
plt.imshow(cb_img_copy, cmap='gray')
plt.show()
print(cb_img_copy)
Обрезка картинок
Можно воспринимать кроп как задачу "из пачки пикселей берём только эти несколько".
Загрузим новозеландскую лодку и потренируемся на ней:
img_NZ_bgr = cv2.imread("New_Zealand_Boat.jpg",cv2.IMREAD_COLOR)
# или так:
# img_NZ_bgr = cv2.imread("New_Zealand_Boat.jpg",1)
# img_NZ_bgr = cv2.imread("New_Zealand_Boat.jpg")
img_NZ_rgb = img_NZ_bgr[:,:,::-1]
# или так:
# img_NZ_rgb = cv2.cvtColor(img_NZ_bgr, cv2.COLOR_BGR2RGB)
# или разбить на каналы и пересобрать в правильном порядке :)
plt.imshow(img_NZ_rgb)
plt.show()
Вырежем серединку:
# кропнутый регион = область загруженной картинки
# c 200 по 400 строку (или Y, если хотите)
# и 300 по 600 колонку (или X, если хотите)
cropped_region = img_NZ_rgb[200:400, 300:600]
plt.imshow(cropped_region)
plt.show()
Масштабирование изображений
Функция resize()
отресайзит картинку в больший или меньший размер. А регулируется это всё аргументами src
,dsize
(обязательные),fx
, fy
(факультативные).
cv2.resize() — синтаксис и аргументы
dst = resize( src, dsize[, dst[, fx[, fy[, interpolation]]]] )
dst
— изображение на выходе. Размер картинки будет равен dsize
(если он ненулевой), или посчитан через src.size()
, fx
, fy
.
Тип данных будет тем же, что и в оригинальной картинке.
src
— понятно, сама картинка, требующая вмешательства. Обязательный аргумент.dsize
— необходимый размер, обязательный аргумент.fx
— коэффициент масштаба горизонтальный.
— коэффициент масштаба вертикальный.
fy
Чуть подробнее о том, что тут происходит
В общем: в dsize
кладётся кортеж с натуральными числами, две штуки: (500, 500). Это размер, в который картинка отмасштабируется.
Можно воспользоваться вместо этого коэффициентами масштаба, тогда вместо dsize
надо впечатать None
.
Коэффициенты масштаба — fx
и fy
— берут оригинальную картинку, и растягивают/стягивают её пропорционально.
dsize
— имеет приоритет: конструкция resize(src, dsize=(100, 100),fx=20, fy=20)
выдаст картинку 100×100 пикселей.
Подробнее про resize(): ссылка на офф. документацию
Первый вариант масштабирования: коэффициенты масштаба
Увеличим кропнутую лодку в два раза:
resized_cropped_region_2x = cv2.resize(cropped_region,None,fx=2, fy=2)
plt.imshow(resized_cropped_region_2x)
plt.show()
Второй вариант масштабирования: сразу укажем нужные размеры
desired_width = 100 # желаемая ширина
desired_height = 200 # желаемая высота
dim = (desired_width, desired_height) # размер в итоге
# Масштабируем картинку
resized_cropped_region = cv2.resize(cropped_region,
dsize = dim,
interpolation = cv2.INTER_AREA)
# Или так:
# resized_cropped_region = cv2.resize(cropped_region,
# dsize = (100, 200),
# interpolation = cv2.INTER_AREA)
plt.imshow(resized_cropped_region)
plt.show()
Масштабирование с сохранением пропорций
За основу возьмём вторую методу, но отталкиваться будем от желаемой ширины.
Немного несложной математики:
# Используем 'dsize'
desired_width = 100 # желаемая ширина
# соотношение сторон: ширина, делённая на ширину оригинала
aspect_ratio = desired_width / cropped_region.shape[1]
# желаемая высота: высота, умноженная на соотношение сторон
desired_height = int(cropped_region.shape[0] * aspect_ratio)
dim = (desired_width, desired_height) # итоговые размеры
# Масштабируем картинку
resized_cropped_region = cv2.resize(cropped_region, dsize=dim, interpolation=cv2.INTER_AREA)
plt.imshow(resized_cropped_region)
plt.show()
Сохранимся-с!
# Приводим картинку к RGB
resized_cropped_region_2x = resized_cropped_region_2x[:,:,::-1]
# Сохраняем картинку
cv2.imwrite("resized_cropped_region_2x.png", resized_cropped_region_2x)
# Посмотрим на сокранённую картинку (тут-то нам и пригодится подгруженный PIL)
im = Image.open('resized_cropped_region_2x.png')
im.show()
Отражение картинки
Происходит с помощью функции flip()
.
dst = cv.flip( src, flipCode )
src
— понятно, сама картинка, требующая вмешательства. Обязательный аргумент.flipCode
— флаг, объясняющий функции, как конкретно мы хотим картинку отразить.
Подробнее про flip(): ссылка на офф. документацию
img_NZ_rgb_flipped_horz = cv2.flip(img_NZ_rgb, 1)
img_NZ_rgb_flipped_vert = cv2.flip(img_NZ_rgb, 0)
img_NZ_rgb_flipped_both = cv2.flip(img_NZ_rgb, -1)
plt.figure(figsize=[18,5])
plt.subplot(141);plt.imshow(img_NZ_rgb_flipped_horz);plt.title("Horizontal Flip");
plt.subplot(142);plt.imshow(img_NZ_rgb_flipped_vert);plt.title("Vertical Flip")
plt.subplot(143);plt.imshow(img_NZ_rgb_flipped_both);plt.title("Both Flipped")
plt.subplot(144);plt.imshow(img_NZ_rgb);plt.title("Original")
Вот и всё! Второй маленький шажок к человеку-фотошопу пройден! До встречи в следующих сериях.
Yuribtr
Вопрос автору - а вы можете сформулировать конечную цель курса? А то в первой части я нашел, что это просто перевод туториала. А в картинке у вас написано что это ускоренный курс. По итогу, какие знания в хотите дать читателям?
Если у вас будет несколько статей, то удобно было бы сделать оглавление и ссылки вперед / назад в каждой статье, например как сделано здесь.
И опечатку в картинке затравки поправьте - там пропущена буква "o" в слове "Course", и звучит угрожающе.
Halt
Crash Curse — проклятие аварии. Самокритичненько.
julicq
Там даже Crush Curse - проклятье дробилки...
Halt
Скорее проклятье любимой.
goshkalinin Автор
...ещё и в "crash" опечатался, так что это звучало максимально угрожающе!
Это-таки перевод ускоренного курса, действительно. Он обещает провести читателя от открытия картинки до распознавания объектов.
Прикручу оглавление со ссылками с третьей части. А то если два перевода на месяц (или годы) застрянут с некликабельными главками, это будет как-то не очень.