Создание видео MJEPG из нескольких изображений в формате JPEG без использования cv :: imdecode () - PullRequest
1 голос
/ 10 апреля 2020

Мне нужно хранить несколько закодированных кадров в памяти.
Я использую cv::imencode(".jpg", ...) для сжатия и сохранения закодированных изображений в std::list<std::vector<u_char>> compressed_images - список сжатых изображений.

Я хочу создать видео из compressed_images, но я должен использовать cv::imdecode(), чтобы декодировать все изображения в cv::Mat, а затем cv::VideoWriter, чтобы сохранить изображения в видео MJPEG.

Могу ли я пропустить cv::imdecode() или использовать другое решение, чтобы избежать кодирования два раза?

1 Ответ

1 голос
/ 11 апреля 2020

Вы можете ВСТАВИТЬ закодированные изображения в FFmpeg.

Согласно следующему сообщению , вы можете "просто смешать изображения JEPG для создания видео".

Если кадры находятся в памяти, вы можете записать закодированные изображения на входную ТРУБУ FFmpeg.

Вместо -f image2 используйте флаг формата -f image2pipe.

Реализация решения на C ++ слишком сложна для меня.
Я реализовал Python пример кода.

Пример кода:

  • Создает список из 100 закодированных кадров (счетчик зеленой рамки).
  • ТРУБА закодированных кадров в подпроцесс ffmpeg.
    Закодированные изображения записываются во stdin входной поток подпроцесса.

Вот Python пример кода:

import numpy as np
import cv2
import subprocess as sp

# Generate 100 synthetic JPEG encoded images in memory:
###############################################################################
# List of JPEG encoded frames.
jpeg_frames = []

width, height, n_frames = 640, 480, 100  # 100 frames, resolution 640x480

for i in range(n_frames):
    img = np.full((height, width, 3), 60, np.uint8)
    cv2.putText(img, str(i+1), (width//2-100*len(str(i+1)), height//2+100), cv2.FONT_HERSHEY_DUPLEX, 10, (30, 255, 30), 20)  # Green number

    # JPEG Encode img into jpeg_img
    _, jpeg_img = cv2.imencode('.JPEG', img)

    # Append encoded image to list.
    jpeg_frames.append(jpeg_img)
###############################################################################

#FFmpeg input PIPE: JPEG encoded images
#FFmpeg output AVI file encoded with MJPEG codec.
# https://video.stackexchange.com/questions/7903/how-to-losslessly-encode-a-jpg-image-sequence-to-a-video-in-ffmpeg
process = sp.Popen('ffmpeg -y -f image2pipe -r 10 -i pipe: -codec copy out.avi', stdin=sp.PIPE)

# Iterate list of encoded frames and write the encoded frames to process.stdin
for jpeg_img in jpeg_frames:
    process.stdin.write(jpeg_img)

# Close and flush stdin
process.stdin.close()

# Wait one more second and terminate the sub-process
try:
    process.wait(1)
except (sp.TimeoutExpired):
    process.kill()

Обновление: реализация C ++:

#include "opencv2/opencv.hpp"
#include "opencv2/highgui.hpp"

#ifdef _MSC_VER
#include <Windows.h>    //For Sleep(1000)
#endif

#include <stdio.h>

int main()
{
    int width = 640;
    int height = 480;
    int n_frames = 100;

    //Generate 100 synthetic JPEG encoded images in memory:
    //////////////////////////////////////////////////////////////////////////
    std::list<std::vector<uchar>> jpeg_frames;

    for (int i = 0; i < n_frames; i++)
    {
        cv::Mat img = cv::Mat(height, width, CV_8UC3);
        img = cv::Scalar(60, 60, 60);

        cv::putText(img, std::to_string(i + 1), cv::Point(width / 2 - 100 * (int)(std::to_string(i + 1).length()), height / 2 + 100), cv::FONT_HERSHEY_DUPLEX, 10, cv::Scalar(30, 255, 30), 20);  // Green number

        //cv::imshow("img", img);cv::waitKey(1);

        std::vector<uchar> jpeg_img;

        cv::imencode(".JPEG", img, jpeg_img);

        jpeg_frames.push_back(jpeg_img);
    }
    //////////////////////////////////////////////////////////////////////////

    //In Windows (using Visual Studio) we need to use _popen and in Linux popen
#ifdef _MSC_VER
    //ffmpeg.exe must be in the system path (or in the working directory)
    FILE *pipeout = _popen("ffmpeg -y -f image2pipe -r 10 -i pipe: -codec copy out.avi", "wb"); //For Windows use "wb"
#else
    //https://batchloaf.wordpress.com/2017/02/12/a-simple-way-to-read-and-write-audio-and-video-files-in-c-using-ffmpeg-part-2-video/
    FILE *pipeout = popen("ffmpeg -y -f image2pipe -r 10 -i pipe: -codec copy out.avi", "w"); //For Linux use "w"

    //In case ffmpeg is not in the execution path, you may use full path:
    //popen("/usr/bin/ffmpeg -y -f image2pipe -r 10 -i pipe: -codec copy out.avi", "w");
#endif

    std::list<std::vector<uchar>>::iterator it;

    //Iterate list of encoded frames and write the encoded frames to pipeout
    for (it = jpeg_frames.begin(); it != jpeg_frames.end(); ++it) 
    {
        std::vector<uchar> jpeg_img = *it;

        // Write this frame to the output pipe
        fwrite(jpeg_img.data(), 1, jpeg_img.size(), pipeout);
    }

    // Flush and close input and output pipes
    fflush(pipeout);

#ifdef _MSC_VER
    _pclose(pipeout);
#else
    pclose(pipeout);
#endif

    //It looks like we need to wait one more second at the end.
    Sleep(1000);

    return 0;
}
...