Commit c862755d authored by Matteo's avatar Matteo
Browse files

refactor main func

parent 7021d2ce
#include <filesystem>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
using namespace cv;
using namespace std;
using json = nlohmann::json;
namespace fs = std::filesystem;
void extractIrregularityImagesForAudio(std::string outputPath, const std::string videoPath, json irregularityFileInput,
json &irregularityFileOutput2) {
......
......@@ -23,7 +23,7 @@
* @copyright 2023, Audio Innova S.r.l.
* @credits Niccolò Pretto, Nadir Dalla Pozza, Sergio Canazza
* @license GPL v3.0
* @version 1.1.1
* @version 1.1.2
* @status Production
*/
#include <stdlib.h>
......@@ -58,6 +58,10 @@
#include "opencv2/xfeatures2d.hpp"
#include "utility.h"
#define A_IRREG_FILE_1 "AudioAnalyser_IrregularityFileOutput1.json"
#define V_IRREG_FILE_1 "VideoAnalyser_IrregularityFileOutput1.json"
#define V_IRREG_FILE_2 "VideoAnalyser_IrregularityFileOutput2.json"
using namespace cv;
using namespace std;
using utility::Frame;
......@@ -109,12 +113,14 @@ RotatedRect rect, rectTape, rectCapstan;
void pprint(string text, string color) { cout << color << text << END << endl; }
struct Args {
fs::path workingPath; /**< The working path where all input files are stored and where all output files will be saved */
fs::path
workingPath; /**< The working path where all input files are stored and where all output files will be saved */
string filesName; /**< The name of the preservation files to be considered */
bool brands; /**< True if tape presents brands on its surface */
float speed; /**< The speed at which the tape was read */
bool brands; /**< True if tape presents brands on its surface */
float speed; /**< The speed at which the tape was read */
Args(fs::path workingPath, string filesName, bool brands, float speed) {
if (speed != 7.5 && speed != 15) throw invalid_argument("Speed must be 7.5 or 15");
this->workingPath = workingPath;
this->filesName = filesName;
this->brands = brands;
......@@ -174,14 +180,43 @@ static const string READING_HEAD_IMG = "input/readingHead.png";
static const string CAPSTAN_TEMPLATE_IMG = "input/capstanBERIO058prova.png";
static const string CONFIG_FILE = "config/config.json";
/**
* @brief Get the next frame object
*
* Whenever we find an Irregularity, we want to skip a lenght equal to the
* Studer reading head (3 cm = 1.18 inches).
* Note the following considerations:
* - since we are analysing video at 25 fps a frame occurs every 40 ms
* - at 15 ips we cross 3 cm of tape in 79 ms (2 frames)
* - at 7.5 ips we cross 3 cm of tape in 157 ms (4 frames)
* The considered lengths are the widths of the tape areas.
* The following condition constitutes a valid approach if the tape areas
* have widths always equal to the reading head
*
* @param cap VideoCapture object
* @param speed tape reading speed
* @return Frame
*/
Frame get_next_frame(VideoCapture& cap, float speed, bool skip = false) {
if (skip) {
int ms_to_skip = speed == 15 ? 79 : 157;
cap.set(CAP_PROP_POS_MSEC, cap.get(CAP_PROP_POS_MSEC) + ms_to_skip);
}
Frame frame;
cap >> frame;
return frame;
}
double rotatedRectArea(RotatedRect rect) { return rect.size.width * rect.size.height; }
/**
* @fn std::tuple<int, int, double, double, vector<Vec4f>, vector<Vec4f>>
* findObject(Mat model, SceneObject object)
* @brief Find the model in the scene using the Generalized Hough Transform. It
* returns the best matches. Find the best matches for positive and negative
* angles. If there are more than one shapes, then choose the one with the
* @brief Find the model in the scene using the Generalized Hough Transform.
* It returns the best matches. Find the best matches for positive and negative
* angles. If there are more than one shape, then choose the one with the
* highest score. If there are more than one with the same highest score, then
* arbitrarily choose the latest
*
......@@ -309,9 +344,7 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) {
return false;
}
/*********************************************************************************************/
/************************************ TAPE AREA DETECTION
/*********************************************************************************************/
/************************************ TAPE AREA DETECTION ****************/
// Compute area basing on reading head detection
Vec4f positionTape(rect.center.x, rect.center.y + rect.size.height / 2 + 20 * (rect.size.width / 200), 1,
......@@ -319,13 +352,11 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) {
rectTape = utility::drawShapes(myFrame, positionTape, Scalar(0, 255 - indexPos * 64, 0), rect.size.width,
50 * (rect.size.width / 200), 0, 0, 1);
/*********************************************************************************************/
/************************************* CAPSTAN DETECTION
/*********************************************************************************************/
/************************************* CAPSTAN DETECTION ******************/
// Read template image - it is smaller than before, therefore there is no
// need to downsample
Mat templateShape = imread(CAPSTAN_TEMPLATE_IMG, IMREAD_GRAYSCALE);
Mat templateShape = cv::imread(CAPSTAN_TEMPLATE_IMG, IMREAD_GRAYSCALE);
if (useSURF) {
// Step 1: Detect the keypoints using SURF Detector, compute the
......@@ -353,7 +384,7 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) {
}
// Draw matches
Mat img_matches;
drawMatches(templateShape, keypoints_object, halved_gray_current_frame, keypoints_scene, good_matches,
cv::drawMatches(templateShape, keypoints_object, halved_gray_current_frame, keypoints_scene, good_matches,
img_matches, Scalar::all(-1), Scalar::all(-1), vector<char>(),
DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
// Localize the object
......@@ -364,7 +395,7 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) {
obj.push_back(keypoints_object[good_matches[i].queryIdx].pt);
scene.push_back(keypoints_scene[good_matches[i].trainIdx].pt);
}
Mat H = findHomography(obj, scene, RANSAC);
Mat H = cv::findHomography(obj, scene, RANSAC);
// Get the corners from the image_1 ( the object to be "detected" )
vector<Point2f> obj_corners(4);
obj_corners[0] = Point2f(0, 0);
......@@ -372,7 +403,7 @@ bool findProcessingAreas(Mat myFrame, SceneObject tape, SceneObject capstan) {
obj_corners[2] = Point2f((float)templateShape.cols, (float)templateShape.rows);
obj_corners[3] = Point2f(0, (float)templateShape.rows);
vector<Point2f> scene_corners(4);
perspectiveTransform(obj_corners, scene_corners, H);
cv::perspectiveTransform(obj_corners, scene_corners, H);
// Find average
float capstanX = (scene_corners[0].x + scene_corners[1].x + scene_corners[2].x + scene_corners[3].x) / 4;
......@@ -625,15 +656,18 @@ void processing(cv::VideoCapture videoCapture, SceneObject capstan, SceneObject
int video_current_ms = videoCapture.get(CAP_PROP_POS_MSEC);
// counters
int savedFrames = 0;
int unsavedFrames = 0;
float lastSaved = -160;
// Whenever we find an Irregularity, we want to skip a lenght equal to the
// Studer reading head (3 cm = 1.18 inches).
int savingRate = 79; // [ms]. Time taken to cross 3 cm at 15 ips, or 1.5 cm at 7.5 ips.
// The considered lengths are the widths of the tape areas.
// The following condition constitutes a valid approach if the tape areas
// have widths always equal to the reading head
if (args.speed == 7.5) savingRate = 157; // Time taken to cross 3 cm at 7.5 ips
/* Whenever we find an Irregularity, we want to skip a lenght equal to the
* Studer reading head (3 cm = 1.18 inches).
* Note the following considerations:
* - since we are analysing video at 25 fps a frame occurs every 40 ms
* - at 15 ips we cross 3 cm of tape in 79 ms (2 frames)
* - at 7.5 ips we cross 3 cm of tape in 157 ms (4 frames)
* The considered lengths are the widths of the tape areas.
* The following condition constitutes a valid approach if the tape areas
* have widths always equal to the reading head
*/
int savingRate = args.speed == 7.5 ? 157 : 79; // [ms]
// The first frame of the video won't be processed
cv::Mat prevFrame;
......@@ -641,6 +675,10 @@ void processing(cv::VideoCapture videoCapture, SceneObject capstan, SceneObject
firstInstant = video_length_ms - video_current_ms;
while (videoCapture.isOpened()) {
Frame currentFrame = get_next_frame(videoCapture, args.speed);
video_current_ms = videoCapture.get(CAP_PROP_POS_MSEC);
Frame frame;
videoCapture >> frame;
video_current_ms = videoCapture.get(CAP_PROP_POS_MSEC);
......@@ -671,23 +709,11 @@ void processing(cv::VideoCapture videoCapture, SceneObject capstan, SceneObject
// An Irregularity has been found!
auto [odd_frame, even_frame] = frame.deinterlace();
// Extract the image corresponding to the ROIs
Point2f pts[4];
savingPinchRoller ? rectCapstan.points(pts) : rectTape.points(pts);
Frame subImage(cv::Mat(frame, cv::Rect(100, min(pts[1].y, pts[2].y), frame.cols - 100,
static_cast<int>(rectTape.size.height))));
// the following comment was in the previous code, if some errors
// occur, add this correction in the deinterlace method If the found
// rectangle is of odd height, we must increase evenSubImage height
// by 1, otherwise we have segmentation_fault!!!
auto [oddSubImage, evenSubImage] = subImage.deinterlace();
string timeLabel = getTimeLabel(video_current_ms, ":");
string safeTimeLabel = getTimeLabel(video_current_ms, "-");
string irregularityImageFilename = to_string(savedFrames) + "_" + safeTimeLabel + ".jpg";
cv::imwrite(irregularityImagesPath / irregularityImageFilename, odd_frame);
cv::imwrite(irregularityImagesPath / irregularityImageFilename, odd_frame);
// Append Irregularity information to JSON
Irregularity irreg = Irregularity(Source::Video, timeLabel);
......@@ -697,10 +723,8 @@ void processing(cv::VideoCapture videoCapture, SceneObject capstan, SceneObject
lastSaved = video_current_ms;
savedFrames++;
} else {
unsavedFrames++;
}
prevFrame = frame;
}
}
......@@ -725,12 +749,11 @@ void processing(cv::VideoCapture videoCapture, SceneObject capstan, SceneObject
* @return int program status.
*/
int main(int argc, char** argv) {
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;
fs::path irregularityFileInputPath;
cv::Mat myFrame;
const fs::path VIDEO_PATH = args.workingPath / "PreservationAudioVisualFile" / args.filesName;
......@@ -741,7 +764,7 @@ int main(int argc, char** argv) {
std::exit(EXIT_FAILURE);
}
irregularityFileInputPath = args.workingPath / "temp" / fileName / "AudioAnalyser_IrregularityFileOutput1.json";
const fs::path irregularityFileInputPath = args.workingPath / "temp" / fileName / A_IRREG_FILE_1;
// Input JSON check
ifstream iJSON(irregularityFileInputPath);
......@@ -750,24 +773,9 @@ int main(int argc, char** argv) {
<< RED << irregularityFileInputPath.string() << " cannot be found or opened." << END << endl;
std::exit(EXIT_FAILURE);
}
if (args.speed != 7.5 && args.speed != 15) {
cerr << RED << BOLD << "config.json error!" << END << endl
<< RED << "Speed parameter must be 7.5 or 15 ips." << END << endl;
std::exit(EXIT_FAILURE);
}
if (tape.threshold.percentual < 0 || tape.threshold.percentual > 100) {
cerr << RED << BOLD << "config.json error!" << END << endl
<< RED << "TapeThresholdPercentual parameter must be a percentage value." << END << endl;
std::exit(EXIT_FAILURE);
}
if (capstan.threshold.percentual < 0 || capstan.threshold.percentual > 100) {
cerr << RED << BOLD << "config.json error!" << END << endl
<< RED
<< "CapstanThresholdPercentual parameter must be a percentage "
"value."
<< END << endl;
std::exit(EXIT_FAILURE);
}
// Read input JSON
iJSON >> irregularityFileInput;
// Adjust input paramenters (considering given ones as pertinent to a speed reference = 7.5)
if (args.brands) {
......@@ -785,23 +793,14 @@ int main(int argc, char** argv) {
cout << " ThresholdPercentualCapstan: " << capstan.threshold.percentual << endl;
cout << endl;
// Read input JSON
iJSON >> irregularityFileInput;
/*********************************************************************************************/
/*********************************** MAKE OUTPUT DIRECTORY
/*********************************************************************************************/
// Make directory with fileName name
outputPath = args.workingPath / "temp" / fileName;
int outputFileNameDirectory = create_directory(outputPath);
fs::create_directory(outputPath);
irregularityImagesPath = outputPath / "IrregularityImages";
int fullFrameDirectory = fs::create_directory(irregularityImagesPath);
fs::create_directory(irregularityImagesPath);
/*********************************************************************************************/
/************************************** AREAS DETECTION
/*********************************************************************************************/
/************************************** AREAS DETECTION *********************************/
cv::VideoCapture videoCapture(VIDEO_PATH);
if (!videoCapture.isOpened()) {
......@@ -827,11 +826,9 @@ int main(int argc, char** argv) {
std::exit(EXIT_FAILURE);
}
/*********************************************************************************************/
/**************************************** PROCESSING
/*********************************************************************************************/
/**************************************** PROCESSING **************************/
pprint("\nProcessing...", CYAN);
pprint("Processing...", CYAN);
// Processing timer
time_t startTimer, endTimer;
......@@ -846,18 +843,14 @@ int main(int argc, char** argv) {
string result("Processing elapsed time: " + to_string((int)min) + ":" + to_string((int)sec));
cout << endl << result << endl;
/*********************************************************************************************/
/************************************* IRREGULARITY FILES
/*********************************************************************************************/
/************************************* IRREGULARITY FILES *****************************/
fs::path outputFile1Name = outputPath / "VideoAnalyser_IrregularityFileOutput1.json";
files::saveFile(outputFile1Name, irregularityFileOutput1.dump(4), false);
files::saveFile(outputPath / V_IRREG_FILE_1, irregularityFileOutput1.dump(4), false);
// Irregularities to extract for the AudioAnalyser and to the TapeIrregularityClassifier
extractIrregularityImagesForAudio(outputPath, VIDEO_PATH, irregularityFileInput, irregularityFileOutput2);
fs::path outputFile2Name = outputPath / "VideoAnalyser_IrregularityFileOutput2.json";
files::saveFile(outputFile2Name, irregularityFileOutput2.dump(4), false);
files::saveFile(outputPath / V_IRREG_FILE_2, irregularityFileOutput2.dump(4), false);
return 0;
return EXIT_SUCCESS;
}
......@@ -6,6 +6,7 @@
using namespace cv;
using namespace std;
namespace fs = std::filesystem;
using json = nlohmann::json;
......@@ -208,6 +209,15 @@ cv::Mat utility::difference(cv::Mat& prevFrame, cv::Mat& currentFrame) {
return diff;
}
Threshold::Threshold(float percentual, int angle, int scale, int pos) {
if (percentual < 0 || percentual > 100) throw std::invalid_argument("Percentual must be between 0 and 100");
this->percentual = percentual;
this->angle = angle;
this->scale = scale;
this->pos = pos;
}
SceneObject::SceneObject(int minDist, Threshold threshold) {
this->minDist = minDist;
this->threshold = threshold;
......@@ -220,9 +230,9 @@ SceneObject SceneObject::from_file(fs::path path, Object obj) {
if (obj == Object::TAPE) {
return SceneObject(j["MinDist"],
Threshold{j["TapeThresholdPercentual"], j["AngleThresh"], j["ScaleThresh"], j["PosThresh"]});
Threshold(j["TapeThresholdPercentual"], j["AngleThresh"], j["ScaleThresh"], j["PosThresh"]));
} else {
return SceneObject(j["MinDistCapstan"], Threshold{j["CapstanThresholdPercentual"], j["AngleThreshCapstan"],
j["ScaleThreshCapstan"], j["PosThreshCapstan"]});
return SceneObject(j["MinDistCapstan"], Threshold(j["CapstanThresholdPercentual"], j["AngleThreshCapstan"],
j["ScaleThreshCapstan"], j["PosThreshCapstan"]));
}
}
\ No newline at end of file
#include <filesystem>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
......@@ -147,6 +148,13 @@ struct Threshold {
int scale; /**< The scale votes threshold for the detection of the object */
int pos; /**< The position votes threshold for the detection of the object
*/
Threshold(){};
/**
* @brief Construct a new Threshold object
* @throws std::invalid_argument if the percentual is not in the range [0, 1]
*/
Threshold(float percentual, int angle, int scale, int pos);
};
/**
......@@ -167,6 +175,7 @@ struct SceneObject {
objects for the detection of the reading head */
Threshold threshold; /**< the threshold values used to detect the object */
SceneObject(){};
SceneObject(int minDist, Threshold threshold);
~SceneObject() = default;
/**
......
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