Не открывается изображение openCV
Для конкатенации путей используйте os.path.join(). Выведите full_path и проверьте, что по этому пути действительно есть картинка.
21 мая 2021 в 2:54
Оно не работает даже если поместить код в папку с картинкой и обратиться по имени
21 мая 2021 в 7:14
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
У OpenCV есть проблемы с чтением из файлов содержащими в пути или имени не английские символы. Чтобы прочитать такие файлы вместо imread() можно использовать такую конструкцию:
image = cv2.imdecode(np.fromfile(full_path, dtype=np.uint8), cv2.IMREAD_COLOR)
Ну и полный пример, который работает с русскими именами в пути. И, да, chdir тут совершенно не нужен.
import os, cv2 import numpy as np CORRECT_EXTENTIONS = ["png"] def test(): images = [] directory = r".\Русский" for file in os.listdir(directory): extention = file.split('.')[-1] if extention in CORRECT_EXTENTIONS: full_path = os.path.join(directory, file) image = cv2.imdecode(np.fromfile(full_path, dtype=np.uint8), cv2.IMREAD_COLOR) image_mask = cv2.inRange(image, (0,0,0), (127,127,127)) images.append(cv2.resize(image_mask, (64,64))) cv2.imshow('image', np.hstack([image, cv2.cvtColor(image_mask, cv2.COLOR_GRAY2BGR)])) cv2.waitKey(0) return images test()
OpenCV — быстрый старт: начало работы с изображениями
Перевожу родной OpenCV-шный туториал. И он хорош! (Сложно сказать, чем не понравились те, что есть.)
Изначально туториал в виде ноутбука , поэтому что-то я убрал. А что-то добавил. В общем, это помесь перевода с пересказом.
Вступление
Эта записнушка поможет с первыми шагами в изучении обработки изображений и машинном зрении через OpenCV. Несколько простых примеров объяснят важные вещи! Рассмотрим:
- Как открыть изображение
- Проверить его атрибуты, вроде формы или типа данных в нём
- Матричное представление картинки в Numpy
- Цветные картинки и работу с каналами изображения
- Вывод изображения через matplotlib
- Сохранение изображений
Импортируем нужные библиотеки
import cv2 # собственно OpenCV import numpy as np # для работы с математикой import matplotlib.pyplot as plt # для вывода картинки
Открываем изображения в OpenCV
Картинкой пойдёт крошечная доска в шашечку, 18×18 пикселей:
OpenCV позволяет работать с разными форматами: JPG, PNG, и так далее. Можно загружать цветные и чб-картинки, изображения с альфа-каналом. Для загрузки воспользуйтесь функцией cv2.imread() .
cv2.imread() — синтаксис и аргументы
retval = cv2.imread( filename[, flags] )
retval : если картинка не загрузилась, в retval запишется None . Такое бывает при ошибке в имени/пути, или если изображение битое.
В функцию передаётся один обязательный аргумент и один необязательный флаг:
- filename : Может быть как относительным, так и абсолютным путём. Это обязательный аргумент.
- Flags : Флаги нужны для чтения изображения в определенном формате (например, в оттенках серого/цветном/с альфа-каналом). Необязательный аргумент! Значение по умолчанию cv2.IMREAD_COLOR или 1 : этот флаг загрузит изображение как цветное.
Перед примерами посмотрим на пару флагов:
- cv2.IMREAD_GRAYSCALE или 0 : Загружаем картинку как чёрно-белую
- cv2.IMREAD_COLOR или 1 : Флаг по умолчанию, загружает картинку как цветную, без альфа-канала.
- cv2.IMREAD_UNCHANGED или -1 : Загружает картинку в том виде, в котором она есть, включая альфу.
Флагов, конечно, больше.
Теперь посмотрим, что внутри шашечек:
# Читаем изображение как чёрно-белое cb_img = cv2.imread("checkerboard_18x18.png",0) # Печатаем что прочитали. Каждый пиксель есть элемент двумерного массива numpy. # Значение пикселей восьмибитное: [0,255] print(cb_img)

Посмотрим атрибуты изображения
# вывод размера изображения (то есть, массива Numpy) print("Image size is ", cb_img.shape) # тип данных в изображении (то есть, в массиве Numpy) print("Data type of image is ", cb_img.dtype)
В ответ получим:
>>>Image size is (18, 18) >>>Data type of image is uint8
Что за uint8?
8-bit unsigned integer arrays!
Uint8 — стандартный способ отображения изображений, где пиксель описывается диапазоном от 0 до 255. Если это изображение в градациях серого, пиксель со значением 0 является черным, а пиксель со значением 255 — белым.
Есть и другие форматы, конечно.
В общем, работаeт всё, что работает с ndarray : .ndim , .itemsize , .fill() .
А теперь выведем картинку через matplotlib
# рисуем шашечки plt.imshow(cb_img) # выводим шашечки plt.show()

Прочитанная цветовая палитра и отображённая могут и не совпасть. Поэтому явно укажем цветовое пространство для вывода изображения:
# Поставим настройку color map plt.imshow(cb_img, cmap='gray')

Другой пример
Расплывчатые шашечки! Такие же, как в прошлый раз, но теперь с нечёткими гранями:
# Читаем картинку как чб cb_img_fuzzy = cv2.imread("checkerboard_fuzzy_18x18.jpg",0) # Печатаем массив print(cb_img_fuzzy) # Показываем картинку plt.imshow(cb_img_fuzzy,cmap='gray') plt.show()


Переходим к цвету
До этого момента мы говорили про чёрно-белые изображения, а теперь поговорим про цветные.
Подопытным кроликом будет лого колы:
Загружаем, проверяем размер и тип данных:
coke_img = cv2.imread("coca-cola-logo.png",1) print("Image size is ", coke_img.shape) print("Data type of image is ", coke_img.dtype)
В ответ получим:
>>>Image size is (700, 700, 3) >>>Data type of image is uint8
Размер картинки поменялся, потому как чб-изображение состоит из одного канала — от чёрного до белого, а цветное — из нескольких, например, трёх: RGB .
Выведем на экран уже известными манипуляциями:
plt.imshow(coke_img) plt.show()

Цвет лого явно отличается от того, что было. Matplotlib ожидает картинку в формате RGB, а OpenCV хранит их в формате BGR. То есть, для корректного отображения нам нужно поменять местами красный и синий каналы.
# ниже numpy-специфичная конструкция: # (:) — взять каждый элемент по порядку, # (::-1) — взять каждый элемент, но в обратном порядке. coke_img_channels_reversed = coke_img[:, :, ::-1] plt.imshow(coke_img_channels_reversed)

Разделение и объединение каналов
cv2.split() — разделит многоканальный массив на несколько одноканальных.
cv2.merge() — объединит массивы в один многоканальный. Массивы должны быть одинакового размера.
Проверим их на озере. В смысле, озером.

# Разбиваем картинку на каналы: B,G,R img_NZ_bgr = cv2.imread("New_Zealand_Lake.jpg",cv2.IMREAD_COLOR) b,g,r = cv2.split(img_NZ_bgr) # Отрисовываем их plt.figure(figsize=[20,5]) plt.subplot(141);plt.imshow(r,cmap='gray');plt.title("Red Channel") plt.subplot(142);plt.imshow(g,cmap='gray');plt.title("Green Channel") plt.subplot(143);plt.imshow(b,cmap='gray');plt.title("Blue Channel") # Собираем обратно в BGR imgMerged = cv2.merge((b,g,r)) # Выводим, что получилось plt.subplot(144);plt.imshow(imgMerged[. -1]);plt.title("Merged Output") plt.show()
точка с запятой в коде.
. не поощряется, но допускается. Помогает писать код в одну строку, а иногда это удобно!

Перевод в иные цветовые пространства
cv2.cvtColor() — преобразует изображение из одного цветового пространства в другое. В случае преобразования в RGB и из него порядок каналов надо указать явно: RGB или BGR. Формат цвета по умолчанию в OpenCV часто называют RGB, но на самом деле это BGR. Первый байт в стандартном 24-битном цветном изображении будет 8-битным синим компонентом, второй байт будет зеленым, а третий байт будет красным. Четвертый, пятый и шестой байты будут, соответственно, вторым пикселем — синий, зеленый, красный, и так до конца картинки.
Преобразование BGR в RGB
img_NZ_rgb = cv2.cvtColor(img_NZ_bgr, cv2.COLOR_BGR2RGB) plt.imshow(img_NZ_rgb) plt.show()
Преобразование в HSV
HSV — Hue, Saturation, Value — тон, насыщенность, значение.
img_hsv = cv2.cvtColor(img_NZ_bgr, cv2.COLOR_BGR2HSV) # Разобъём картинку на H,S,V каналы h,s,v = cv2.split(img_hsv) # Нарисуем и покажем их plt.figure(figsize=[20,5]) plt.subplot(141);plt.imshow(h,cmap='gray');plt.title("H Channel") plt.subplot(142);plt.imshow(s,cmap='gray');plt.title("S Channel") plt.subplot(143);plt.imshow(v,cmap='gray');plt.title("V Channel") plt.subplot(144);plt.imshow(img_NZ_rgb);plt.title("Original") plt.show()

Модификация отдельного канала
Хочется синий посиней? Есть решение!
img_NZ_bgr = cv2.imread("New_Zealand_Lake.jpg",cv2.IMREAD_COLOR) b,g,r = cv2.split(img_NZ_bgr) b = b+50 # осиняем plt.figure(figsize=[20,5]) plt.subplot(141);plt.imshow(r,cmap='gray');plt.title("Red Channel") plt.subplot(142);plt.imshow(g,cmap='gray');plt.title("Green Channel") plt.subplot(143);plt.imshow(b,cmap='gray');plt.title("Blue Channel") imgMerged = cv2.merge((r,g,b)) # в этот раз соберём сразу RGB plt.subplot(144);plt.imshow(imgMerged);plt.title("Merged Output") plt.show()

Сохранение изображений
Почти cv2.imread , только cv2.imwrite . Первым аргументом передаём путь и имя, вторым — изображение. Оба два обязательны. Расширение OpenCV подберёт, отталкиваясь от указанного в имени. Ещё можно докинуть параметров, и, например, указать качество JPG.
cv2.imwrite( filename, img[, params] )
Вот и всё! Первый маленький шажок к человеку-фотошопу пройден! До встречи в следующих сериях.
Как предобрабатывать изображения с OpenCV
Огромную долю в восприятии информации человеком занимает визуальная информация. Практически все в мире можно представить в виде изображения. Изображения и видео могут содержать в себе очень много данных — именно поэтому задачи по обработке изображений человек отдает машине.
Компьютерное зрение применяется во многих сферах, например в детекции, классификации, сегментации объектов на изображении. И для каждой уникальной проблемы внутри этих сфер необходимо создать автоматизированное решение, научить компьютер.
Сегодня речь пойдет об одном из важнейших этапов создания моделей компьютерного зрения – предварительной обработке изображений. Используя различные способы предобработки, можно достичь более высоких показателей качества и быстродействия обученной модели. Например, снизить или повысить до необходимого уровня качество картинок, расширить датасет в случае недостатка входных данных с помощью аугментации, выделить или затенить объекты, и так далее – все зависит от решаемой проблемы. Здесь мы рассмотрим некоторые из ключевых методик в предобработке изображений и задачи, в которых они могут применяться.
Но для начала попробуем разобраться, что такое изображение для компьютера?
Изображение можно определить как двумерную функцию F(x,y), где x и y — координаты на плоскости изображения, а амплитуда f называется интенсивностью или яркостью изображения в точке с этими координатами. В цифровых изображениях значение функции F(x, y) – конечно. То есть, цифровое изображение – это массив пикселей, расположенных в столбцах и строках. Пиксели — это элементы изображения, которые содержат информацию о насыщенности. Изображение также может быть представлено в 3D, где x, y и z становятся пространственными координатами. Пиксели расположены в виде матрицы. F(x,y) – черно-белое изображение, F(x,y,z) – цветное.
Отличие черно-белого изображение от цветного колоссально. Первое – это одномерное пространство черных и белых точек, пикселей. Цветное изображение обычно представляется в виде трех одномерных пространств, наложенных друг на друга:
В процессе обработки изображений, кстати, чаще используют черно-белые картинки. Использование оттенков серого/чб помогает упростить алгоритмы, увеличить скорость обработки. Плюс такие изображения легче воспринимать.
От слов к делу. Самый популярный инструмент для обработки изображений на питоне – OpenCV. Для изучения инструмента написано много литературы, он довольно прост в установке и работе. Также установим модуль matplotlib. С его помощью можно выводить изображения в интерфейсе Jupyter-Notebook. Для установки выполним в командной строке 2 простые команды:
pip install opencv-python pip install matplotlib
Теперь попробуем преобразовать цветного тигра в черно-белого. Делается это просто. Для начала импортируем библиотеки, которые понадобятся для работы:
import cv2 import numpy as np from matplotlib import pyplot as plt
Теперь «прочитаем» изображение, используя функцию cv2.imread(). А за преобразование цветных картинок в черно-белые, используем функцию cv2.cvtColor с параметром cv2.COLOR_BGR2GRAY():
image = cv2.imread(‘tiger.jpg’) image = np.flip(image, axis=-1) bw_img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
Посмотрим, что получилось, используя следующий код:
images = [] images.append(image) images.append(bw_img) titles = [‘тигр’,’тигр чб’] for i in range(len(images)): plt.subplot(1,2,i+1),plt.imshow(images[i],’gray’) plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
В результате выполнения этих команд будут выведены изображения:
Получившуюся картинку можно сохранить, используя функцию cv2.imwrite():
cv2.imwrite(‘bw_tiger.jpg’, bw_tiger)
Теперь перейдем к более сложным алгоритмам обработки изображений.
Морфологическая обработка изображений:
Этот метод позволяет удалить дефекты из бинарных изображений. Иногда области, созданные в результате простой обработки, могут быть искажены шумом. Морфологическая же обработка позволяет сглаживать изображения.
Морфологические операции отображаются на структуре черно-белых изображений. Этот метод изменяет изображение, используя некоторый шаблон или фильтр, структурирующий элемент. Он помещается в различные места на изображении и сравнивает между собой разные области с группами пикселей, преобразуя их в зависимости от задачи.
Есть два вида морфологического преобразования:
— дилатация (морфологическое расширение) – свертка изображения или выделенной области изображения с шаблоном. В шаблоне выделяется единственная ведущая позиция (anchor), которая совмещается с текущим пикселем при вычислении свертки. Во многих случаях в качестве шаблона выбирается квадрат или круг с ведущей позицией в центре. Такая операция вызывает рост светлых областей на изображении;
— эрозия (морфологическое сужение) – операция обратная дилатации. Эта операция вызывает уменьшение светлых областей на изображении:
Применение этих операции может повысить «читаемость» текста.
Посмотрим, как это работает, используя функции cv2.dilate(), cv2.erode() с заранее заданным ядром (kernel) размером 3х3 пикселя:
# чтение изображения img = cv2.imread(‘letters.jpg’) # определение ядра свертки morph_kernel = np.ones((3, 3)) # применение функций к изображению # параметр iterations означает, сколько раз будет применена операция dilate_img = cv2.dilate(img, kernel= morph_kernel, iterations=1) erode_img = cv2.erode(img, kernel= morph_kernel, iterations=1)
Код для вывода изображений на экран или их сохранения можете адаптировать из предыдущего примера. Результат выполнения функций:
Фильтр размытия по гауссу:
Блюр (размытие) часто используется для сглаживания неравномерных значений пикселей изображения, обрезая самые высокие значения. В алгоритмах компьютерного зрения, данный метод используется для улучшения структуры изображения в различных масштабах, для уменьшения размера изображения.
Итак, пусть исходное изображение будет задано яркостью x(m,n). Гауссово размытие с радиусом r считается по формуле:
Вывод формулы займет слишком много места и не так важен. На хабре есть подробное описание (https://habr.com/ru/post/151157/), как из формулы получается такое ядро фильтра:
Размытие по Гауссу на изображении в OpenCV происходит в два этапа. В первом проходе так называемое ядро фильтра Гаусса размывает изображение по одной из плоскостей (горизонтальная или вертикальная). А во втором проходе размывает по оставшейся плоскости.
Применим фильтр Гаусса в OpenCV, используя функцию cv2.GaussianBlur():
img = cv2.imread(‘distorted.png’) # параметром ksize=(11, 11) зададим размер ядра фильтра размытия 11х11 пикселей: # параметры sigmaX/Y 0, 0 отвечают за сдвиг ядра при проходе по осям X, Y blurred_img = cv2.GaussianBlur(img, ksize=(11, 11), sigmaX =0, sigmaY=0)
Результат выполнения функций:
Повышение резкости изображения:
В процессе обработки изображений могут возникать сценарии, когда изображение размыто и для выполнения задачи необходимо его «восстановить», то есть, увеличить резкость.
К сожалению, как в старых голливудских фильмах не получится. Нельзя просто взять и преобразовать мелкий кадр номера авто в километре от камеры с разрешением 0.3 мегапикселя в что-то читаемое. Но можно попытаться. И для этого есть метод в OpenCV! И он предполагает применение фильтра к изображению! Кстати, подробнее о других фильтрах для обработки можно почитать на вики https://en.wikipedia.org/wiki/Kernel_(image_processing).
Процесс увеличения резкости обычно используется для улучшения видимости краев изображения. Есть много фильтров, которые мы можем использовать, но наиболее популярный из них можно представить в виде следующей матрицы:
Суть та же, что и раньше – проходим последовательно по каждой оси изображения и модифицируем группы пикселей для достижения желаемого результата.
Сначала зададим ядро фильтра (матрицу), после чего — применим ее к изображению, используя функцию cv2.filter2D():
img = cv2.imread(‘blurred.jpg’) sharp_filter = np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]]) # параметр ddepth отвечает за «глубину» картинки # ddepth=-1 означает, что глубина получившейся картинки будет как у исходной sharpen_img = cv2.filter2D(img, ddepth=-1, kernel=sharp_filter)
Результат выполнения функций:
Определение границ объектов на изображении:
Да, есть и такие задачи. Данный метод может пригодиться для извлечения «полезной» информации о форме объекта из изображения. Для определения формы объекта, человек подсознательно ищет резкие переходы между уровнями яркости частей объектов. Сделаем точно так же и в машинном алгоритме.
Наиболее распространенным алгоритмом обнаружения краев является фильтр (оператор) Собеля. Этот оператор основан на свёртке изображения небольшими сепарабельными целочисленными фильтрами в вертикальном и горизонтальном направлениях, поэтому его относительно легко вычислять. С другой стороны, используемая им аппроксимация градиента достаточно грубая, особенно это сказывается на высокочастотных колебаниях изображения. Оператор вычисляет градиент яркости изображения в каждой точке. Так находится направление наибольшего увеличения яркости и величина её изменения в этом направлении. Результат показывает, насколько «резко» или «плавно» меняется яркость изображения в каждой точке, а значит, вероятность нахождения точки на грани, а также ориентацию границы.
Оператор использует 2 ядра 3х3, которыми сворачивают исходное изображение для вычисления приближённых значений производных по горизонтали и по вертикали. Пусть А —исходное изображение, а Gx и Gy— два изображения, на которых каждая точка содержит приближённые производные по x и y. Они вычисляются следующим образом (* — операция свертки):
Координата x здесь возрастает «направо», а y — «вниз». В каждой точке изображения приближённое значение величины градиента можно вычислить путём использования полученных приближенных значений производных:
Для более корректного выделения объекта, рекомендуется немного уменьшить четкость изображения. Здесь используем упомянутый ранее cv2.GaussianBlur(). За применение оператора Собеля в OpenCV отвечает функция cv2.Sobel():
img = cv2.imread(‘tiger.jpg’) # для использования оператора собеля сразу по 2 осям зададим параметры dx=1, dy=1 # используя параметр ddepth=cv2.CV_64F, зададим глубину изображения в 64 бита sobelxy = cv2.Sobel(img, ddepth=cv2.CV_64F, dx=1, dy=1, ksize=5)
Результат выполнения функций:
Благодарю за прочтение! В заключение хочется напомнить, что в материале описаны только некоторые часто используемые алгоритмы обработки изображений. Естественно, их гораздо больше. Для решения разных задач могут понадобиться разные алгоритмы или их комбинации. Постарайтесь применять их там, где это действительно надо и помните главное правило при разработке моделей – «garbage in, garbage out».
OpenCV: считывания картинки через imread()
Прежде чем проводить какие-нибудь манипуляции с изображением — его нужно предварительно считать из файла.
В OpenCV для этого используется функция imread().
Mat cv::imread( const String & filename, int flags = IMREAD_COLOR )
retval = cv2.imread( filename[, flags] )
Возможные параметры (flags):
IMREAD_UNCHANGED
Python: cv.IMREAD_UNCHANGED
Если установлен — возвращает загруженное изображение как есть (с альфа-каналом, иначе он будет обрезан).
IMREAD_GRAYSCALE
Python: cv.IMREAD_GRAYSCALE
— преобразует изображение в одноканальное изображение в оттенках серого (внутреннее преобразование кодека).
IMREAD_COLOR
Python: cv.IMREAD_COLOR
— преобразует изображение в 3-канальное цветное изображение BGR.
IMREAD_ANYDEPTH
Python: cv.IMREAD_ANYDEPTH
— возвращает 16-битное / 32-битное изображение, когда вход имеет соответствующую глубину, в противном случае — преобразует его в 8-битное.
IMREAD_ANYCOLOR
Python: cv.IMREAD_ANYCOLOR
— изображение считывается в любом возможном цветовом формате.
IMREAD_LOAD_GDAL
Python: cv.IMREAD_LOAD_GDAL
— использовать драйвер gdal для загрузки изображения.
IMREAD_REDUCED_GRAYSCALE_2
Python: cv.IMREAD_REDUCED_GRAYSCALE_2
— преобразовать изображение в одноканальное изображение в оттенках серого и уменьшить размер изображения на 1/2.
IMREAD_REDUCED_COLOR_2
Python: cv.IMREAD_REDUCED_COLOR_2
— преобразовать изображение в 3-канальное цветное изображение BGR и уменьшить размер изображения на 1/2.
IMREAD_REDUCED_GRAYSCALE_4
Python: cv.IMREAD_REDUCED_GRAYSCALE_4
— преобразовать изображение в одноканальное изображение в оттенках серого и уменьшить размер изображения на 1/4.
IMREAD_REDUCED_COLOR_4
Python: cv.IMREAD_REDUCED_COLOR_4
— преобразовать изображение в 3-канальное цветное изображение BGR и уменьшить размер изображения на 1/4.
IMREAD_REDUCED_GRAYSCALE_8
Python: cv.IMREAD_REDUCED_GRAYSCALE_8
— преобразовать изображение в одноканальное изображение в оттенках серого и уменьшить размер изображения на 1/8.
IMREAD_REDUCED_COLOR_8
Python: cv.IMREAD_REDUCED_COLOR_8
— преобразовать изображение в 3-канальное цветное изображение BGR и уменьшить размер изображения на 1/8.
IMREAD_IGNORE_ORIENTATION
Python: cv.IMREAD_IGNORE_ORIENTATION
— не поворачивать изображение в соответствии с флагом ориентации EXIF.
Попробуем считать нашу тестовую картинку.
Подключаем нужные библиотеки:
import matplotlib.pyplot as plt %matplotlib inline import cv2 print('OpenCV version:', cv2.__version__)
OpenCV version: 3.4.2
Считаем тестовое изображение:
imgorig = cv2.imread('cat2.jpg') print(type(imgorig)) print(imgorig.shape)
(278, 345, 3)
Посмотрим на нашу картинку:
plt.imshow(imgorig) plt.show()

Неожиданно! В чём же дело?
А в том, что OpenCV в качестве порядка хранения цветов изображения, по-умолчанию, использует порядок BGR, а matplotlib использует RGB.
Поэтому, при попытке отображении matplotlib-ом изображения, загруженного с помощью OpenCV, возникает путаница в порядке каналов и получается подобный эффект.
Чтобы решить эту проблему, достаточно сконвертировать картинку в RGB-формат при помощи функции cv2.cvtColor().
rgb_img = cv2.cvtColor(imgorig, cv2.COLOR_BGR2RGB) plt.imshow(rgb_img) plt.show()

Отлично!
Остаётся только единственный вопрос:
Почему в OpenCV по-умолчанию используется формат BGR ?
Поиск даёт единственный ответ — по историческим причинам.
В ранних версиях OpenCV использовался порядок BGR, так как тогда именно формат BGR был популярен среди производителей камер и поставщиков программного обеспечения.
Например, в ОС Windows для указания значения цвета с помощью COLORREF используется именно формат BGR:
0x00bbggrr
Вот и выходит, что по историческим причинам, в OpenCV и сейчас, по-умолчанию, используется порядок цветов BGR. Остаётся только помнить об этом.