Обработка изображений в C++: OpenCV, stb, ImageMagick
Загрузить, изменить размер, наложить фильтр — задача одна, а библиотек под неё три класса. Разбираем OpenCV, stb_image, CImg и ImageMagick по весу, скорости сборки и порогу входа.

Вам нужно в C++-сервисе принять JPEG, уменьшить до 800 пикселей по длинной стороне, наложить водяной знак и отдать WebP. Очевидное решение — «возьму OpenCV, там всё есть» — тянет за собой 200+ МБ бинарников, десятки транзитивных зависимостей и время сборки, которое превращает CI в мучение. Для одной операции ресайза это как забивать гвоздь бетономешалкой.
Обратная крайность — написать декодер JPEG руками — не делает никто в здравом уме. Между этими полюсами лежат три-четыре реальных варианта, и выбор между ними определяется не тем, «что мощнее», а размером задачи, ограничениями сборки и тем, готовы ли вы тащить рантайм-зависимость.
Что вообще выбираем
Библиотеки для работы с растром в C++ делятся на два лагеря по способу подключения.
- Header-only — подключаются одним заголовком, компилируются вместе с вашим кодом. Ноль зависимостей на этапе линковки, ноль DLL в поставке. Сюда относятся stb_image / stb_image_write и CImg.
- Полноценные библиотеки — ставятся как пакет, линкуются как
.so/.lib, тянут свои зависимости (кодеки, BLAS, иногда GUI-бэкенды). Это OpenCV и ImageMagick (через Magick++).
Ещё один водораздел — что библиотека умеет. Декодирование и сохранение форматов есть у всех. Ресайз, поворот, цветовые преобразования — почти у всех. А вот детектирование объектов, морфология, работа с видео и матричная алгебра — это уже территория OpenCV, и ради ресайза их брать незачем.
Коротко про каждую
stb_image / stb_image_write (public domain, Sean Barrett). Два файла-заголовка. Декодирует JPEG, PNG, BMP, TGA, PSD, GIF, HDR; пишет PNG, JPG, BMP, TGA, HDR. Ресайз живёт в отдельном заголовке stb_image_resize2.h. Никаких фильтров, свёрток и цветокоррекции из коробки — только пиксельный буфер, дальше сами.
CImg (CeCILL-C, header-only, один заголовок CImg.h). В отличие от stb, это уже библиотека обработки: свёртки, морфология, геометрия, рисование, дисплей. Форматы читает/пишет ограниченно сама (BMP, PNM, а JPEG/PNG — через внешние libjpeg/libpng или вызов ImageMagick).
OpenCV 4.x (Apache 2.0). Промышленный комбайн: чтение форматов, ресайз, фильтры, компьютерное зрение, ML, видео, GPU-модули. Ставится через пакетный менеджер или vcpkg. Для простого пайплайна ресайза избыточен, но если задача растёт в сторону распознавания — окупается.
ImageMagick / Magick++ (ImageMagick License, совместима с Apache 2.0). Самый широкий набор форматов (сотни) и высокоуровневые операции. Тяжёлая рантайм-зависимость, зато читает практически всё, что существует.
Сравнение
| Критерий | stb_image(+resize) | CImg | OpenCV 4.x | Magick++ |
|---|---|---|---|---|
| Подключение | header-only, 2–3 файла | header-only, 1 файл | пакет + линковка | пакет + линковка |
| Внешние зависимости | нет | libjpeg/libpng для форматов | много | много |
| Форматы чтения | ~7 базовых | ограниченно / через сторонние | широко | очень широко (сотни) |
| Ресайз, поворот | да (resize) | да | да | да |
| Фильтры, свёртки, морфология | нет | да | да | да |
| Компьютерное зрение / ML | нет | нет | да | нет |
| Вес в поставке | минимальный | минимальный | десятки–сотни МБ | десятки МБ |
| Порог входа | низкий | средний | средний | средний |
Точные размеры бинарников зависят от того, какие модули OpenCV вы собрали и какие делегаты включены в ImageMagick, — их стоит померить на вашей сборке, а не полагаться на «примерно столько же».
Как это выглядит в коде
stb: минимальный пайплайн ресайза
Декодировать JPEG, уменьшить до 800 по ширине, сохранить в PNG. stb_image версии, актуальной на момент публикации (одиночные заголовки из репозитория nothings/stb). STB_..._IMPLEMENTATION определяется ровно в одном .cpp-файле, иначе получите дубли символов при линковке.
// сборка: g++ -O2 main.cpp -o resize -lm
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image_resize2.h"
#include <cstdio>
int main() {
int width = 0, height = 0, channels = 0;
// последний аргумент 0 — сохранить число каналов как в файле
unsigned char* src = stbi_load("input.jpg", &width, &height, &channels, 0);
if (!src) {
std::fprintf(stderr, "decode failed: %s\n", stbi_failure_reason());
return 1;
}
const int target_w = 800;
// сохраняем пропорции
const int target_h = static_cast<int>(
static_cast<long long>(height) * target_w / width);
unsigned char* dst = new unsigned char[target_w * target_h * channels];
// сигнатуру и enum-ы каналов сверяйте в stb_image_resize2.h вашей версии
stbir_resize_uint8_linear(
src, width, height, 0,
dst, target_w, target_h, 0,
static_cast<stbir_pixel_layout>(channels));
// качество JPEG для write здесь не нужно — пишем PNG
if (!stbi_write_png("output.png", target_w, target_h, channels,
dst, target_w * channels)) {
std::fprintf(stderr, "write failed\n");
}
stbi_image_free(src);
delete[] dst;
return 0;
}Имя функции ресайза в stb менялось: в старом stb_image_resize.h оно другое, чем в новом stb_image_resize2.h. Если копируете чужой пример и получаете «undefined reference», первым делом сверьте, какой из двух заголовков у вас и как называется функция в нём — смотрите комментарии в начале самого файла.
OpenCV: то же самое, но в три строки
OpenCV 4.x, линковка через pkg-config или vcpkg.
// сборка: g++ -O2 main.cpp -o resize `pkg-config --cflags --libs opencv4`
#include <opencv2/opencv.hpp>
int main() {
cv::Mat src = cv::imread("input.jpg", cv::IMREAD_COLOR);
if (src.empty()) return 1;
const int target_w = 800;
const int target_h = src.rows * target_w / src.cols;
cv::Mat dst;
cv::resize(src, dst, cv::Size(target_w, target_h), 0, 0, cv::INTER_AREA);
cv::imwrite("output.png", dst);
return 0;
}Разница в объёме кода обманчива: три строки OpenCV стоят вам всей сборочной инфраструктуры вокруг них. За лаконичность вы платите на этапе деплоя, а не написания.
Вопрос не «какая библиотека мощнее», а «какую зависимость вы готовы носить с собой всю жизнь проекта». Ресайз пишется за час на любой из них — а вот выкинуть OpenCV из легаси-сборки через два года почти невозможно.
Когда что выбирать
- Берите stb, если задача — декодировать, ресайзить, сохранять в популярных форматах, и вы хотите нулевые зависимости и мгновенную сборку. Идеально для CLI-утилит, встраивания в игровой движок, микросервиса в тонком контейнере.
- Берите CImg, если нужны фильтры, свёртки, геометрические преобразования и рисование, но при этом важна header-only-простота, а экзотические форматы не нужны. Учтите: для JPEG/PNG ей всё равно понадобятся сторонние libjpeg/libpng.
- Берите OpenCV, если задача уже содержит или вырастет в компьютерное зрение: детектирование, трекинг, дескрипторы, работа с видеопотоком, инференс. Ради одного ресайза — нет.
- Берите ImageMagick/Magick++, если ключевое требование — поддержка максимально широкого набора форматов и высокоуровневые операции без ручного кода, а тяжёлая рантайм-зависимость приемлема.
Что ломается на практике
- Дубли символов у stb.
STB_IMAGE_IMPLEMENTATIONв двух .cpp — и линкер сыплет «multiple definition». Определяйте макросы реализации в одном файле, в остальных подключайте заголовок без макроса. - Цветовой порядок в OpenCV.
imreadдаёт BGR, а не RGB. Отдаёте буфер во внешний код, ожидающий RGB, — получаете синие лица. Конвертируйте черезcv::cvtColor. - Метод интерполяции при уменьшении. Для даунскейла
INTER_AREAв OpenCV даёт заметно чище результат, чемINTER_LINEAR. Для увеличения — наоборот. В stb выбор ограничен тем, что предоставляет resize-заголовок. - EXIF-ориентация. Ни stb, ни базовый ресайз OpenCV не разворачивают фото по EXIF-тегу Orientation автоматически. Снимки со смартфона окажутся повёрнутыми боком. Ориентацию читайте и применяйте сами.
- Каналы и альфа. При декодировании с сохранением исходного числа каналов PNG с прозрачностью придёт как 4-канальный. Ресайз и запись должны получать корректное число каналов, иначе — искажения или падение.
Что делать дальше
Соберите минимальный прототип на stb и на OpenCV, прогоните на своём наборе картинок и померьте три вещи: время сборки с нуля, размер итогового артефакта в вашем контейнере и время обработки на реальном объёме. Решение почти всегда очевидно после этих трёх чисел, а не из абстрактного «что мощнее».
Если пайплайн упрётся в производительность на потоке изображений — смотрите в сторону libvips: она спроектирована под потоковую обработку с низким потреблением памяти, и это отдельный разговор, выходящий за рамки выбора между четырьмя вариантами выше.
Редактор изображений: Убрать лишнее, заменить фон, добавить объект, расширить кадр — на вашем изображении.
Спросить за 20 ₽Источники: nothings/stb — репозиторий одиночных заголовков, OpenCV documentation
Частые вопросы
А если мне нужен только ресайз и ничего больше?
У меня OpenCV 3, а не 4 — примеры подойдут?
Почему после сборки stb линкер ругается на multiple definition?
Фотографии со смартфона сохраняются повёрнутыми — это баг библиотеки?
Что выбрать, если сейчас нужен только ресайз, но проект может вырасти в распознавание?
Годятся ли эти библиотеки для обработки на потоке из тысяч изображений?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.