[英]how to capture images per second from video files by using opencv and c++?
我在設計程序時遇到過允許每秒從視頻文件(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;
}
首先,我上面寫的代碼是用於從視頻文件中逐幀捕獲圖像。 但是,如果我每幀獲取圖像,我的文件夾中就會有很多圖片,所以我想每秒從視頻文件中捕獲一個圖像。 我曾嘗試使用 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 文件夾。 但是,當我應用此代碼時,我得到了如下所示的非常奇怪的結果。
我以為我應該從每 24 張圖像中獲取顏色值,但是正如您在上面看到的結果,我以隨機順序從所有圖像中獲取了顏色值。
先感謝您。
學習如何以有效的方式編碼真是太好了!!
只是為了清除您在評論中提到的關於CV_CAP_PROP_POS_MSEC
的錯誤:
當我將 CV_CAP_PROP_POS_MSEC 應用於我的代碼時,我發現了一些錯誤消息,例如“未定義 CV_CAP_PROP_POS_MSEC”。
許多常量值都在 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)
。 它是相同的功能,但后者給出了一個布爾結果,如果它無法抓取圖像,則該結果為假,例如視頻結束。 我在最后更改了計數添加,你得到了 0,23 幀,...這樣第一個也將被使用。 最后,您應該使用命名空間cv::
、 std::
等。這只是最佳實踐,它避免了某些庫可能出現的歧義和問題。
如果您不需要磁盤中的圖像,而只需要分析,則刪除保存部分並將每一幀傳遞給 imageAnalysis 函數,這樣您可能會有更多數據用於統計。 另外,考慮在函數中返回 cv:Scalar sums,然后您可以對整秒或整個視頻進行一些統計。
如果您有任何問題,請隨時在評論中提問。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.