- OpenCV — быстрый старт: базовые операции с изображениями
- Доступ к отдельным пикселям
- Изменение пикселей
- Обрезка картинок
- Масштабирование изображений
- cv2.resize() — синтаксис и аргументы
- Чуть подробнее о том, что тут происходит
- Первый вариант масштабирования: коэффициенты масштаба
- Второй вариант масштабирования: сразу укажем нужные размеры
- Масштабирование с сохранением пропорций
- Сохранимся-с!
- Отражение картинки
- OpenCV — cравнение алгоритмов интерполяции при изменении размеров изображения
- Сравнение работы различных методов интерполяции при масштабировании изображений в OpenCV
- Сравнение скорости работы различных методов интерполяции при масштабировании изображений в OpenCV
- Заключение
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")
Вот и всё! Второй маленький шажок к человеку-фотошопу пройден! До встречи в следующих сериях.
OpenCV — cравнение алгоритмов интерполяции при изменении размеров изображения
Изменение размеров (масштабирование / scaling) — это очень часто используемый метод при работе с изображениями.
В OpenCV для выполнения этой задачи используется функция resize().
void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR )
dst = cv2.resize( src, dsize[, dst[, fx[, fy[, interpolation]]]] )
Размер изображения можно либо указать вручную (dsize), либо передать коэффициенты масштабирования по соответствующим осям картинки: fx, fy.
height, width = img.shape[:2] res = cv.resize(img, (2*width, 2*height), interpolation = cv.INTER_CUBIC) # ИЛИ res = cv.resize(img, None, fx=2, fy=2, interpolation = cv.INTER_CUBIC)
Параметр interpolation устанавливает один из нескольких возможных методов интерполяции:
cv.INTER_NEAREST — интерполяция методом ближайшего соседа (nearest-neighbor interpolation),
cv.INTER_LINEAR — билинейная интерполяция (bilinear interpolation (используется по умолчанию),
cv.INTER_CUBIC — бикубическая интерполяция (bicubic interpolation) в окрестности 4×4 пикселей,
cv.INTER_AREA — передискретизации с использованием отношения площади пикселя,
cv.INTER_LANCZOS4 — интерполяция Ланцоша (Lanczos interpolation) в окрестности 8×8 пикселей.
Для выбора метода — нужно разобраться — какой из этих методов лучше?
По умолчанию, интерполяция выполняется методом cv.INTER_LINEAR.
В официальном учебном пособии OpenCV (Geometric Transformations of Images) указывается, что:
* для сжатия изображения — наиболее предпочтительным методом интерполяции является cv.INTER_AREA,
* для увеличения изображения — наиболее предпочтительны методы интерполяции: cv.INTER_CUBIC (медленный) и cv.INTER_LINEAR.
Остаётся провести сравнение работы различных методов интерполяции, чтобы на практике проверить — какой их них лучше подходит для уменьшения или увеличения изображений.
Сравнение работы различных методов интерполяции при масштабировании изображений в OpenCV
Ниже приведены результаты доступных в OpenCV алгоритмов интерполяции при масштабировании изображений. Примеры показаны как для увеличения, так и для уменьшения размера изображения.
Подключаем нужные библиотеки:
import numpy as np import matplotlib.pyplot as plt %matplotlib inline np.random.seed(42) import seaborn as sns plt.style.use('seaborn-white') sns.set_context('notebook', rc=, font_scale=1.5) import cv2 print('OpenCV version:', cv2.__version__)
Загружаем тестовые картинки:
imgorig = cv2.imread('cat4.jpg') imgorig2 = cv2.imread('cat2.jpg') imgorig3 = cv2.imread('cat3.jpg') img = cv2.cvtColor(imgorig, cv2.COLOR_BGR2RGB) print(img.shape) img2 = cv2.cvtColor(imgorig2, cv2.COLOR_BGR2RGB) print(img2.shape) img3 = cv2.cvtColor(imgorig3, cv2.COLOR_BGR2RGB) print(img3.shape)
(224, 224, 3) (278, 345, 3) (385, 455, 3)
interpolation_algorithm = [ ("nearest", cv2.INTER_NEAREST), ("bilinear", cv2.INTER_LINEAR), ("bicubic", cv2.INTER_CUBIC), ("area", cv2.INTER_AREA), ("lanczos4", cv2.INTER_LANCZOS4) ]
def resize_test(img, factor, is_plot=True, file_name=None): height, width, channels = img.shape height2, width2 = int(height*factor), int(width*factor) print('orig size:', height, width) print('resize size:', height2, width2) imgs = [] for alg in interpolation_algorithm: img_r = cv2.resize(img, (width2, height2), interpolation = alg[1]) imgs.append(img_r) if is_plot: plt.figure(figsize=(11, 2)) plt.subplot(1, len(imgs)+1, 1) plt.title('orig') plt.imshow(img) plt.axis('off') for i in range( len(imgs) ): plt.subplot(1, len(imgs)+1, i+2) plt.title( interpolation_algorithm[i][0] ) plt.imshow(imgs[i]) plt.axis('off') plt.subplots_adjust(wspace=0) if file_name != None: plt.savefig(file_name) plt.show() return imgs print('DOWN') img_down0 = resize_test(img, 0.25, file_name='opencv_resize_down1.png') img_down1 = resize_test(img2, 0.125, file_name='opencv_resize_down2.png') img_down2 = resize_test(img3, 0.125, file_name='opencv_resize_down3.png') print('UP') img_up0 = resize_test(img_down0[3], 4, file_name='opencv_resize_up1.png') img_up1 = resize_test(img_down1[3], 8, file_name='opencv_resize_up2.png') img_up2 = resize_test(img_down2[3], 8, file_name='opencv_resize_up3.png')
orig size: 224 224 resize size: 56 56
orig size: 278 345 resize size: 34 43
orig size: 385 455 resize size: 48 56
orig size: 56 56 resize size: 224 224
orig size: 34 43 resize size: 272 344
orig size: 48 56 resize size: 384 448
Сравнение скорости работы различных методов интерполяции при масштабировании изображений в OpenCV
Дополнительно рассмотрим сравнение скорости работы различных алгоритмов интерполяции.
import pandas as pd import time
Выполним 50 раз процедуру изменения масштаба изображения с разным коэффициентом масштабирования и разными алгоритмами интерполяции:
n = 50 data = [] factors = [0.125, 0.25, 0.5, 0.75, 1.25, 1.5, 2, 3, 4, 6, 8, 10] for alg in interpolation_algorithm: for factor in factors: times = [] for i in range(n): t0 = time.time() cv2.resize(img, None, fx=factor, fy=factor, interpolation=alg[1]) tdelta = (time.time()-t0) times.append(tdelta) mtime = np.mean(times) err = 2*np.std(times) data.append(dict(time=mtime, algorithm=alg[0], error=err, scale=factor)) df = pd.DataFrame(data) ax = df.set_index('scale').groupby("algorithm")['time'].plot(legend=True, figsize=(10, 8), grid=True, title='Algorithm time compare') ax[0].set_xlabel("scale factor") ax[0].set_ylabel("mean time (sec)")
Полученное графическое представление скорости работы алгоритмов:
Заключение
Полученные данные говорят сами за себя.
Для уменьшения масштаба изображения, лучше всего подходит метод cv2.INTER_AREA.
Для увеличения масштаба изображения, лучше всего подходят методы (расположены в порядке уменьшения скорости работы): cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_LANCZOS4.
За исключением cv2.INTER_LANCZOS4 — все методы имеют сравнимое время работы.
Однако cv2.INTER_CUBIC и cv2.INTER_LINEAR более медленные, чем cv2.INTER_AREA и cv2.INTER_NEAREST, а cv2.INTER_LANCZOS4 — самый медленный из всех алгоритмов.