По работе я занимаюсь разработкой алгоритмов обработки изображений и в частности алгоритмами автоматического слежения за объектами на видео для специального применения. Недавно понадобилось сделать модель алгоритма, управляемую с удаленного компьютера для отладки логики работы в сложной системе. Раньше такая задача не стояла, т.к. все алгоритмы реализовывались в итоге на FPGA. Давно работаю с OpenCV и, потерев руки, подошел к написанию программы. Но энтузиазм быстро погас, когда столкнулся непосредственно с передачей видео по сети.
Задача заключалась в следующем:
1. Написать программу сервер, которая загружает видео из файла, сжимает в JPEG и передает по протоколу TCP программе клиенту.
2. Написать программу клиент, которая принимает видео по TCP, декодирует и отображает.
Описанные выше задачи являются элементарными и служат для того, чтобы «отработать» технологию. Кажется, что эта тема уже описана давно, но после некоторого времени в поиске готового ответа (куска кода) я понял, что не все очевидно. Поэтому здесь я привожу свой опыт в этом вопросе. Возможно кому-то мой опыт окажется полезным.
Программу разрабатывал в среде Visual Studio 2015 с использование библиотеки OpenCV версии 3.1. Опущу этапы создания проекта, подключения библиотек и написание кода, отвечающего за сетевое взаимодействие. В конце статьи дам ссылку на исходные коды проекта с полными коментариями в коде для быстрого понимания. Сосредоточимся на главной проблеме: как получить видео из файла, сжать его в JPEG с нужной степенью компрессии и передать по сети с дальнейшим декодированием и отображением на приемной стороне. Ниже небольшой кусок кода, показывающий как сжать кадр видео и передать его по сети (с учетом того, что соединение с приемной стороной установлено).
Теперь опишем как распаковать изображение на приемной стороне. Ниже фрагмент кода программы клиента.
Описанные операции производятся для каждого кадра видео. Надеюсь этот небольшой пример поможет решить возникающие трудности сжатия и передачи потокового видео через TCP используя библиотеку OpenCV. Ниже по ссылке скриншот работы клиента и сервера.
www.dropbox.com/s/ikisl8rjxxd5d0f/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png
Ввиду того, что коэффициент сжатия стоит 20 (высокое сжатие) на приемной стороне можно заметить значительные искажения картинки.
Исходные коды программ сервера и клиента с подробными комментариями вы можете посмотреть по ссылкам:
www.dropbox.com/s/3ucjsdes7khcr24/Server.cpp?dl=0
www.dropbox.com/s/14mat8bhlonz392/Client.cpp?dl=0
Удачи во всех начинаниях!
Задача заключалась в следующем:
1. Написать программу сервер, которая загружает видео из файла, сжимает в JPEG и передает по протоколу TCP программе клиенту.
2. Написать программу клиент, которая принимает видео по TCP, декодирует и отображает.
Описанные выше задачи являются элементарными и служат для того, чтобы «отработать» технологию. Кажется, что эта тема уже описана давно, но после некоторого времени в поиске готового ответа (куска кода) я понял, что не все очевидно. Поэтому здесь я привожу свой опыт в этом вопросе. Возможно кому-то мой опыт окажется полезным.
Программу разрабатывал в среде Visual Studio 2015 с использование библиотеки OpenCV версии 3.1. Опущу этапы создания проекта, подключения библиотек и написание кода, отвечающего за сетевое взаимодействие. В конце статьи дам ссылку на исходные коды проекта с полными коментариями в коде для быстрого понимания. Сосредоточимся на главной проблеме: как получить видео из файла, сжать его в JPEG с нужной степенью компрессии и передать по сети с дальнейшим декодированием и отображением на приемной стороне. Ниже небольшой кусок кода, показывающий как сжать кадр видео и передать его по сети (с учетом того, что соединение с приемной стороной установлено).
// Объявляем переменные
Mat srcMat; // Данные исходного изображения
vector<uchar> imgBuf; // Буфер для сжатого изображения
vector<int> quality_params = vector<int>(2); // Вектор параметров качества сжатия
quality_params[0] = CV_IMWRITE_JPEG_QUALITY; // Кодек JPEG
quality_params[1] = 20; // По умолчанию качество сжатия (95) 0-100
// Захватываем кадр видео
cd.frame = cvQueryFrame(cd.videocap);
// Получаем изображение в вектор
srcMat = cv::cvarrToMat(cd.frame);
// Кодируем изображение кодеком JPEG
imencode(".jpg", srcMat, imgBuf, quality_params);
// Отправляем данные
send(clientSocket, (const char*)&imgBuf[0], static_cast<int>(imgBuf.size()), 0);
Теперь опишем как распаковать изображение на приемной стороне. Ниже фрагмент кода программы клиента.
// Объявляем переменные
int iResult; // Переменная на результат операций
const int MAX_BUF_SIZE = 2073600; // Произвольно максимальный размер приемного буфера
unsigned char *buf = new unsigned char[MAX_BUF_SIZE]; // Буфер для прима сообщений
vector<uchar> videoBuffer; // Буфер данных изображения
Mat jpegimage; // Вектор данных изображения
IplImage img; // Изображение для вывода
// Ожидаем прихода данных
iResult = recv(connectSocket, (char *)&buf[0], MAX_BUF_SIZE, 0);
if (iResult > 0) {
// Если пришли данные изображения, копируем их
videoBuffer.resize(iResult);
memcpy((char*)(&videoBuffer[0]), buf, iResult);
// Декодируем данные
jpegimage = imdecode(Mat(videoBuffer), CV_LOAD_IMAGE_COLOR);
img = jpegimage;
// Выводим изображение
cvShowImage("Recieved Video", &img);
// Ожидаем отклика управления (произвольно 5 ms)
cvWaitKey(5);
}//if (iResult > 0)...
Описанные операции производятся для каждого кадра видео. Надеюсь этот небольшой пример поможет решить возникающие трудности сжатия и передачи потокового видео через TCP используя библиотеку OpenCV. Ниже по ссылке скриншот работы клиента и сервера.
www.dropbox.com/s/ikisl8rjxxd5d0f/%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9.png
Ввиду того, что коэффициент сжатия стоит 20 (высокое сжатие) на приемной стороне можно заметить значительные искажения картинки.
Исходные коды программ сервера и клиента с подробными комментариями вы можете посмотреть по ссылкам:
www.dropbox.com/s/3ucjsdes7khcr24/Server.cpp?dl=0
www.dropbox.com/s/14mat8bhlonz392/Client.cpp?dl=0
Удачи во всех начинаниях!