- OpenCV — быстрый старт: базовые операции с изображениями
- Доступ к отдельным пикселям
- Изменение пикселей
- Обрезка картинок
- Масштабирование изображений
- cv2.resize() — синтаксис и аргументы
- Чуть подробнее о том, что тут происходит
- Первый вариант масштабирования: коэффициенты масштаба
- Второй вариант масштабирования: сразу укажем нужные размеры
- Масштабирование с сохранением пропорций
- Сохранимся-с!
- Отражение картинки
- Меняем размер изображения в Python Pillow с обрезанием и изменением ширины и высоты
- Получение размеров
- Изменение размера изображения с resize()
- Учет соотношения сторон
- Создание эскиза thumbnail
- Обрезание (cropping) картинки
- Создание границ картинки и ее увеличение за счет новой области
- Создание границ изображения с ImageOps.expand
OpenCV — быстрый старт: базовые операции с изображениями
Судя по количеству закладок на первой части, работа моя — не зряшная.
В прошлый раз разбирали скучное открывание-закрывание картинки, в этот раз засунем в неё руки поглубже:
- Доступ к пикселям и работа с ними.
- Масштабирование картинки.
- Обрезка.
- Отражение.
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_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 пикселей.
Первый вариант масштабирования: коэффициенты масштаба
Увеличим кропнутую лодку в два раза:
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() .
src — понятно, сама картинка, требующая вмешательства. Обязательный аргумент.
flipCode — флаг, объясняющий функции, как конкретно мы хотим картинку отразить.
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")
Вот и всё! Второй маленький шажок к человеку-фотошопу пройден! До встречи в следующих сериях.
Меняем размер изображения в Python Pillow с обрезанием и изменением ширины и высоты
В Python есть много библиотек по работе с изображениями. Одна из самых известных библиотек — Pillow (ранее PIL). В этой статье будут рассмотрены примеры по изменению размеров картинки с помощью увеличения и уменьшения картинки, а так же обрезания.
Pillow не является стандартной библиотекой и устанавливается отдельно:
Получение размеров
Для получения размера картинки мы должны открыть ее (создать объект класса Image) и получить атрибут ‘size’. Это можно сделать так:
from PIL import Image image_path = 'F:/hello.png' img = Image.open(image_path) # получаем ширину и высоту width, height = img.size print(width, height) # открываем картинку в окне img.show()
Изменение размера изображения с resize()
Для изменения ширины и высоты есть метод ‘resize’. Этот метод не изменяет текущую картинку, а возвращает новую. Новый размер мы должны указать в виде кортежа ‘(ширина, высота)’. Сохранить картинку можно через метод ‘save’:
from PIL import Image image_path = 'F:/hello.png' img = Image.open(image_path) # изменяем размер new_image = img.resize((200, 385)) new_image.show() # сохранение картинки new_image.save('F:/hello2.png')
На примере выше видно, что картинка меняется не пропорционально. Это можно изменить двумя способами:
Учет соотношения сторон
Что бы создать пропорциональную картинку вам нужно определиться с размером одно из сторон. В примере ниже указана фиксированная ширина, но вы можете немного изменить скрипт и указать высоту. Мы должны вычислить процентное соотношение старой ширины к новой, а затем умножить ее на высоту:
from PIL import Image image_path = 'F:/hello.png' # указываем фиксированный размер стороны fixed_width = 200 img = Image.open(image_path) # получаем процентное соотношение # старой и новой ширины width_percent = (fixed_width / float(img.size[0])) # на основе предыдущего значения # вычисляем новую высоту height_size = int((float(img.size[0]) * float(width_percent))) # меняем размер на полученные значения new_image = img.resize((fixed_width, height_size)) new_image.show() new_image.save('F:/hello2.png')
Создание эскиза thumbnail
Есть еще один способ выполнить предыдущие шаги автоматически — он называется ‘thumbnail’. Его отличие от resize() в том, что он не может увеличить размер изображения. Если с помощью resize() вы можете изменить размер 200х200 на 400х400, то метод thumbnail() оставит исходный.
В методе ‘thumbnail()’ создается кортеж с 2-мя числами: максимальная ширина и высота. Понятие «максимальный» нужно оценивать как значение, которое можно менять в меньшую сторону. Если вы возьмете картинку размером 100х100 и захотите ее уменьшить до 100х50, то Pillow высчитает оптимальны размер как 50х50.
from PIL import Image image_path = 'F:/hello.png' img = Image.open(image_path) # пропорциональное изменение картинки img.thumbnail(size=(350,400)) img.show() img.save('F:/hello3.png')
Обрезание (cropping) картинки
Для обрезания ненужных частей картинки используется метод ‘crop()’. Внутри этого метода помещается кортеж со значениями (отступами) соответствующие сторонам, которые нужно обрезать. Стороны можно определить следующими переменными:
Оси x1 и y1 находятся в левой верхней части изображения, а оси x2 и y2 в правой нижней. Левая верхняя часть имеет координаты 0 и 0, а правая нижняя соответствует ширине и высоте изображения. Картинка, которую я использовал в предыдущих примерах, имеет размер 401х385. Если распределить все координаты, соответствующие этой картинке, получится следующий результат:
Представим, что нам нужно обрезать название на картинке — это примерно 60px. Мы должны вычесть (вырезать) это значение из нижнего правого угла (высоты):
from PIL import Image image_path = 'F:/hello.png' img = Image.open(image_path) size = img.size width, height = img.size # обрезаем картинку new_image = img.crop((0,0,width,height-60)) new_image.show() new_image.save('F:/hello3.png')
Если бы мы хотели оставить только название, то нужно было бы изменить значение y1 (2-ой индекс):
Создание границ картинки и ее увеличение за счет новой области
Увеличить размер изображения, не растягивая ее (как делает resize()), можно тремя методами.
Первый, который так же обсуждался в предыдущей части статьи, увеличит изображение без возможности изменения фона (в зависимости от изображения он может быть черным/прозрачным). В примере ниже мы добавим по 50px к каждому углу:
Второй способ — за счет создания нового изображения и вставки в него старого.
Для создания нового изображения нужно использовать метод ‘Image.new()’. В этом методе мы должны указать тип картинки, ее ширину и высоту, а так же цвет. Что бы вставить изображение — используется метод ‘paste()’. В нем указывается отступ формата ‘(верх, лево)’ либо вы можете использовать координаты аналогичные методу ‘crop()’.
В следующем примере мы создадим изображение, которое будет на 50px больше и вставим в него старую картинку. Картинка будет располагаться в 25px от верхнего и левого края:
from PIL import Image image_path = 'F:/hello.png' old_img = Image.open(image_path) # создание нового изображения new_image = Image.new(old_img.mode, (old_img.size[0]+50, old_img.size[1]+50), 'white') # вставляем старой изображение в новое new_image.paste(old_img, (25, 25)) new_image.show() new_image.save('F:/hello3.png')
Создание границ изображения с ImageOps.expand
Создать границы картинки (расширить) так же можно через метод ImageOps.expand(). Вообще через модуль ‘ImageOps’ можно проделать все шаги описанные выше, но он может не работать с какими-то типами изображений.
В метод ‘expand()’ мы должны передать объект ‘Image’, размер границы и цвет. Пример с результатом аналогичным предыдущему:
from PIL import Image from PIL import ImageOps image_path = 'F:/hello.png' old_img = Image.open(image_path) # создание нового изображения с белым фоном new_image = ImageOps.expand(old_img, border=25, fill=(255,255,255)) new_image.show() new_image.save('F:/hello3.png')