Ас-саляму алейкум, братья!
В этом посте хочу показать вам как при помощи программного кода обрабатывать изображение и видео. Материал разобью на 4 шага, работать будем на Java. Для начала потребуется поставить библиотеку openCV, которую можно скачать на официальном сайте ' https://opencv.org/ ', более подробная инструкция в прикреплённом видео.
Шаг 1: [Обработка изображения]
Давайте создадим всплывающее окно для отображения изображений.
// Создаём окно для просмотра изображения.
JFrame window = new JFrame("Window:");
// Создаём контейнер для изображения.
JLabel screen = new JLabel();
// Установлимаем операцию закрытия по умолчанию.
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Делаем окно отображения контента видимым.
window.setVisible(true);
Подгрузим изображение прописав путь к нему в метод Imgcodecs.imread()
// Загружаем изображение, храним его как объект матрицы.
Mat img = Imgcodecs.imread("src\\a_Image\\butterfly.png");
Производим поток преобразований над изображением, после чего выводим его в окне созданном выше.
/* Преобразуем изображение в матрицу байтов с целью
получить массив байтов (пикселей). */
MatOfByte buf = new MatOfByte();
Imgcodecs.imencode(".png", img, buf);
/* Преобразуем массив пикселей в ImageIcon,
изображение которое будет отображатся. */
ImageIcon ic = new ImageIcon(buf.toArray());
// Привязываем изображение к контейнеру.
screen.setIcon(ic);
// Привязываем контейнер к окну отображения.
window.getContentPane().add(screen);
window.pack();
Поздравляю! Первый навык разблокирован, теперь мы можем отобразить .jpg в всплывающем окне при помощи джавы. Давайте на этом не будем останавливаться и отредактируем .jpg.
Редактировать .jpg будем при помощи матрицы трансформаций (kernel). Суть состоит в следующем, представим kernel как прямоугольную матрицу, каждый элемент это множитель, разные наборы множителей вызывают разные эфекты. Kernel проходит через все элементы .jpg, домнажает их на себя, тем самым изменяя .jpg. Более наглядно можно посмотреть на картинках.
// Создаём ядро (kernel) для обработки изображения (матрица трансформации).
Mat kernel = new Mat (20,20, CvType.CV_8UC1, new Scalar(1.0));
Теперь на основе kernel мы можем использовать фильтры, эфекты и искожения. Обработываемое фото можно менять по размеру, обрезать и сохранить.
// Расширяем светлые и сужаем тёмные области.
Imgproc.dilate(img, imgEmpty, kernel);
// Расширяем тёмные и сужаем светлые области.
Imgproc.erode(img, imgEmpty, kernel);
// Конвертируем изображение в другое цветовое пространство.
Imgproc.cvtColor(img, imgEmpty, Imgproc.COLOR_BGR2GRAY);
// Размываем изображение (параметры ядра должны быть нечетными).
Imgproc.GaussianBlur(img, imgEmpty, new Size (15,15), 0);
// Выделяем границы изображения.
Imgproc.Canny(img, imgEmpty, 2,2);
// Изменяем размер изображения.
Imgproc.resize(img, imgEmpty, new Size(200,200));
// Обрезаем изображение.
Mat imgCrop = img.colRange(400,600).rowRange(200,400);
// Обрабатываем часть изображения.
Imgproc.erode(img.colRange(400,600).rowRange(200,400), img.colRange(400,600).rowRange(200,400), kernel);
// Сохраняем изображение.
Imgcodecs.imwrite("src\\a_Image\\savedImg.png", img);
Шаг 2: [Обработка видео и видеопотока]
Для работы с видео создадим объект класса VideoCapture(). Его функционал позволит отображать изображение с веб-камеры и воспроизводить видеозаписи. В качестве аргумента передаём на вход путь к видеофайлу или id веб-камеры (default: 0).
/* Инициализируем видеопоток. Класс VideoCapture предназначен для захвата кадра
из видео или видеопотока. */
VideoCapture cap = new VideoCapture("src/b_Video/testVideo.mp4");
Обращаясь к объекту класса VideoCapture() в цикле поочередно извлекаем кадры, присваиваем матрице frame значение текущего кадра.
// До тех пор пока поступает кадр из видеопотока выполняем следующие действия:
while (cap.grab()) {
// Извлекаем кадр из видеопотока.
cap.read(frame);
Вывод всплывающего окна следует проводить в цикле, так же используя следующие функции можно нарисрвать линию, прямоугольник, круг и поместить на видео текст:
// Инициализируем цвет (BGR)
Scalar color = new Scalar(0,255,0);
/* Наносим линию на изображение.
Обрабатываемое изображение, начальная точкаб конечная точкаб цвет, толщина линии */
Imgproc.line(frame, new Point(0,0), new Point(640,480), color,2);
//
Imgproc.rectangle(frame, new Rect(200, 100,240, 280), new Scalar(255, 0, 0),3);
//
Imgproc.circle(frame, new Point(320,240), 120, new Scalar(0, 0, 255), 5);
//
Imgproc.putText(frame, "SDBproduction", new Point(150,50),
4, 1.5, new Scalar(5, 5, 5));
Шаг 3: [Стриминг видео "UDP Socket"]
Для передачи видеопотока по локальной сети мы выстроим следующую логику работы. Сервер всегда находится в режиме ожидания. Клиент может подключиться к серверу и начать передавать видео, в этом случае сервер переключится в режим приёма видео от клиента, как только клиент отключится сервер уйдет в режим ожидания.
Давайте пропишим код для клиента:
Создаём соединение со стороны клиента.
/* Инициализируем видеопоток. Класс VideoCapture предназначен для захвата кадра
из видео или видеопотока. */
VideoCapture cap = new VideoCapture(1);
// Создаём сокет, точку соединения между двумя компьютерами.
Socket socket = new Socket("192.168.1.159",1234);
/* Создаём объект DataOutputStream который связываем с нашим сокетом.
Данный объект позволяет отправлять примитивные типы данных. */
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
Каждый кадр конвертируем в примитивный тип данных и отправляем на сервер.
// До тех пор пока поступает кадр из видеопотока выполняем следующие действия:
while (cap.grab()) {
try {
// Извлекаем кадр из видеопотока.
cap.read(frame);
/* Преобразуем изображение в матрицу байтов с целью
получить массив байтов (пикселей). */
Imgcodecs.imencode(".png", frame, buf);
imageData = buf.toArray();
// Извлекаем размер изображения.
int dataLength = imageData.length;
// Отправляем размер изображения.
dataOutputStream.writeInt(dataLength);
/* Отправляем изображение в виде массива байтов (пикселей).
"последовательность байтов" */
dataOutputStream.write(imageData);
Сервер со своей стороны состоит из двух циклов. В первом цикле ожидаем подключения к серверу от клиента. Если подключение установлено, то внутри этого цикла запускается второй, который будет принимать видеопоток от пользователя и выводить его.
// Создаём серверный сокет, который будет слушать на заданом порту.
ServerSocket serverSocket = new ServerSocket(1234);
// В бесконечном цикле ожидаем подключение к серверу.
while (true) {
try {
/* Ожидаем клиента, если придёт запрос от клиента создаём сокет (точку соединения)
чтобы подключится. */
Socket socket = serverSocket.accept();
/* Создаём объект DataInputStream который связываем с нашим сокетом.
Данный объект позволяет принимать примитивные типы данных. */
DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
// В бесконечном цикле принимаем данные от клиента.
while (true) {
// Принимаем размер изображения.
int dataLength = dataInputStream.readInt();
// Если изображение существует, то
if (dataLength > 0) {
/* принимаем изображение в виде массива байтов (пикселей).
"последовательность байтов" */
byte[] imageData = new byte [dataLength];
dataInputStream.readFully(imageData,0,dataLength);
/* Преобразуем массив пикселей в ImageIcon,
изображение которое будет отображатся. */
ic = new ImageIcon(imageData);
// Привязываем изображение к контейнеру.
screen.setIcon(ic);
// Привязываем контейнер к окну отображения.
window.setContentPane(screen);
window.pack();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Шаг 4: [Детекция объектов " YOLOv4 "]
Свёрточная нейронная сеть [скачать]
Чтобы разобраться с детекцией объектов на джава предлагаю вам посмотреть мой видеоурок.
Огромное спасибо за внимание!
Комментарии (8)
Heliki
25.10.2021 17:21+2Почему сама работа с OpenCV вынесена за пределы статьи? В текущем виде (с тем кодом, который приведен) было бы логичнее назвать ее "рисуем окно и захватываем видеопоток".
TimurBaldin
28.10.2021 07:35+1Отрадно видеть для подобных задач реализацию на родной Java )А то python порядком надоел)
mtop
ссылочка то дохлая на просторы 404 ведет
dsb42 Автор
Извиняюсь, не знаю в чем проблема. Скачать можно тут:
github.com/AlexeyAB/darknet/wiki/YOLOv4-model-zoo
AlexeyAB
OpenCV >= 4.5.4 и также поддерживает все эти Scaled-YOLOv4 модели: https://github.com/AlexeyAB/darknet#pre-trained-models
maquefel
А вот спасибо за ссылку, добрый человек.