Commit 7eb5bc11 authored by Matteo's avatar Matteo
Browse files

refactor

parent ee66ff7d
...@@ -2,4 +2,18 @@ ...@@ -2,4 +2,18 @@
The core of the acquisition process is splitted in two AI-modules: the audio analyser and the video analyser. The video analyser takes advantage of computer vision to detect anomalies on the tape area recorded in video. The core of the acquisition process is splitted in two AI-modules: the audio analyser and the video analyser. The video analyser takes advantage of computer vision to detect anomalies on the tape area recorded in video.
In simple words the analyser takes in input the video, takes a frame in the middle of the video to find ROIs In simple words the analyser takes in input the video, takes a frame in the middle of the video to find ROIs
\ No newline at end of file
```mermaid
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook
: Google
2005 : Youtube
2006 : Twitter
```
<script>
mermaid.initialize({ startOnLoad: true });
</script>
...@@ -16,6 +16,7 @@ $search ...@@ -16,6 +16,7 @@ $search
$mathjax $mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" /> <link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet $extrastylesheet
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.0.2/dist/add-html-label-6e56ed67.min.js"></script>
<script type="text/javascript" src="$relpath^../doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js"></script> <script type="text/javascript" src="$relpath^../doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js"></script>
<script type="text/javascript" src="$relpath^../doxygen-awesome-css/doxygen-awesome-fragment-copy-button.js"></script> <script type="text/javascript" src="$relpath^../doxygen-awesome-css/doxygen-awesome-fragment-copy-button.js"></script>
<script type="text/javascript" src="$relpath^../doxygen-awesome-css/doxygen-awesome-paragraph-link.js"></script> <script type="text/javascript" src="$relpath^../doxygen-awesome-css/doxygen-awesome-paragraph-link.js"></script>
......
#ifndef FORAUDIOANALYSER_H
#define FORAUDIOANALYSER_H
#include <filesystem> #include <filesystem>
#include <opencv2/core/core.hpp> #include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp> #include <opencv2/highgui/highgui.hpp>
...@@ -61,4 +63,5 @@ void extractIrregularityImagesForAudio(std::string outputPath, const std::string ...@@ -61,4 +63,5 @@ void extractIrregularityImagesForAudio(std::string outputPath, const std::string
std::cout << "\033[0;31mTimeLabel error for Audio Analyser Irregularity " << i << "." << std::endl; std::cout << "\033[0;31mTimeLabel error for Audio Analyser Irregularity " << i << "." << std::endl;
} }
} }
} }
\ No newline at end of file #endif // FORAUDIOANALYSER_H
\ No newline at end of file
...@@ -4,13 +4,9 @@ ...@@ -4,13 +4,9 @@
using std::cout, std::endl, std::cerr, std::ofstream, std::ios; using std::cout, std::endl, std::cerr, std::ofstream, std::ios;
void files::saveFile(std::filesystem::path fileName, std::string content, bool append) { void files::save_file(std::filesystem::path fileName, std::string content) {
ofstream outputFile; ofstream outputFile;
if (append) { outputFile.open(fileName);
outputFile.open(fileName, ios::app);
} else {
outputFile.open(fileName);
}
outputFile << content << endl; outputFile << content << endl;
outputFile.close(); outputFile.close();
} }
...@@ -23,16 +19,9 @@ void files::findFileNameFromPath(std::string* path, std::string* fileName, std:: ...@@ -23,16 +19,9 @@ void files::findFileNameFromPath(std::string* path, std::string* fileName, std::
*extension = path->substr(path->find_last_of(".") + 1, path->size()); *extension = path->substr(path->find_last_of(".") + 1, path->size());
} }
int files::findFileName(std::string videoPath, std::string& fileName, std::string& extension) { std::pair<std::string, std::string> files::get_filename_and_extension(std::string videoPath) {
std::string fileName, extension;
files::findFileNameFromPath(&videoPath, &fileName, &extension); files::findFileNameFromPath(&videoPath, &fileName, &extension);
if (extension.compare("avi") != 0 && extension.compare("mp4") != 0 && extension.compare("mov") != 0) { return std::pair<std::string, std::string>(fileName, extension);
cerr << "Input file extension must be \"avi\", \"mp4\" or \"mov\"." << endl;
return -1;
} else {
cout << "Video to be analysed: " << endl;
cout << " File name: " << fileName << endl;
cout << " Extension: " << extension << endl;
}
return 0;
} }
...@@ -25,16 +25,13 @@ ...@@ -25,16 +25,13 @@
*/ */
namespace files { namespace files {
/** /**
* @fn void saveFile(std::filesystem::path fileName, std::string content, bool * @fn void save_file(std::filesystem::path fileName, std::string content)
* append)
* @brief Save content to a file * @brief Save content to a file
* *
* @param fileName the name of the file * @param fileName the name of the file
* @param content the content to be saved * @param content the content to be saved
* @param append if true, the content will be appended to the file, otherwise
* the file will be overwritten
*/ */
void saveFile(std::filesystem::path fileName, std::string content, bool append); void save_file(std::filesystem::path fileName, std::string content);
/** /**
* @fn void findFileNameFromPath(std::string* path, std::string* fileName, * @fn void findFileNameFromPath(std::string* path, std::string* fileName,
...@@ -48,15 +45,12 @@ void saveFile(std::filesystem::path fileName, std::string content, bool append); ...@@ -48,15 +45,12 @@ void saveFile(std::filesystem::path fileName, std::string content, bool append);
void findFileNameFromPath(std::string* path, std::string* fileName, std::string* extension); void findFileNameFromPath(std::string* path, std::string* fileName, std::string* extension);
/** /**
* @fn int findFileName(std::string videoPath, std::string &fileName, * @fn std::pair<std::string, std::string> findFileName(std::string videoPath)
* std::string &extension)
* @brief Check if the specified input video file exists and is supported. * @brief Check if the specified input video file exists and is supported.
* *
* @param[in] videoPath Full video path; * @param videoPath Full video path;
* @param[out] fileName Video file name; * @return a pair of strings containing the video file name and its extension.
* @param[out] extension Video extension.
* @return int -1 if the format is not supported, 0 otherwise.
*/ */
int findFileName(std::string videoPath, std::string& fileName, std::string& extension); std::pair<std::string, std::string> get_filename_and_extension(std::string videoPath);
} // namespace files } // namespace files
#endif // FILES_H #endif // FILES_H
\ No newline at end of file
...@@ -38,11 +38,13 @@ ...@@ -38,11 +38,13 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/core/core.hpp> #include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp> #include <opencv2/features2d.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgcodecs.hpp> #include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp> #include <opencv2/imgproc.hpp>
#include <ranges> #include <opencv2/xfeatures2d.hpp>
#include "forAudioAnalyser.h" #include "forAudioAnalyser.h"
#include "lib/Irregularity.h" #include "lib/Irregularity.h"
...@@ -50,12 +52,6 @@ ...@@ -50,12 +52,6 @@
#include "lib/colors.h" #include "lib/colors.h"
#include "lib/files.h" #include "lib/files.h"
#include "lib/time.h" #include "lib/time.h"
#include "opencv2/calib3d.hpp"
#include "opencv2/core.hpp"
#include "opencv2/features2d.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/xfeatures2d.hpp"
#include "utility.h" #include "utility.h"
#define A_IRREG_FILE_1 "AudioAnalyser_IrregularityFileOutput1.json" #define A_IRREG_FILE_1 "AudioAnalyser_IrregularityFileOutput1.json"
...@@ -79,9 +75,6 @@ namespace po = boost::program_options; ...@@ -79,9 +75,6 @@ namespace po = boost::program_options;
*/ */
bool useSURF = true; bool useSURF = true;
bool savingPinchRoller = false;
bool pinchRollerRect = false;
bool savingBrand = false;
bool endTapeSaved = false; bool endTapeSaved = false;
float mediaPrevFrame = 0; float mediaPrevFrame = 0;
...@@ -91,13 +84,11 @@ float mediaPrevFrame = 0; ...@@ -91,13 +84,11 @@ float mediaPrevFrame = 0;
*/ */
bool firstBrand = true; bool firstBrand = true;
float firstInstant = 0; float firstInstant = 0;
string fileName, extension;
// Path variables // Path variables
static fs::path outputPath{}; static fs::path outputPath{};
static fs::path irregularityImagesPath{}; static fs::path irregularityImagesPath{};
// JSON files // JSON files
static json configurationFile{};
static json irregularityFileOutput1{}; static json irregularityFileOutput1{};
static json irregularityFileOutput2{}; static json irregularityFileOutput2{};
// RotatedRect identifying the processing area // RotatedRect identifying the processing area
...@@ -110,7 +101,12 @@ RotatedRect rect, rectTape, rectCapstan; ...@@ -110,7 +101,12 @@ RotatedRect rect, rectTape, rectCapstan;
* @param text * @param text
* @param color * @param color
*/ */
void pprint(string text, string color) { cout << color << text << END << endl; } void pprint(string text, string color) { std::cout << color << text << END << endl; }
void print_error_and_exit(string title, string message) {
std::cerr << RED << BOLD << title << END << endl;
std::cerr << RED << message << END << endl;
exit(EXIT_FAILURE);
}
struct Args { struct Args {
fs::path fs::path
...@@ -155,19 +151,16 @@ struct Args { ...@@ -155,19 +151,16 @@ struct Args {
"speed,s", po::value<float>()->required(), "Specify the speed at which the tape was read"); "speed,s", po::value<float>()->required(), "Specify the speed at which the tape was read");
po::store(po::command_line_parser(argc, argv).options(desc).run(), vm); po::store(po::command_line_parser(argc, argv).options(desc).run(), vm);
if (vm.count("help")) { if (vm.count("help")) {
cout << desc << "\n"; std::cout << desc << "\n";
std::exit(EXIT_SUCCESS); std::exit(EXIT_SUCCESS);
} }
po::notify(vm); po::notify(vm);
} catch (po::invalid_command_line_syntax& e) { } catch (po::invalid_command_line_syntax& e) {
pprint("The command line syntax is invalid: " + string(e.what()), RED + BOLD); print_error_and_exit("Invalid command line syntax!", string(e.what()));
std::exit(EXIT_FAILURE);
} catch (po::required_option& e) { } catch (po::required_option& e) {
cerr << "Error: " << e.what() << endl; print_error_and_exit("Missing required option!", string(e.what()));
std::exit(EXIT_FAILURE);
} catch (nlohmann::detail::type_error e) { } catch (nlohmann::detail::type_error e) {
pprint("config.json error! " + string(e.what()), RED); print_error_and_exit("config.json error!", string(e.what()));
std::exit(EXIT_FAILURE);
} }
return Args(fs::path(vm["working-path"].as<string>()), vm["files-name"].as<string>(), vm["brands"].as<bool>(), return Args(fs::path(vm["working-path"].as<string>()), vm["files-name"].as<string>(), vm["brands"].as<bool>(),
...@@ -204,7 +197,6 @@ Frame get_next_frame(VideoCapture& cap, float speed, bool skip = false) { ...@@ -204,7 +197,6 @@ Frame get_next_frame(VideoCapture& cap, float speed, bool skip = false) {
int ms_to_skip = speed == 15 ? 79 : 157; int ms_to_skip = speed == 15 ? 79 : 157;
cap.set(CAP_PROP_POS_MSEC, cap.get(CAP_PROP_POS_MSEC) + ms_to_skip); cap.set(CAP_PROP_POS_MSEC, cap.get(CAP_PROP_POS_MSEC) + ms_to_skip);
} }
Frame frame; Frame frame;
cap >> frame; cap >> frame;
return frame; return frame;
...@@ -295,9 +287,7 @@ std::tuple<int, int, double, double, vector<Vec4f>, vector<Vec4f>> findObject(Ma ...@@ -295,9 +287,7 @@ std::tuple<int, int, double, double, vector<Vec4f>, vector<Vec4f>> findObject(Ma
* @return false otherwise. * @return false otherwise.
*/ */
bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) { bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) {
/*********************************************************************************************/ /************************** READING HEAD DETECTION ***********************/
/*********************************** READING HEAD DETECTION
/*********************************************************************************************/
// Save a grayscale version of myFrame in myFrameGrayscale and downsample it // Save a grayscale version of myFrame in myFrameGrayscale and downsample it
// in half pixels for performance reasons // in half pixels for performance reasons
...@@ -357,7 +347,7 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) { ...@@ -357,7 +347,7 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) {
// Read template image - it is smaller than before, therefore there is no // Read template image - it is smaller than before, therefore there is no
// need to downsample // need to downsample
Mat templateShape = cv::imread(CAPSTAN_TEMPLATE_IMG, IMREAD_GRAYSCALE); Mat capstan_template = cv::imread(CAPSTAN_TEMPLATE_IMG, IMREAD_GRAYSCALE);
if (useSURF) { if (useSURF) {
// Step 1: Detect the keypoints using SURF Detector, compute the // Step 1: Detect the keypoints using SURF Detector, compute the
...@@ -367,7 +357,7 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) { ...@@ -367,7 +357,7 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) {
vector<KeyPoint> keypoints_object, keypoints_scene; vector<KeyPoint> keypoints_object, keypoints_scene;
Mat descriptors_object, descriptors_scene; Mat descriptors_object, descriptors_scene;
detector->detectAndCompute(templateShape, noArray(), keypoints_object, descriptors_object); detector->detectAndCompute(capstan_template, noArray(), keypoints_object, descriptors_object);
detector->detectAndCompute(gray_current_frame, noArray(), keypoints_scene, descriptors_scene); detector->detectAndCompute(gray_current_frame, noArray(), keypoints_scene, descriptors_scene);
// Step 2: Matching descriptor vectors with a FLANN based matcher // Step 2: Matching descriptor vectors with a FLANN based matcher
...@@ -385,7 +375,7 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) { ...@@ -385,7 +375,7 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) {
} }
// Draw matches // Draw matches
Mat img_matches; Mat img_matches;
cv::drawMatches(templateShape, keypoints_object, halved_gray_current_frame, keypoints_scene, good_matches, cv::drawMatches(capstan_template, keypoints_object, halved_gray_current_frame, keypoints_scene, good_matches,
img_matches, Scalar::all(-1), Scalar::all(-1), vector<char>(), img_matches, Scalar::all(-1), Scalar::all(-1), vector<char>(),
DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS); DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
// Localize the object // Localize the object
...@@ -400,9 +390,9 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) { ...@@ -400,9 +390,9 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) {
// Get the corners from the image_1 ( the object to be "detected" ) // Get the corners from the image_1 ( the object to be "detected" )
vector<Point2f> obj_corners(4); vector<Point2f> obj_corners(4);
obj_corners[0] = Point2f(0, 0); obj_corners[0] = Point2f(0, 0);
obj_corners[1] = Point2f((float)templateShape.cols, 0); obj_corners[1] = Point2f((float)capstan_template.cols, 0);
obj_corners[2] = Point2f((float)templateShape.cols, (float)templateShape.rows); obj_corners[2] = Point2f((float)capstan_template.cols, (float)capstan_template.rows);
obj_corners[3] = Point2f(0, (float)templateShape.rows); obj_corners[3] = Point2f(0, (float)capstan_template.rows);
vector<Point2f> scene_corners(4); vector<Point2f> scene_corners(4);
cv::perspectiveTransform(obj_corners, scene_corners, H); cv::perspectiveTransform(obj_corners, scene_corners, H);
...@@ -416,7 +406,7 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) { ...@@ -416,7 +406,7 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) {
// centering and -90 in height // centering and -90 in height
Vec4f positionCapstan(capstanX + 10, capstanY + 45, 1, 0); Vec4f positionCapstan(capstanX + 10, capstanY + 45, 1, 0);
rectCapstan = utility::drawShapes(myFrame, positionCapstan, Scalar(255 - indexPos * 64, 0, 0), rectCapstan = utility::drawShapes(myFrame, positionCapstan, Scalar(255 - indexPos * 64, 0, 0),
templateShape.cols - 20, templateShape.rows - 90, 0, 0, 1); capstan_template.cols - 20, capstan_template.rows - 90, 0, 0, 1);
} else { } else {
// Process only right portion of the image, where the capstain always // Process only right portion of the image, where the capstain always
...@@ -431,16 +421,16 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) { ...@@ -431,16 +421,16 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) {
// Reset algorithm and set parameters // Reset algorithm and set parameters
auto [indexPos, indexNeg, maxValPos, maxValNeg, positionsC1Pos, positionsC1Neg] = auto [indexPos, indexNeg, maxValPos, maxValNeg, positionsC1Pos, positionsC1Neg] =
findObject(templateShape, capstan, capstanProcessingAreaGrayscale); findObject(capstan_template, capstan, capstanProcessingAreaGrayscale);
RotatedRect rectCapstanPos, rectCapstanNeg; RotatedRect rectCapstanPos, rectCapstanNeg;
if (positionsC1Pos.size() > 0) if (positionsC1Pos.size() > 0)
rectCapstanPos = utility::drawShapes(myFrame, positionsC1Pos[indexPos], Scalar(255 - indexPos * 64, 0, 0), rectCapstanPos = utility::drawShapes(myFrame, positionsC1Pos[indexPos], Scalar(255 - indexPos * 64, 0, 0),
templateShape.cols - 22, templateShape.rows - 92, capstan_template.cols - 22, capstan_template.rows - 92,
capstanProcessingAreaRectX + 11, capstanProcessingAreaRectY + 46, 1); capstanProcessingAreaRectX + 11, capstanProcessingAreaRectY + 46, 1);
if (positionsC1Neg.size() > 0) if (positionsC1Neg.size() > 0)
rectCapstanNeg = utility::drawShapes(myFrame, positionsC1Neg[indexNeg], Scalar(255 - indexNeg * 64, 128, 0), rectCapstanNeg = utility::drawShapes(myFrame, positionsC1Neg[indexNeg], Scalar(255 - indexNeg * 64, 128, 0),
templateShape.cols - 22, templateShape.rows - 92, capstan_template.cols - 22, capstan_template.rows - 92,
capstanProcessingAreaRectX + 11, capstanProcessingAreaRectY + 46, 1); capstanProcessingAreaRectX + 11, capstanProcessingAreaRectY + 46, 1);
if (maxValPos > 0) if (maxValPos > 0)
...@@ -460,9 +450,7 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) { ...@@ -460,9 +450,7 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) {
} }
} }
cout << endl; // Save the image containing the ROIs
// Save the image containing the detected areas
cv::imwrite(outputPath.string() + "/tapeAreas.jpg", myFrame); cv::imwrite(outputPath.string() + "/tapeAreas.jpg", myFrame);
return true; return true;
...@@ -480,12 +468,10 @@ RotatedRect check_skew(RotatedRect roi) { ...@@ -480,12 +468,10 @@ RotatedRect check_skew(RotatedRect roi) {
// thanks to http://felix.abecassis.me/2011/10/opencv-rotation-deskewing/ // thanks to http://felix.abecassis.me/2011/10/opencv-rotation-deskewing/
cv::Size rect_size = roi.size; cv::Size rect_size = roi.size;
float angle = roi.angle; float angle = roi.angle;
if (roi.angle < -45.) { if (roi.angle < -45.) {
angle += 90.0; angle += 90.0;
swap(rect_size.width, rect_size.height); swap(rect_size.width, rect_size.height);
} }
return RotatedRect(roi.center, rect_size, angle); return RotatedRect(roi.center, rect_size, angle);
} }
...@@ -523,7 +509,7 @@ Frame get_difference_for_roi(Frame previous, Frame current, RotatedRect roi) { ...@@ -523,7 +509,7 @@ Frame get_difference_for_roi(Frame previous, Frame current, RotatedRect roi) {
* @return false otherwise. * @return false otherwise.
*/ */
bool is_frame_different(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd, SceneObject capstan, SceneObject tape, bool is_frame_different(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd, SceneObject capstan, SceneObject tape,
Args args) { Args args) {
bool result = false; bool result = false;
/*********************** Capstan analysis ************************/ /*********************** Capstan analysis ************************/
...@@ -531,9 +517,8 @@ bool is_frame_different(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd, Sc ...@@ -531,9 +517,8 @@ bool is_frame_different(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd, Sc
// In the last minute of the video, check for pinchRoller position for // In the last minute of the video, check for pinchRoller position for
// endTape event // endTape event
if (!endTapeSaved && msToEnd < 60000) { if (!endTapeSaved && msToEnd < 60000) {
// Capstan area float capstanDifferentPixelsThreshold =
int capstanAreaPixels = rectCapstan.size.width * rectCapstan.size.height; (rectCapstan.size.width * rectCapstan.size.height) * capstan.threshold.percentual / 100;
float capstanDifferentPixelsThreshold = capstanAreaPixels * capstan.threshold.percentual / 100;
RotatedRect corrected_capstan_roi = check_skew(rectCapstan); RotatedRect corrected_capstan_roi = check_skew(rectCapstan);
Frame difference_frame = get_difference_for_roi(Frame(prevFrame), Frame(currentFrame), corrected_capstan_roi); Frame difference_frame = get_difference_for_roi(Frame(prevFrame), Frame(currentFrame), corrected_capstan_roi);
...@@ -551,15 +536,10 @@ bool is_frame_different(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd, Sc ...@@ -551,15 +536,10 @@ bool is_frame_different(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd, Sc
} }
if (blackPixelsCapstan > capstanDifferentPixelsThreshold) { if (blackPixelsCapstan > capstanDifferentPixelsThreshold) {
savingPinchRoller = true;
endTapeSaved = true; // Never check again for end tape instant endTapeSaved = true; // Never check again for end tape instant
return true; return true;
} }
} }
// savingPinchRoller will already be false before the last minute of the
// video. After having saved the capstan, the next time reset the variable
// to not save again
savingPinchRoller = false;
/********************* Tape analysis *********************/ /********************* Tape analysis *********************/
...@@ -568,18 +548,11 @@ bool is_frame_different(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd, Sc ...@@ -568,18 +548,11 @@ bool is_frame_different(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd, Sc
float tapeDifferentPixelsThreshold = tapeAreaPixels * tape.threshold.percentual / 100; float tapeDifferentPixelsThreshold = tapeAreaPixels * tape.threshold.percentual / 100;
RotatedRect corrected_tape_roi = check_skew(rectTape); RotatedRect corrected_tape_roi = check_skew(rectTape);
Frame croppedCurrentFrame = Frame(currentFrame) Frame croppedCurrentFrame = Frame(currentFrame)
.warp(getRotationMatrix2D(corrected_tape_roi.center, corrected_tape_roi.angle, 1.0)) .warp(getRotationMatrix2D(corrected_tape_roi.center, corrected_tape_roi.angle, 1.0))
.crop(corrected_tape_roi.size, corrected_tape_roi.center); .crop(corrected_tape_roi.size, corrected_tape_roi.center);
Frame difference_frame = get_difference_for_roi(Frame(prevFrame), Frame(currentFrame), corrected_tape_roi); Frame difference_frame = get_difference_for_roi(Frame(prevFrame), Frame(currentFrame), corrected_tape_roi);
int decEnd = (msToEnd % 1000) / 100;
int secEnd = (msToEnd - (msToEnd % 1000)) / 1000;
int minEnd = secEnd / 60;
secEnd = secEnd % 60;
/********************** Segment analysis ************************/ /********************** Segment analysis ************************/
int blackPixels = 0; int blackPixels = 0;
...@@ -608,21 +581,13 @@ bool is_frame_different(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd, Sc ...@@ -608,21 +581,13 @@ bool is_frame_different(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd, Sc
} }
/***** BRANDS MANAGEMENT *****/ /***** BRANDS MANAGEMENT *****/
if (args.brands) { // At the beginning of the video, wait at least 5 seconds before the
// At the beginning of the video, wait at least 5 seconds before the // next Irregularity to consider it as a brand. It is not guaranteed
// next Irregularity to consider it as a brand. It is not guaranteed // that it will be the first brand, but it is generally a safe
// that it will be the first brand, but it is generally a safe // approach to have a correct image
// approach to have a correct image if (args.brands && firstBrand && firstInstant - msToEnd > 5000) {
if (firstBrand) { firstBrand = false;
if (firstInstant - msToEnd > 5000) { result = true;
firstBrand = false;
savingBrand = true;
result = true;
}
// In the following iterations reset savingBrand, since we are
// no longer interested in brands.
} else
savingBrand = false;
} }
} }
...@@ -668,7 +633,7 @@ void processing(cv::VideoCapture videoCapture, SceneObject capstan, SceneObject ...@@ -668,7 +633,7 @@ void processing(cv::VideoCapture videoCapture, SceneObject capstan, SceneObject
video_current_ms = videoCapture.get(CAP_PROP_POS_MSEC); video_current_ms = videoCapture.get(CAP_PROP_POS_MSEC);
if (frame.empty()) { if (frame.empty()) {
cout << endl << "Empty frame!" << endl; std::cout << endl << "Empty frame!" << endl;
videoCapture.release(); videoCapture.release();
return; return;
} }
...@@ -682,15 +647,15 @@ void processing(cv::VideoCapture videoCapture, SceneObject capstan, SceneObject ...@@ -682,15 +647,15 @@ void processing(cv::VideoCapture videoCapture, SceneObject capstan, SceneObject
int secToEnd = msToEnd / 1000; int secToEnd = msToEnd / 1000;
int minToEnd = (secToEnd / 60) % 60; int minToEnd = (secToEnd / 60) % 60;
secToEnd = secToEnd % 60; secToEnd = secToEnd % 60;
string secStrToEnd = secToEnd < 10 ? "0" + to_string(secToEnd) : to_string(secToEnd); string secStrToEnd = (secToEnd < 10 ? "0" : "") + to_string(secToEnd);
string minStrToEnd = minToEnd < 10 ? "0" + to_string(minToEnd) : to_string(minToEnd); string minStrToEnd = (minToEnd < 10 ? "0" : "") + to_string(minToEnd);
cout << "\rIrregularities: " << savedFrames << ". "; std::cout << "\rIrregularities: " << savedFrames << ". ";
cout << "Remaining video time [mm:ss]: " << minStrToEnd << ":" << secStrToEnd << flush; std::cout << "Remaining video time [mm:ss]: " << minStrToEnd << ":" << secStrToEnd << flush;
irregularity_found = is_frame_different(prevFrame, frame, msToEnd, capstan, tape, args); irregularity_found = is_frame_different(prevFrame, frame, msToEnd, capstan, tape, args);
if (irregularity_found) { if (irregularity_found) {
auto [odd_frame, even_frame] = frame.deinterlace(); auto [odd_frame, _] = frame.deinterlace();
string irregularityImageFilename = string irregularityImageFilename =
to_string(savedFrames) + "_" + getTimeLabel(video_current_ms, "-") + ".jpg"; to_string(savedFrames) + "_" + getTimeLabel(video_current_ms, "-") + ".jpg";
...@@ -704,7 +669,6 @@ void processing(cv::VideoCapture videoCapture, SceneObject capstan, SceneObject ...@@ -704,7 +669,6 @@ void processing(cv::VideoCapture videoCapture, SceneObject capstan, SceneObject
savedFrames++; savedFrames++;
} }
prevFrame = frame; prevFrame = frame;
} }
} }
...@@ -729,49 +693,33 @@ void processing(cv::VideoCapture videoCapture, SceneObject capstan, SceneObject ...@@ -729,49 +693,33 @@ void processing(cv::VideoCapture videoCapture, SceneObject capstan, SceneObject
* @return int program status. * @return int program status.
*/ */
int main(int argc, char** argv) { int main(int argc, char** argv) {
SceneObject capstan = SceneObject::from_file(CONFIG_FILE, ROI::CAPSTAN);
SceneObject tape = SceneObject::from_file(CONFIG_FILE, ROI::TAPE);
const Args args = argc > 1 ? Args::from_cli(argc, argv) : Args::from_file(CONFIG_FILE); const Args args = argc > 1 ? Args::from_cli(argc, argv) : Args::from_file(CONFIG_FILE);
SceneObject capstan = SceneObject::from_file(CONFIG_FILE, Object::CAPSTAN);
SceneObject tape = SceneObject::from_file(CONFIG_FILE, Object::TAPE);
json irregularityFileInput;
const fs::path VIDEO_PATH = args.workingPath / "PreservationAudioVisualFile" / args.filesName; const fs::path VIDEO_PATH = args.workingPath / "PreservationAudioVisualFile" / args.filesName;
const auto [fileName, extension] = files::get_filename_and_extension(VIDEO_PATH);
if (files::findFileName(VIDEO_PATH, fileName, extension) == -1) {
cerr << RED << BOLD << "Input error!" << END << endl
<< RED << VIDEO_PATH.string() << " cannot be found or opened." << END << endl;
std::exit(EXIT_FAILURE);
}
const fs::path irregularityFileInputPath = args.workingPath / "temp" / fileName / A_IRREG_FILE_1; const fs::path irregularityFileInputPath = args.workingPath / "temp" / fileName / A_IRREG_FILE_1;
// Input JSON check std::cout << "Video to be analysed: " << endl;
std::cout << "\tFile name: " << fileName << endl;
std::cout << "\tExtension: " << extension << endl;
if (extension.compare("avi") != 0 && extension.compare("mp4") != 0 && extension.compare("mov") != 0)
print_error_and_exit("Input error", "The input file must be an AVI, MP4 or MOV file.");
ifstream iJSON(irregularityFileInputPath); ifstream iJSON(irregularityFileInputPath);
if (iJSON.fail()) { if (iJSON.fail())
cerr << RED << BOLD << "config.json error!" << END << endl print_error_and_exit("config.json error", irregularityFileInputPath.string() + " cannot be found or opened.");
<< RED << irregularityFileInputPath.string() << " cannot be found or opened." << END << endl;
std::exit(EXIT_FAILURE);
}
// Read input JSON // Read input JSON
json irregularityFileInput;
iJSON >> irregularityFileInput; iJSON >> irregularityFileInput;
// Adjust input paramenters (considering given ones as pertinent to a speed reference = 7.5) // Adjust input paramenters (considering given ones as pertinent to a speed reference = 7.5)
if (args.brands) { if (args.speed == 15) {
if (args.speed == 15) tape.threshold.percentual += 6; tape.threshold.percentual += args.brands ? 6 : 20;
} else if (args.speed == 15) } else if (!args.brands)
tape.threshold.percentual += 20;
else
tape.threshold.percentual += 21; tape.threshold.percentual += 21;
cout << endl;
cout << "Parameters:" << endl;
cout << " Brands: " << args.brands << endl;
cout << " Speed: " << args.speed << endl;
cout << " ThresholdPercentual: " << tape.threshold.percentual << endl;
cout << " ThresholdPercentualCapstan: " << capstan.threshold.percentual << endl;
cout << endl;
// Make directory with fileName name // Make directory with fileName name
outputPath = args.workingPath / "temp" / fileName; outputPath = args.workingPath / "temp" / fileName;
fs::create_directory(outputPath); fs::create_directory(outputPath);
...@@ -779,56 +727,27 @@ int main(int argc, char** argv) { ...@@ -779,56 +727,27 @@ int main(int argc, char** argv) {
irregularityImagesPath = outputPath / "IrregularityImages"; irregularityImagesPath = outputPath / "IrregularityImages";
fs::create_directory(irregularityImagesPath); fs::create_directory(irregularityImagesPath);
/************************************** AREAS DETECTION *********************************/ cv::VideoCapture videoCapture(VIDEO_PATH); // Open video file
if (!videoCapture.isOpened()) print_error_and_exit("Video error", "Video file cannot be opened.");
cv::VideoCapture videoCapture(VIDEO_PATH); videoCapture.set(CAP_PROP_POS_FRAMES,
if (!videoCapture.isOpened()) { videoCapture.get(CAP_PROP_FRAME_COUNT) / 2); // Set frame position to half video length
pprint("Video unreadable.", RED + BOLD);
std::exit(EXIT_FAILURE);
}
int frames_number = videoCapture.get(CAP_PROP_FRAME_COUNT);
// Set frame position to half video length
videoCapture.set(CAP_PROP_POS_FRAMES, frames_number / 2);
cv::Mat middle_frame = get_next_frame(videoCapture, args.speed); cv::Mat middle_frame = get_next_frame(videoCapture, args.speed);
videoCapture.set(CAP_PROP_POS_FRAMES, 0); // Reset frame position
cout << "Video resolution: " << middle_frame.cols << "x" << middle_frame.rows << endl; std::cout << "\tResolution: " << middle_frame.cols << "x" << middle_frame.rows << "\n\n";
bool found = findProcessingAreas(middle_frame, tape, capstan); bool found = findProcessingAreas(middle_frame, tape, capstan);
if (!found) print_error_and_exit("Processing area not found", "Try changing JSON parameters.");
// Reset frame position
videoCapture.set(CAP_PROP_POS_FRAMES, 0);
if (!found) {
pprint("Processing area not found. Try changing JSON parameters.", RED);
std::exit(EXIT_FAILURE);
}
/**************************************** PROCESSING **************************/
pprint("Processing...", CYAN); pprint("Processing...", CYAN);
// Processing timer
time_t startTimer, endTimer;
startTimer = time(NULL);
processing(videoCapture, capstan, tape, args); processing(videoCapture, capstan, tape, args);
endTimer = time(NULL); files::save_file(outputPath / V_IRREG_FILE_1, irregularityFileOutput1.dump(4));
float min = (endTimer - startTimer) / 60;
float sec = (endTimer - startTimer) % 60;
string result("Processing elapsed time: " + to_string((int)min) + ":" + to_string((int)sec));
cout << endl << result << endl;
/************************************* IRREGULARITY FILES *****************************/
files::saveFile(outputPath / V_IRREG_FILE_1, irregularityFileOutput1.dump(4), false);
// Irregularities to extract for the AudioAnalyser and to the TapeIrregularityClassifier // Irregularities to extract for the AudioAnalyser and to the TapeIrregularityClassifier
extractIrregularityImagesForAudio(outputPath, VIDEO_PATH, irregularityFileInput, irregularityFileOutput2); extractIrregularityImagesForAudio(outputPath, VIDEO_PATH, irregularityFileInput, irregularityFileOutput2);
files::save_file(outputPath / V_IRREG_FILE_2, irregularityFileOutput2.dump(4));
files::saveFile(outputPath / V_IRREG_FILE_2, irregularityFileOutput2.dump(4), false);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
...@@ -223,12 +223,12 @@ SceneObject::SceneObject(int minDist, Threshold threshold) { ...@@ -223,12 +223,12 @@ SceneObject::SceneObject(int minDist, Threshold threshold) {
this->threshold = threshold; this->threshold = threshold;
} }
SceneObject SceneObject::from_file(fs::path path, Object obj) { SceneObject SceneObject::from_file(fs::path path, ROI obj) {
ifstream iConfig(path); ifstream iConfig(path);
json j; json j;
iConfig >> j; iConfig >> j;
if (obj == Object::TAPE) { if (obj == ROI::TAPE) {
return SceneObject(j["MinDist"], return SceneObject(j["MinDist"],
Threshold(j["TapeThresholdPercentual"], j["AngleThresh"], j["ScaleThresh"], j["PosThresh"])); Threshold(j["TapeThresholdPercentual"], j["AngleThresh"], j["ScaleThresh"], j["PosThresh"]));
} else { } else {
......
#ifndef UTILITY_H
#define UTILITY_H
#include <filesystem> #include <filesystem>
#include <opencv2/core/core.hpp> #include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp> #include <opencv2/highgui/highgui.hpp>
...@@ -158,11 +160,11 @@ struct Threshold { ...@@ -158,11 +160,11 @@ struct Threshold {
}; };
/** /**
* @enum Object * @enum ROI
* @brief Enum containing the possible objects to detect. * @brief Enum containing the possible objects to detect.
* *
*/ */
enum Object { TAPE, CAPSTAN }; enum ROI { TAPE, CAPSTAN };
/** /**
* @struct SceneObject * @struct SceneObject
...@@ -187,5 +189,6 @@ struct SceneObject { ...@@ -187,5 +189,6 @@ struct SceneObject {
* @param obj The object to detect. * @param obj The object to detect.
* @return SceneObject The SceneObject created from the file. * @return SceneObject The SceneObject created from the file.
*/ */
static SceneObject from_file(fs::path path, Object obj); static SceneObject from_file(fs::path path, ROI obj);
}; };
#endif // UTILITY_H
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment