Как я могу вернуть мат opencv. js на c ++ - PullRequest
0 голосов
/ 10 апреля 2020

При написании веб-приложения для браузера (подтверждение концепции wasm), я хочу вернуть циновку, снятую веб-камерой, обратно на сайт c ++, чтобы обработать изображение и отобразить его (пользовательский интерфейс написан Qt5).

Как я мог вернуть коврик? Решение, которое я нашел, это

  1. итерация пикселей по img.ucharPtr, копирование значения пикселей в строку и возврат его в c ++. Но итерация пикселя на js медленная, не идеальное решение.
  2. Используйте imencode для кодирования мата в jpg и возврата его, проблема в том, что когда я вызываю imencode, это выдает мне сообщение об ошибке "cv .imencode не является функцией ". Я загружаю opencv. js с по этой ссылке, это официальный сайт .

    function captureFrame()
    {
        console.log("capture frame start");
        console.log("cols = ", global_frame.cols, ", rows = ",  global_frame.rows, ", type = ", global_frame.type(), ", steps = ", global_frame.step[0]);
        global_cap.read(global_frame);  // Read a frame from camera
        console.log("convert from rgba 2 rgb");
        cv.cvtColor(global_frame, global_rgb_frame, cv.COLOR_RGBA2RGB);
    
        console.log("convert to byte64 string");
        var base64_frame = cv.imencode(".jpg", global_rgb_frame).toString('base64');
        var length_bytes = lengthBytesUTF8(base64_frame);
        var string_on_wasm_heap = _malloc(length_bytes);
        stringToUTF8(base64_frame, string_on_wasm_heap, length_bytes);
    
        return string_on_wasm_heap;
    }
    

Было бы идеально, если бы я мог открыть веб-камеру и получить доступ фрейм непосредственно из c ++, но пока не нашел способа сделать это.

Ответы [ 2 ]

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

Я нашел решение, довольно простое.

function captureFrame()
{
    console.log("capture frame start");
    console.log("cols = ", global_frame.cols, ", rows = ", global_frame.rows, ", type = ", global_frame.type(), ", steps = ", global_frame.step[0]);
    global_cap.read(global_frame);

    console.log("convert from rgba 2 rgb");
    cv.cvtColor(global_frame, global_rgb_frame, cv.COLOR_RGBA2RGB);

    HEAPU8.set(global_rgb_frame.data, global_buffer);
    console.log("buffer values = ", buffer[0], ", ", buffer[1], ", ", buffer[2]);
    console.log("global_rgb_frame.data values = ", global_rgb_frame.data[0], ", ", global_rgb_frame.data[1], ", ", global_rgb_frame.data[2]);        

    return global_buffer;    
}

global_buffer - это куча, которую я запускаю при запуске камеры, чтобы уменьшить необходимость выделения памяти, я делаю ее как глобальную переменную, этот уродливый прием часто встречается, когда мне нужно общаться на разных языках.

try{
        // Get a permission from user to use a camera.
        navigator.mediaDevices.getUserMedia(constraints)
          .then(function(stream) {
              global_camera.srcObject = stream;
              let {width, height} = stream.getTracks()[0].getSettings();
              global_height = height;
              global_width = width;
              console.log("width x height of stream = ", width, "," , height);
              global_frame = new cv.Mat(height, width, cv.CV_8UC4);
              global_rgb_frame = new cv.Mat(height, width, cv.CV_8UC3);
              global_camera.setAttribute("width", width);
              global_camera.setAttribute("height", height);
              global_buffer = _malloc(global_rgb_frame.rows * global_rgb_frame.cols * 3);

              global_camera.onloadedmetadata = function(e) {                  
                  global_camera.play();
                  //! [Open a camera stream]
                  global_cap = new cv.VideoCapture(global_camera);  //*/
          };
        });        
    }catch(err){
        console.log("err:", err, ", err messages:", err.message);
    }

После этого зарегистрируйте функцию в файле cpp.

EM_JS(unsigned char*, capture_frame, (), {
    return captureFrame();
})

Преобразование буфера к QImage и нарисуйте его на QLabel

    auto *buffer = capture_frame();
    auto const width = get_frame_width(); //access js global variable
    auto const height = get_frame_height(); //access js global variable
    qDebug()<<__func__<<"width x height = "<<width<<", "<<height;
    qDebug()<<__func__<<"buffer values = "<<buffer[0]<<", "<<buffer[1]<<", "<<buffer[2];
    cv::Mat frame(height, width, CV_8UC3, buffer);
    QImage img(frame.data, frame.cols, frame.rows,
               static_cast<int>(frame.step[0]), QImage::Format_RGB888);
    ui->labelImage->setPixmap(QPixmap::fromImage(img.copy()));
0 голосов
/ 10 апреля 2020

Если я понимаю, о чем вы просите, вы можете использовать код ниже, который я нашел из этой темы Opencv .

Просто возьмите экранную шапку с последним кадром из видео захватить и вернуть его как объект mat.

VideoCapture cap("video1.mp4"); 

if( !cap.isOpened())
{
     cout << "Cannot open the video file" << endl;
     return -1;
}

double count = cap.get(CV_CAP_PROP_FRAME_COUNT); //get the frame count
cap.set(CV_CAP_PROP_POS_FRAMES,count-1); //Set index to last frame
namedWindow("Screen Cap", CV_WINDOW_AUTOSIZE);

while(1)
{
    Mat frame;
    bool success = cap.read(frame); 
    if (!success){
      cout << "Cannot read  frame " << endl;
      break;
    }
    imshow("MyVideo", frame);
    if(waitKey(0) == 27) break;
}

Если вы хотите провести дальнейший углубленный анализ, я нашел эти документы здесь.

...