c++ - 如何使用opencv和c++从视频文件中每秒捕获图像?

标签 c++ visual-studio opencv video

我在设计程序时遇到过允许每秒从视频文件(avi、mp4 等...)中捕获图像的问题。

首先,我能够从视频文件中逐帧捕捉图像。 其次,我能够同时分析同一文件夹中图像的像素颜色值,并将像素值保存在 txt 文件中。

这里我遇到了一些问题。我现在正试图同时组合这两个代码,但我得到了奇怪的结果。我引用下面的代码。

int main(){
VideoCapture cap("D:\\data\\extra\\video200ul.avi"); 
if (!cap.isOpened())  
    return -1;

Ptr<BackgroundSubtractor> pMOG2 = createBackgroundSubtractorMOG2(20, 16, true);

Mat fg_mask;
Mat frame;
int count = 0;

String name, folder;

for (;;) {
    // Get frame
    cap >> frame; // get a new frame from video
    ++count;
    // Update counter

    // Background subtraction
    if (count % 2 == 0) {
        pMOG2->apply(frame, fg_mask, 0.001);

        cout << count << endl;

        if (!frame.empty()) {
            imshow("frame", frame);
            //      imshow("fg_mask", fg_mask);
        }
        // Save foreground mask
        name = "mask" + std::to_string(count) + ".png";
        //      string name = "mask_" + std::to_string(static_cast<long long>(count) + ".png";
        folder = imwrite("D:\\data\\extra\\" + name, frame);

    }
    anal(folder);
}   
waitKey(0);
return 0;

首先,我上面写的代码用于从视频文件中逐帧捕获图像。但是,如果我按帧获取图像,我的文件夹中就会有很 multimap 片,所以我想每秒从视频文件中捕获一张图像。我尝试使用 CV_CAP_PROP_POS_MSEC 而不是使用 cap << frame,但它对我不起作用。

其次,当我将此代码合并到我在下面编写的另一个代码时,它显示了一些错误消息,例如“libpng 警告图像宽度、长度、数据在 ihdr 中为零。”

int anal(String folder) {

folder = "D:\\data\\extra\\*.png"; 
vector<String> filenames;

glob(folder, filenames);

cv::Mat ori_image;

for (size_t i = 0; i < filenames.size(); ++i) {

    ori_image = imread(filenames[i], IMREAD_COLOR);

    if (ori_image.empty()) {
        cout << "Check your file again." << std::endl;
        return -1;
    }

    rectangle(ori_image, Point(215, 98), Point(245, 110), Scalar(0, 255, 255), 1); 

    imshow("Original Image", ori_image);

    cv::Scalar sums;
    sums = cv::sum(ori_image);

    double totalSum = sums[0] + sums[1] + sums[2];

    if (totalSum <= 0) {
        cout << "$$ RGB percentage $$" << " \n\n";
        cout << "R: " << 100.0 / 3 << " % \n";
        cout << "G: " << 100.0 / 3 << " % \n";
        cout << "B: " << 100.0 / 3 << " % \n\n";
    }
    else {
        cout << "$$ RGB percentage $$" << " \n\n"; // red value
        cout << "R: " << sums[2] / totalSum * 100 << " % \n"; // red value
        cout << "G: " << sums[1] / totalSum * 100 << " % \n"; // green value
        cout << "B: " << sums[0] / totalSum * 100 << " % \n\n"; // blue value
    }

当我准备上面的代码时,我试图计算视频中所有捕获图像的红色、蓝色、绿色百分比。但是,当我将这两个代码分开并运行它们时,它们运行良好,但是如果我将它们合并在一起,它会显示错误消息。

我想结合这两个代码来分析视频中每秒捕获的图像的颜色值。

请帮我解决这个问题。

提前谢谢你。

------------编辑部分--------------------

我使用了你的修改版本并应用到我更新的代码中,

void imageAnalysis(std::string folder, cv::Mat frame){
cv::Mat ori_image = frame.clone();
std::string path = folder;
cv::rectangle(ori_image, Point(215, 105), Point(245, 120), Scalar(0, 255, 255), 1);

cv::imshow("Original Image", ori_image);
cv::waitKey(1);


String folder = "D:\\data\\dfdf\\*.png";
vector<String> filenames;
cv::glob(path, filenames);

for (size_t t = 0; t < filenames.size(); t++) {
    ori_image = imread(filenames[t], IMREAD_COLOR); // ori_image

    if (ori_image.empty()) {    //ori_image
        cout << "Check your file again." << "\n";
        break;
        //return -1;
    }

    rectangle(ori_image, Point(215, 105), Point(245, 120), Scalar(0, 255, 255), 1); 
    imshow("Original Image", ori_image);
    cv::waitKey(1);

    Mat image_HSV;
    cvtColor(ori_image, image_HSV, CV_BGR2HSV);

    double h = 0.0;
    double s = 0.0;
    double v = 0.0;

    int col = image_HSV.cols; // 480
    int row = image_HSV.rows; // 272

    int corow = ((col - 235) - 215) * ((row - 152) - 108);  

    Mat mask;
    inRange(image_HSV, Scalar(100, 0, 0), Scalar(100, 255, 255), mask); // convert binary
    image_HSV.setTo(Scalar(0, 0, 0), mask);

    for (int i = 108; i < row - 152; i++) {     
        for (int j = 215; j < col - 235; j++) {
            Vec3b hsv = image_HSV.at<cv::Vec3b>(i, j);

            h += (int)(hsv.val[0]);
            s += (int)(hsv.val[1]);
            v += (int)(hsv.val[2]);

            if (hsv[0] != 100) {
                hsv[0] = 0;
                hsv[1] = 0;
                hsv[2] = 0;
            }
        }
    }

    cout << "$$ Hue(H), Saturation(S), Brightness(V) $$" << filenames[t] << " !! \n\n";
    cout << "H: " << h / corow * 360 / 180 << " % \n";  // 
    cout << "S: " << s / corow * 100 / 255 << " % \n";
    cout << "V: " << v / corow * 100 / 255 << " % \n\n";

    std::ofstream file("D:\\data\\dfdf\\result_4.txt", std::ios_base::app);
    file << v / corow * 100 / 255 << " \n"; // v value
    file.close();
}   

正如您所见的 imageAnalysis() 函数,我添加了 std::string 文件夹作为从视频剪辑中提取图像的路径。然而,当我应用这段代码时,我得到了如下所示的非常奇怪的结果。

enter image description here

我原以为我应该从每 24 张图像中获取颜色值,但正如您在上面看到的结果,我以随机顺序从所有图像中获取颜色值。

提前谢谢你。

学习如何以高效的方式编写代码真是太好了!

最佳答案

只是为了清除您在评论中提到的关于 CV_CAP_PROP_POS_MSEC 的错误:

when I apply CV_CAP_PROP_POS_MSEC to my code, I found some error messages like "CV_CAP_PROP_POS_MSEC is not defined."

许多常量值都在 OpenCV 中限定范围。这意味着,CV_CAP_PROP_POS_MSEC 未定义,但 cv::CV_CAP_PROP_POS_MSEC 已定义。您还可以使用 cv::CAP_PROP_FPS 获取 FPS。

现在对于你的代码,我实际上会做一些不需要保存和加载图像的事情,而是传递要处理的图像,如下所示:

#include "opencv2/opencv.hpp"
#include <iostream>

int main(){
  cv::VideoCapture cap("D:\\data\\extra\\video200ul.avi"); 
  if (!cap.isOpened())  
  {
    std::cout << "Could not open video" << std::endl;
    return -1;
  }

  cv::Ptr<cv::BackgroundSubtractor> pMOG2 = cv::createBackgroundSubtractorMOG2(20, 16, true);

  cv::Mat fg_mask, frame;
  int count = 0;
  const int fps = 24; // you may set here the fps or get them from the video

  std::string name, folder;

  // with cap.read you can check already if the video ended
  while (cap.read(frame)) {
    // Background subtraction
    if (count % fps == 0) {
        pMOG2->apply(frame, fg_mask, 0.001);
        // Save foreground mask
        name = "mask" + std::to_string(count) + ".png";
        bool result = cv::imwrite("D:\\data\\extra\\" + name, frame);
        imageAnalysis(frame, count);
    }
    // at the end of the loop so that the first image is used
    ++count;
  }   
  cv::waitKey(0);
  return 0;
}

imageAnalysis 函数定义为:

// You can pass cv::Mat as value, it is almost like a smart pointer
void imageAnalysis(cv::Mat frame, int count)
{
  cv::Mat ori_image = frame.clone();
  cv::rectangle(ori_image, Point(215, 98), Point(245, 110), Scalar(0, 255, 255), 1); 
  // each imshow needs a waitKey to update the window in which it is being shown
  cv::imshow("Original Image", ori_image);
  cv::waitKey(1);

  cv::Scalar sums;
  sums = cv::sum(ori_image);
  double totalSum = sums[0] + sums[1] + sums[2];

  std::ofstream output("D:\\data\\extra\\mask" + std::to_string(count) + ".txt");
  if (totalSum <= 0) 
  {
    std::cout << "$$ RGB percentage $$" << std::endl << std::endl;
    std::cout << "R: " << 100.0 / 3 << std::endl;
    std::cout << "G: " << 100.0 / 3 << std::endl;
    std::cout << "B: " << 100.0 / 3 << std::endl << std::endl;
    output << "$$ RGB percentage $$" << std::endl << std::endl;
    output << "R: " << 100.0 / 3 << std::endl;
    output << "G: " << 100.0 / 3 << std::endl;
    output << "B: " << 100.0 / 3 << std::endl << std::endl;
  }
else {
    std::cout << "$$ RGB percentage $$" << std::endl << std::endl;
    std::cout << "R: " << sums[2] / totalSum * 100 << std::endl; // red value
    std::cout << "G: " << sums[1] / totalSum * 100 << std::endl; // green value
    std::cout << "B: " << sums[0] / totalSum * 100 << std::endl << std::endl; // blue value
    output  << "$$ RGB percentage $$" << std::endl << std::endl;
    output  << "R: " << sums[2] / totalSum * 100 << std::endl; // red value
    output  << "G: " << sums[1] / totalSum * 100 << std::endl; // green value
    output  << "B: " << sums[0] / totalSum * 100 << std::endl << std::endl; // blue value
  }
}

上面代码的一些注释,我将cap >> frame替换为cap.read(frame)。它具有相同的功能,但如果无法抓取图像(例如视频结束),后者会给出一个 bool 结果,该结果为 false。我更改了最后添加的计数,你得到帧 0,23,... 这样第一个也将被使用。最后,您应该使用命名空间 cv::std:: 等。这只是最佳实践,它避免了某些库可能出现的歧义和问题。

如果您不需要磁盘中的图像,而只需要分析,则删除保存部分并将每一帧传递给 imageAnalysis 函数,这样您可以有更多的数据用于统计。此外,考虑在函数中返回 cv:Scalar of sums,然后您可以对整个秒或整个视频进行一些统计。

如果您有任何问题,请随时在评论中提问。

关于c++ - 如何使用opencv和c++从视频文件中每秒捕获图像?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54292656/

相关文章:

visual-studio - 如何从Powershell创建WSP包

python - 从图像中提取信息

python - 傅立叶变换的逆给出 “data type not supported”错误

c++ - 如何使格式标志在 C++ 中不绑定(bind)?

c# - WPF xaml Visual Studio 不断自动更改到我的资源的路径

c++ - 根据参数创建数组

android - Xamarin + Visual Studio 2015

opencv - 咖啡 bean 分离算法

c++ - 使用结构体初始化 OpenGL VBO

c++ - 多项式运算