Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
MPAI-Private
MPAI-CAE
arp
Video Analyzer
Commits
8bcea3e9
Commit
8bcea3e9
authored
Jun 04, 2023
by
Matteo
Browse files
refactor old functions
parent
8f5a80ed
Changes
3
Hide whitespace changes
Inline
Side-by-side
src/main.cpp
View file @
8bcea3e9
...
...
@@ -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
<<
"
\r
Irregularities: "
<<
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
<<
"
\t
Resolution: "
<<
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
));
...
...
src/utility.cpp
View file @
8bcea3e9
#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"
);
...
...
src/utility.hpp
View file @
8bcea3e9
#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.
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment