Commit 8bcea3e9 authored by Matteo's avatar Matteo
Browse files

refactor old functions

parent 8f5a80ed
......@@ -41,13 +41,13 @@
#include <opencv2/highgui.hpp>
#include <variant>
#include "forAudioAnalyser.h"
#include "lib/Irregularity.hpp"
#include "lib/IrregularityFile.hpp"
#include "lib/colors.hpp"
#include "lib/core.hpp"
#include "lib/detection.hpp"
#include "lib/files.hpp"
#include "forAudioAnalyser.h"
#include "lib/io.hpp"
#include "lib/time.hpp"
#include "utility.hpp"
......@@ -71,8 +71,6 @@ static fs::path g_output_path{};
static fs::path g_irregularity_images_path{};
static json g_irregularity_file_1{};
static json g_irregularity_file_2{};
// RotatedRect identifying the processing area
RotatedRect g_rect_tape, g_rect_capstan;
struct Args {
fs::path
......@@ -166,47 +164,45 @@ Frame get_next_frame(VideoCapture& cap, float speed, bool skip = false) {
float rotated_rect_area(RotatedRect rect) { return rect.size.width * rect.size.height; }
/**
* @fn bool find_processing_areas(Mat my_frame)
* @fn bool find_processing_areas(Frame frame, SceneObject tape, SceneObject capstan)
* @brief Identifies the Regions Of Interest (ROIs) on the video,
* which are:
* - The reading head;
* - The tape area under the tape head (computed on the basis of the detected
* reading head);
* - The capstan.
* @param my_frame The current frame of the video.
* @param frame the frame to be analysed
* @param tape the tape object
* @param capstan the capstan object
* @return true if some areas have been detected;
* @return false otherwise.
*/
bool find_processing_areas(Frame frame, SceneObject tape, SceneObject capstan) {
va::Result<pair<va::detection::Roi, va::detection::Roi>> find_processing_areas(Frame frame, SceneObject tape,
SceneObject capstan) {
va::detection::SceneElement tape_element{
va::detection::ElementType::TAPE,
tape.minDist,
{tape.threshold.percentual, tape.threshold.angle, tape.threshold.scale, tape.threshold.pos}};
auto tape_roi_result = va::detection::find_roi(frame, va::detection::Algorithm::GHT, tape_element);
if (std::holds_alternative<va::Error>(tape_roi_result)) {
pprint(std::get<va::Error>(tape_roi_result), RED);
return false;
}
g_rect_tape = std::get<va::detection::Roi>(tape_roi_result);
cv::rectangle(frame, g_rect_tape.boundingRect(), cv::Scalar(0, 255, 0), 2);
if (std::holds_alternative<va::Error>(tape_roi_result))
return va::Error("Error while finding tape roi: " + std::get<va::Error>(tape_roi_result));
auto rect_tape = std::get<va::detection::Roi>(tape_roi_result);
va::detection::SceneElement capstan_element{
va::detection::ElementType::CAPSTAN,
capstan.minDist,
{capstan.threshold.percentual, capstan.threshold.angle, capstan.threshold.scale, capstan.threshold.pos}};
auto capstan_roi_result = va::detection::find_roi(frame, va::detection::Algorithm::SURF, capstan_element);
if (std::holds_alternative<va::Error>(capstan_roi_result)) {
pprint(std::get<va::Error>(capstan_roi_result), RED);
return false;
}
g_rect_capstan = std::get<va::detection::Roi>(capstan_roi_result);
if (std::holds_alternative<va::Error>(capstan_roi_result))
return va::Error("Error while finding capstan roi: " + std::get<va::Error>(capstan_roi_result));
auto rect_capstan = std::get<va::detection::Roi>(capstan_roi_result);
cv::rectangle(frame, g_rect_capstan.boundingRect(), cv::Scalar(255, 0, 0), 2);
cv::rectangle(frame, rect_tape.boundingRect(), cv::Scalar(0, 255, 0), 2);
cv::rectangle(frame, rect_capstan.boundingRect(), cv::Scalar(255, 0, 0), 2);
cv::imwrite(g_output_path.string() + "/my_tape_areas.jpg", frame);
return true;
return make_pair(rect_tape, rect_capstan);
}
/**
......@@ -249,7 +245,8 @@ Frame get_difference_for_roi(Frame previous, Frame current, RotatedRect roi) {
/**
* @fn bool is_frame_different(cv::Mat prev_frame, cv::Mat current_frame, int
* ms_to_end)
* ms_to_end, SceneObject capstan, SceneObject tape, Args args, va::detection::Roi rect_tape, va::detection::Roi
* rect_capstan)
* @brief Compares two consecutive video frames and establish if there
* potentially is an Irregularity. The comparison is pixel-wise and based on
* threshold values set on config.json file.
......@@ -258,11 +255,16 @@ Frame get_difference_for_roi(Frame previous, Frame current, RotatedRect roi) {
* @param current_frame the current frame;
* @param ms_to_end the number of milliseconds left before the end of the video.
* Useful for capstan analysis.
* @param capstan the capstan object;
* @param tape the tape object;
* @param args the command line arguments;
* @param rect_tape the tape area under the tape head;
* @param rect_capstan the capstan area.
* @return true if a potential Irregularity has been found;
* @return false otherwise.
*/
bool is_frame_different(cv::Mat prev_frame, cv::Mat current_frame, int ms_to_end, SceneObject capstan, SceneObject tape,
Args args) {
Args args, va::detection::Roi rect_tape, va::detection::Roi rect_capstan) {
bool result = false;
int num_different_pixels = 0;
......@@ -271,7 +273,7 @@ bool is_frame_different(cv::Mat prev_frame, cv::Mat current_frame, int ms_to_end
// In the last minute of the video, check for pinchRoller position for
// endTape event
if (!g_end_tape_saved && ms_to_end < 60000) {
RotatedRect corrected_capstan_roi = check_skew(g_rect_capstan);
RotatedRect corrected_capstan_roi = check_skew(rect_capstan);
Frame difference_frame = get_difference_for_roi(Frame(prev_frame), Frame(current_frame), corrected_capstan_roi);
for (int i = 0; i < difference_frame.rows; i++) {
......@@ -284,7 +286,7 @@ bool is_frame_different(cv::Mat prev_frame, cv::Mat current_frame, int ms_to_end
}
}
float capstan_pixel_threshold = rotated_rect_area(g_rect_capstan) * capstan.threshold.percentual / 100;
float capstan_pixel_threshold = rotated_rect_area(rect_capstan) * capstan.threshold.percentual / 100;
if (num_different_pixels > capstan_pixel_threshold) {
g_end_tape_saved = true; // Never check again for end tape instant
return true;
......@@ -293,7 +295,7 @@ bool is_frame_different(cv::Mat prev_frame, cv::Mat current_frame, int ms_to_end
/********************* Tape analysis *********************/
RotatedRect corrected_tape_roi = check_skew(g_rect_tape);
RotatedRect corrected_tape_roi = check_skew(rect_tape);
Frame cropped_current_frame =
Frame(current_frame)
.warp(cv::getRotationMatrix2D(corrected_tape_roi.center, corrected_tape_roi.angle, 1.0))
......@@ -316,7 +318,7 @@ bool is_frame_different(cv::Mat prev_frame, cv::Mat current_frame, int ms_to_end
}
}
}
float tape_area_pixels_sq = rotated_rect_area(g_rect_tape);
float tape_area_pixels_sq = rotated_rect_area(rect_tape);
mean_current_frame_color = current_frame_color_sum / tape_area_pixels_sq;
/*********************** Decision stage ************************/
......@@ -348,7 +350,7 @@ bool is_frame_different(cv::Mat prev_frame, cv::Mat current_frame, int ms_to_end
/**
* @fn void processing(cv::VideoCapture video_capture, SceneObject capstan,
* SceneObject tape, Args args)
* SceneObject tape, Args args, va::detection::Roi rect_tape, va::detection::Roi rect_capstan)
* @brief video processing phase, where each frame is analysed.
* It saves the IrregularityImages and updates the IrregularityFiles if an
* Irregularity is found
......@@ -365,8 +367,11 @@ bool is_frame_different(cv::Mat prev_frame, cv::Mat current_frame, int ms_to_end
* @param capstan the capstan SceneObject;
* @param tape the tape SceneObject;
* @param args the command line arguments.
* @param rect_tape the tape Roi;
* @param rect_capstan the capstan Roi.
*/
void processing(cv::VideoCapture video_capture, SceneObject capstan, SceneObject tape, Args args) {
void processing(cv::VideoCapture video_capture, SceneObject capstan, SceneObject tape, Args args,
va::detection::Roi rect_tape, va::detection::Roi rect_capstan) {
const int video_length_ms =
((float)video_capture.get(CAP_PROP_FRAME_COUNT) / video_capture.get(CAP_PROP_FPS)) * 1000;
int video_current_ms = video_capture.get(CAP_PROP_POS_MSEC);
......@@ -402,7 +407,8 @@ void processing(cv::VideoCapture video_capture, SceneObject capstan, SceneObject
std::cout << "\rIrregularities: " << num_saved_frames << ". ";
std::cout << "Remaining video time [mm:ss]: " << min_str_to_end << ":" << sec_str_to_end << flush;
irregularity_found = is_frame_different(prev_frame, frame, ms_to_end, capstan, tape, args);
irregularity_found =
is_frame_different(prev_frame, frame, ms_to_end, capstan, tape, args, rect_tape, rect_capstan);
if (irregularity_found) {
auto [odd_frame, _] = frame.deinterlace();
......@@ -446,9 +452,11 @@ int main(int argc, char** argv) {
const string A_IRREG_FILE_1 = "AudioAnalyser_IrregularityFileOutput1.json";
const string V_IRREG_FILE_1 = "VideoAnalyser_IrregularityFileOutput1.json";
const string V_IRREG_FILE_2 = "VideoAnalyser_IrregularityFileOutput2.json";
Args args = argc > 1 ? Args::from_cli(argc, argv) : Args::from_file(CONFIG_FILE);
SceneObject capstan = SceneObject::from_file(CONFIG_FILE, ROI::CAPSTAN);
SceneObject tape = SceneObject::from_file(CONFIG_FILE, ROI::TAPE);
Args args = argc > 1 ? Args::from_cli(argc, argv) : Args::from_file(CONFIG_FILE);
const fs::path VIDEO_PATH = args.working_path / "PreservationAudioVisualFile" / args.files_name;
const auto [FILE_NAME, FILE_FORMAT] = files::get_filename_and_extension(VIDEO_PATH);
const fs::path AUDIO_IRR_FILE_PATH = args.working_path / "temp" / FILE_NAME / A_IRREG_FILE_1;
......@@ -488,11 +496,13 @@ int main(int argc, char** argv) {
std::cout << "\tResolution: " << middle_frame.cols << "x" << middle_frame.rows << "\n\n";
bool found = find_processing_areas(middle_frame, tape, capstan);
if (!found) print_error_and_exit("Processing area not found: Try changing JSON parameters.");
auto processing_areas = find_processing_areas(middle_frame, tape, capstan);
if (std::holds_alternative<va::Error>(processing_areas))
print_error_and_exit("Processing area not found: Try changing JSON parameters.");
auto [rect_tape, rect_capstan] = std::get<pair<va::detection::Roi, va::detection::Roi>>(processing_areas);
pprint("Processing...", CYAN);
processing(video_capture, capstan, tape, args);
processing(video_capture, capstan, tape, args, rect_tape, rect_capstan);
files::save_file(g_output_path / V_IRREG_FILE_1, g_irregularity_file_1.dump(4));
......
#include "utility.hpp"
#include <filesystem>
#include <fstream>
#include <nlohmann/json.hpp>
using namespace cv;
using namespace std;
namespace fs = std::filesystem;
using json = nlohmann::json;
void utility::detect_shape(Ptr<GeneralizedHoughGuil> alg, int pos_thresh, vector<Vec4f>& positive_positions,
Mat& positive_votes, vector<Vec4f>& negative_positions, Mat& negative_votes,
Mat processing_area) {
alg->setPosThresh(pos_thresh);
int num_prev_matches = 0;
int threshold_increment = 0;
int max_match_score = 0;
// Process shapes with positive angles
alg->setMinAngle(0);
alg->setMaxAngle(3);
while (true) {
alg->detect(processing_area, positive_positions, positive_votes);
int current_matches = positive_positions.size();
if (current_matches == 1 || (current_matches == 0 && num_prev_matches == 0)) {
// We detected the most interesting shape
// Impossible to find with these parameters
break;
} else if (current_matches == 0 && num_prev_matches > 0) {
// It is not possible to detect only one shape with the current
// parameters
alg->setPosThresh(pos_thresh + threshold_increment - 1); // Decrease position value
alg->detect(processing_area, positive_positions,
positive_votes); // Detect all available shapes
break;
}
num_prev_matches = current_matches;
// Find maximum vote
for (int j = 0; j < positive_votes.cols / 3; j++) {
if (positive_votes.at<int>(3 * j) > max_match_score) max_match_score = positive_votes.at<int>(3 * j);
}
if (current_matches > 10) {
threshold_increment += 5; // To speed up computation when there are too many matches
} else if (max_match_score - (pos_thresh + threshold_increment) > 100) {
threshold_increment += 100; // To speed up computation when there are few super high
// matches
} else {
threshold_increment++;
}
alg->setPosThresh(pos_thresh + threshold_increment);
}
// Reset incremental position value
threshold_increment = 0;
num_prev_matches = 0;
max_match_score = 0;
// Process shapes with negative angles
alg->setMinAngle(357);
alg->setMaxAngle(360);
while (true) {
alg->detect(processing_area, negative_positions, negative_votes);
int current_matches = negative_positions.size();
if (current_matches == 1 || (current_matches == 0 && num_prev_matches == 0)) {
// We detected the most interesting shape
// Impossible to found with these parameters
break;
} else if (current_matches == 0 && num_prev_matches > 0) {
// It is not possible to detect only one shape with the current
// parameters
alg->setPosThresh(pos_thresh + threshold_increment - 1); // Decrease position value
alg->detect(processing_area, negative_positions,
negative_votes); // Detect all available shapes
break;
}
num_prev_matches = current_matches;
// Find maximum vote
for (int j = 0; j < positive_votes.cols / 3; j++) {
if (positive_votes.at<int>(3 * j) > max_match_score) max_match_score = positive_votes.at<int>(3 * j);
}
if (current_matches > 10) {
threshold_increment += 5; // To speed up computation when there are too many matches
} else if (max_match_score - (pos_thresh + threshold_increment) > 100) {
threshold_increment += 100; // To speed up computation when there are few super high
// matches
} else {
threshold_increment++;
}
alg->setPosThresh(pos_thresh + threshold_increment);
}
}
RotatedRect utility::drawShapes(Mat frame, const Vec4f& positions, Scalar color, int width, int height, int offsetX,
int offsetY, float processingScale) {
RotatedRect rr;
Point2f rrpts[4];
Point2f pos(positions[0] + offsetX, positions[1] + offsetY);
float scale = positions[2];
float angle = positions[3];
rr.center = pos * processingScale;
rr.size = Size2f(width * scale * processingScale, height * scale * processingScale);
rr.angle = angle;
rr.points(rrpts);
line(frame, rrpts[0], rrpts[1], color, 2);
line(frame, rrpts[1], rrpts[2], color, 2);
line(frame, rrpts[2], rrpts[3], color, 2);
line(frame, rrpts[3], rrpts[0], color, 2);
return rr;
}
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");
......
#ifndef UTILITY_H
#define UTILITY_H
#include <filesystem>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <fstream>
#include <nlohmann/json.hpp>
using namespace cv;
using namespace std;
namespace fs = std::filesystem;
/**
* @brief Namespace containing a set of utility functions used in the project.
* The functions are mainly used to perform operations on images.
*/
namespace utility {
/**
* @fn void detect_shape(Ptr<GeneralizedHoughGuil> alg, int
* pos_thresh, vector<Vec4f> &positive_positions, Mat &positive_votes,
* vector<Vec4f> &negative_positions, Mat &negative_votes, Mat processing_area)
* @brief Detects a shape in an image, using a the OpenCV algorithm
* GeneralizedHoughGuil.
*
* @param[in] alg the algorithm instance;
* @param[in] pos_thresh the position votes threshold, which determines the minimum number of votes required to consider
* a detection valid;
* @param[out] positive_positions vector representing the position assigned to
* each found rectangle for positive angles;
* @param[out] positive_votes vector representing the vote assigned to each found
* rectangle for positive angles;
* @param[out] negative_positions vector representing the position assigned to
* each found rectangle for negative angles;
* @param[out] negative_votes vector representing the vote assigned to each found
* rectangle for negative angles;
* @param[in] processing_area the image to be processed.
*/
void detect_shape(Ptr<GeneralizedHoughGuil> alg, int pos_thresh, vector<Vec4f>& positive_positions, Mat& positive_votes,
vector<Vec4f>& negative_positions, Mat& negative_votes, Mat processing_area);
/**
* @fn RotatedRect drawShapes(Mat frame, Vec4f &positions, Scalar color, int
* width, int height, int offsetX, int offsetY, float processingScale)
* @brief Draw rectangles on an image.
*
* @param frame Frame on which the rectangles will be drawn;
* @param positions The position of the rectangle;
* @param color The color of the rectangle;
* @param width The width of the rectangle;
* @param height The height of the rectangle;
* @param offsetX X offset on the position of the rectangle;
* @param offsetY Y offset on the position of the rectangle;
* @param processingScale Scaling factor, useful for downsizing.
* @return RotatedRect Object representing the drawn rectangle.
*/
RotatedRect drawShapes(Mat frame, const Vec4f& positions, Scalar color, int width, int height, int offsetX, int offsetY,
float processingScale);
} // namespace utility
/**
* @struct Threshold
* @brief Struct containing the threshold values used to detect a shape.
......
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