Commit 864f7f04 authored by Matteo's avatar Matteo
Browse files

update

parent b89f3469
......@@ -5,14 +5,37 @@ SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
SET(CMAKE_CXX_STANDARD 23)
# include(FetchContent)
# FetchContent_Declare(
# googletest
# URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
# )
# # For Windows: Prevent overriding the parent project's compiler/linker settings
# SET(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# FetchContent_MakeAvailable(googletest)
LINK_DIRECTORIES(/usr/local/lib)
add_library(analyser_lib
src/lib/colors.h
src/lib/parser.cpp
src/lib/parser.h
src/lib/time.cpp
src/lib/time.h
src/lib/enums.h
src/lib/enums.cpp
src/lib/Irregularity.h
src/lib/Irregularity.cpp
src/lib/IrregularityFile.h
src/lib/IrregularityFile.cpp
src/lib/TimeLabel.h
src/lib/TimeLabel.cpp
src/utility.h
src/utility.cpp
)
add_library(files
src/lib/files.h
src/lib/files.cpp
)
FIND_PACKAGE(OpenCV REQUIRED)
......@@ -29,4 +52,25 @@ TARGET_LINK_LIBRARIES(video_analyser
nlohmann_json::nlohmann_json
${Boost_PROGRAM_OPTIONS_LIBRARY}
analyser_lib
files
)
# enable_testing()
# ADD_EXECUTABLE(
# test_suite
# tests/irregularity_test.cpp
# tests/enums_test.cpp
# )
# TARGET_LINK_LIBRARIES(
# test_suite
# GTest::gtest_main
# analyser_lib
# ${OpenCV_LIBRARIES}
# ${Boost_PROGRAM_OPTIONS_LIBRARY}
# nlohmann_json::nlohmann_json
# )
# include(GoogleTest)
# gtest_discover_tests(test_suite)
......@@ -6,7 +6,7 @@ WORKDIR /app
COPY . ./
RUN bash build.sh
RUN make build
RUN pip install --no-cache-dir -r requirements.txt
......
build:
cmake -S . -B build
cmake --build build
test:
cd build && ctest
clean:
rm -rf build
rm -rf bin
run:
./bin/$(TARGET)
......@@ -8,9 +8,7 @@ Implements the Technical Specification of [MPAI CAE-ARP](https://mpai.community/
* Irregularity Images.
## Getting started
The *Video Analyser* is written in C++23 and its executable for MacOS Monterey systems is provided in this repository and it can be used as is.
The program was built using CMake, which is therefore required to build the program for other systems.
The *Video Analyser* is written in C++23. It relies on OpenCV to elaborate Irregularity Images and on Boost C++ Libraries to create the command line interface and generate UUIDs. The configuration file is read with [nlohmann/json](https://github.com/nlohmann/json).
## Installation
[Boost C++ Libraries](https://www.boost.org) are required for creating the command line interface (with [Boost.Program_options](https://www.boost.org/doc/libs/1_81_0/doc/html/program_options.html)) and generating UUIDs (with [Uuid](https://www.boost.org/doc/libs/1_81_0/libs/uuid/doc/uuid.html)).
......@@ -176,3 +174,11 @@ Developed with IDE [Visual Studio Code](https://code.visualstudio.com).
## License
This project is licensed with [GNU GPL v3.0](https://www.gnu.org/licenses/gpl-3.0.html).
# TODO
This section refers to the code delivered by February 2023.
- To be able to work with the "old" neural network (by Ilenya), the output images should correspond to the old "whole tape" where, from the frame judged as interesting, an area corresponding to the height of the tape was extracted (so about the height of the current rectangle) and as wide as the original frame (so 720px). This area will then have to be resized to 224x224 as in the past. If instead you decide to use the new neural network, no changes are needed;
- A resize function of the entire video should be implemented if it does not conform to the PAL standard (currently taken for granted);
- Progressive videos, which do not require deinterlacing, should be managed (in the code there are several steps that operate considering this property);
\ No newline at end of file
TODO
Si riferisce al codice consegnato entro febbraio 2023.
• Per funzionare con la rete neurale "vecchia" (di Ilenya), le immagini in
output dovrebbero corrispondere alle vecchie "nastro intero" dove, dal frame
giudicato come interessante, si estraeva un'area corrispondente all'altezza del
nastro (quindi circa all'altezza del rettangolo attualmente identificato) e
larga tanto quanto il frame originale (quindi 720px). Quest'area dovrà poi
essere ridimensionata in 224x224 come in passato. Se invece si deciderà di
usare la nuova rete neurale, non sono necessarie modifiche;
• Bisognerebbe implementare una funzione di resize di tutto il video se non è
conforme allo standard PAL (attualmente dato per scontato);
• Bisognerebbe gestire i video progressivi, che non necessitano di
deinterlacciamento (nel codice ci sono diversi passaggi che operano
considerando questa proprietà);
\ No newline at end of file
{
"Identifier": {
"ImplementerID": 102,
"Specification": {
"Standard": "CAE",
"AIW": "ARP",
"AIM": "VideoAnalyser",
"Version": "1.00"
}
},
"Description": "This AIM implements the Video Analyser.",
"Types": [{
"Name": "Image_t",
"Type": "uint64[]"
},{
"Name": "IrregularityImages_t",
"Type": "Image_t[]"
},{
"Name": "Video_t",
"Type": "{int32 frameNumber; int16 x; int16 y; byte[] frame}"
},{
"Name": "JSON_t",
"Type": "{byte[] oneByteText | uint16[] twoByteText}"
}],
"Ports": [{
"Name": "PreservationAudioVisualFile",
"Direction": "InputOutput",
"RecordType": "Video_t",
"Technology": "Software",
"Protocol": "",
"IsRemote": false
},{
"Name": "IrregularityFileInput",
"Direction": "InputOutput",
"RecordType": "JSON_t",
"Technology": "Software",
"Protocol": "",
"IsRemote": false
},{
"Name": "IrregularityFileOutput_1",
"Direction": "OutputInput",
"RecordType": "JSON_t",
"Technology": "Software",
"Protocol": "",
"IsRemote": false
},{
"Name": "IrregularityFileOutput_2",
"Direction": "OutputInput",
"RecordType": "JSON_t",
"Technology": "Software",
"Protocol": "",
"IsRemote": false
},{
"Name": "IrregularityImages",
"Direction": "OutputInput",
"RecordType": "IrregularityImages_t",
"Technology": "Software",
"Protocol": "",
"IsRemote": false
}],
"SubAIMs": [],
"Topology": [],
"Implementations": []
}
\ No newline at end of file
#!/usr/bin/env bash
mkdir -p build
cd build
cmake ..
make
\ No newline at end of file
#include <boost/lexical_cast.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include "Irregularity.h"
Irregularity::Irregularity(Source source, string time_label, IrregularityType type, string image_URI)
{
this->id = boost::uuids::random_generator()();
this->source = source;
this->time_label = time_label;
this->type = type;
this->image_URI = image_URI;
}
Irregularity::~Irregularity() {}
json Irregularity::toJSON() {
json j;
j["IrregularityID"] = boost::lexical_cast<string>(this->id);
j["Source"] = sourceToString(this->source);
j["TimeLabel"] = this->time_label;
j["IrregularityType"] = irregularityTypeToString(this->type);
if (!this->image_URI.empty())
j["ImageURI"] = this->image_URI;
return j;
}
Irregularity Irregularity::fromJSON(json j) {
return Irregularity(
sourceFromString(j["Source"]),
j["TimeLabel"],
irregularityTypeFromString(j["IrregularityType"]),
j["ImageURI"]
);
}
#ifndef IRREGULARITY_H
#define IRREGULARITY_H
#include <boost/uuid/uuid.hpp>
#include <nlohmann/json.hpp>
#include "enums.h"
using std::string;
using json = nlohmann::json;
/**
* @brief an irregularity of the tape detected by the system
*
*/
struct Irregularity
{
boost::uuids::uuid id;
Source source;
string time_label;
IrregularityType type;
string image_URI;
Irregularity(Source source, string time_label, IrregularityType type, string image_URI);
~Irregularity();
json toJSON();
static Irregularity fromJSON(json j);
};
#endif // IRREGULARITY_H
\ No newline at end of file
#include <exception>
#include "IrregularityFile.h"
IrregularityFile::IrregularityFile(uint16_t offset, std::list<Irregularity> irregularities)
: offset(offset), irregularities(irregularities) {}
IrregularityFile::~IrregularityFile() {}
json IrregularityFile::toJSON() {
json j;
j["Offset"] = this->offset;
return j;
}
IrregularityFile IrregularityFile::fromJSON(json j) {
throw "Not implemented";
}
uint16_t IrregularityFile::getOffset() const {
return this->offset;
}
std::list<Irregularity> IrregularityFile::getIrregularities() const {
return this->irregularities;
}
IrregularityFile IrregularityFile::add(const Irregularity irregularity) {
this->irregularities.push_back(irregularity);
return *this;
}
IrregularityFile IrregularityFile::remove_by_id(const boost::uuids::uuid id) {
for (auto it = this->irregularities.begin(); it != this->irregularities.end(); ++it) {
if (it->id == id) {
this->irregularities.erase(it);
break;
}
}
return *this;
}
IrregularityFile IrregularityFile::sort() {
this->irregularities.sort([](const Irregularity& a, const Irregularity& b) {
return a.time_label < b.time_label;
});
return *this;
}
\ No newline at end of file
#ifndef IRREGULARITY_FILE_H
#define IRREGULARITY_FILE_H
#include <list>
#include <nlohmann/json.hpp>
#include "Irregularity.h"
using json = nlohmann::json;
class IrregularityFile
{
private:
/* data */
uint16_t offset;
std::list<Irregularity> irregularities;
public:
IrregularityFile(uint16_t offset, std::list<Irregularity> irregularities);
~IrregularityFile();
static IrregularityFile fromJSON(const json j);
json toJSON();
uint16_t getOffset() const;
std::list<Irregularity> getIrregularities() const;
IrregularityFile add(const Irregularity irregularity);
IrregularityFile remove_by_id(const boost::uuids::uuid id);
IrregularityFile sort();
};
#endif // IRREGULARITY_FILE_H
\ No newline at end of file
#include "TimeLabel.h"
TimeLabel::TimeLabel(/* args */)
{
}
TimeLabel::~TimeLabel()
{
}
\ No newline at end of file
#ifndef TIME_LABEL_H
#define TIME_LABEL_H
class TimeLabel
{
private:
/* data */
public:
TimeLabel(/* args */);
~TimeLabel();
};
#endif // TIME_LABEL_H
\ No newline at end of file
#include <stdexcept>
#include "enums.h"
std::string sourceToString(Source source)
{
switch (source) {
case Audio:
return "a";
case Video:
return "v";
case Both:
return "b";
default:
throw std::invalid_argument("Invalid Source");
}
}
Source sourceFromString(std::string source)
{
if (source == "a")
return Audio;
else if (source == "v")
return Video;
else if (source == "b")
return Both;
else
throw std::invalid_argument("Invalid Source");
}
std::string irregularityTypeToString(IrregularityType type)
{
switch (type) {
case BRANDS_ON_TAPE:
return "b";
case SPLICE:
return "sp";
case START_OF_TAPE:
return "sot";
case ENDS_OF_TAPE:
return "eot";
case DAMAGED_TAPE:
return "da";
case DIRT:
return "di";
case MARKS:
return "m";
case SHADOWS:
return "s";
case WOW_AND_FLUTTER:
return "wf";
case PLAY_PAUSE_STOP:
return "pps";
case SPEED:
return "ssv";
case EQUALIZATION:
return "esv";
case SPEED_AND_EQUALIZATION:
return "ssv";
case BACKWARD:
return "sb";
default:
throw std::invalid_argument("Invalid IrregularityType");
}
}
IrregularityType irregularityTypeFromString(std::string type)
{
if (type == "b")
return BRANDS_ON_TAPE;
else if (type == "sp")
return SPLICE;
else if (type == "sot")
return START_OF_TAPE;
else if (type == "eot")
return ENDS_OF_TAPE;
else if (type == "da")
return DAMAGED_TAPE;
else if (type == "di")
return DIRT;
else if (type == "m")
return MARKS;
else if (type == "s")
return SHADOWS;
else if (type == "wf")
return WOW_AND_FLUTTER;
else if (type == "pps")
return PLAY_PAUSE_STOP;
else if (type == "ssv")
return SPEED;
else if (type == "esv")
return EQUALIZATION;
else if (type == "ssv")
return SPEED_AND_EQUALIZATION;
else if (type == "sb")
return BACKWARD;
else
throw std::invalid_argument("Invalid IrregularityType");
}
\ No newline at end of file
#include<string>
enum Source{
Audio,
Video,
Both
};
enum IrregularityType {
BRANDS_ON_TAPE,
SPLICE,
START_OF_TAPE,
ENDS_OF_TAPE,
DAMAGED_TAPE,
DIRT,
MARKS,
SHADOWS,
WOW_AND_FLUTTER,
PLAY_PAUSE_STOP,
SPEED,
EQUALIZATION,
SPEED_AND_EQUALIZATION,
BACKWARD
};
std::string sourceToString(Source source);
Source sourceFromString(std::string source);
std::string irregularityTypeToString(IrregularityType type);
IrregularityType irregularityTypeFromString(std::string type);
#include <iostream>
#include "parser.h"
#include "files.h"
using std::string, std::cout, std::endl, std::cerr;
using std::cout, std::endl, std::cerr, std::ofstream, std::ios;
/**
* @brief Separates video file name from its extension.
*
* @param[in] path Full video path;
* @param[out] fileName Video file name;
* @param[out] extension Video extension.
*/
void findFileNameFromPath(string* path, string* fileName, string* extension) {
void files::saveFile(std::filesystem::path fileName, std::string content, bool append) {
ofstream outputFile;
if (append) {
outputFile.open(fileName, ios::app);
} else {
outputFile.open(fileName);
}
outputFile << content << endl;
outputFile.close();
}
void files::findFileNameFromPath(std::string* path, std::string* fileName, std::string* extension) {
*path = path->substr(path->find_last_of("'") + 1, path->size());
string path_without_extension = path->substr(0, path->find_last_of("."));
std::string path_without_extension = path->substr(0, path->find_last_of("."));
*fileName = path_without_extension.substr(path_without_extension.find_last_of("/") + 1, path_without_extension.size());
*extension = path->substr(path->find_last_of(".") + 1, path->size());
}
int files::findFileName(std::string videoPath, std::string &fileName, std::string &extension) {
/**
* @brief Check if the specified input video file exists and is supported.
*
* @param[in] videoPath Full video path;
* @param[out] fileName Video file name;
* @param[out] extension Video extension.
* @return int -1 if the format is not supported, 0 otherwise.
*/
int findFileName(string videoPath, string &fileName, string &extension) {
findFileNameFromPath(&videoPath, &fileName, &extension);
files::findFileNameFromPath(&videoPath, &fileName, &extension);
if (extension.compare("avi") != 0 && extension.compare("mp4") != 0 && extension.compare("mov") != 0) {
cerr << "Input file extension must be \"avi\", \"mp4\" or \"mov\"." << endl;
......@@ -39,4 +34,5 @@ int findFileName(string videoPath, string &fileName, string &extension) {
cout << " Extension: " << extension << endl;
}
return 0;
}
\ No newline at end of file
}
#include <filesystem>
#include <fstream>
#include <string>
#include <iostream>
#include <stdlib.h>
namespace files {
/**
* @brief Save content to a file
*
* @param fileName the name of the file
* @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);
/**
* @brief Separates video file name from its extension.
*
* @param[in] path Full video path;
* @param[out] fileName Video file name;
* @param[out] extension Video extension.
*/
void findFileNameFromPath(std::string* path, std::string* fileName, std::string* extension);
/**
* @brief Check if the specified input video file exists and is supported.
*
* @param[in] videoPath Full video path;
* @param[out] fileName Video file name;
* @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);
}
\ No newline at end of file
#ifndef FINDFILENAME_H
#define FINDFILENAME_H
#include <stdlib.h>
#include <filesystem>
using std::string;
void findFileNameFromPath(string* path, string* fileName, string* extension);
int findFileName(string videoPath, string &fileName, string &extension);
#endif
\ No newline at end of file
#include "time.h"
using namespace std;
/**
* @brief Convert an int representing milliseconds to the corresponding Time Label string.
*
......@@ -9,7 +7,7 @@ using namespace std;
* @param delim the time separator
* @return string the corresponding Time Label string.
*/
string getTimeLabel(int ms, string delim) {
std::string getTimeLabel(int ms, std::string delim) {
int mil = ms % 1000;
int sec = ms / 1000;
......@@ -17,7 +15,7 @@ string getTimeLabel(int ms, string delim) {
int hours = sec / 3600;
sec = sec % 60;
string hoursStr = to_string(hours), minStr = to_string(min), secStr = to_string(sec), milStr = to_string(mil);
std::string hoursStr = std::to_string(hours), minStr = std::to_string(min), secStr = std::to_string(sec), milStr = std::to_string(mil);
if (hours < 10)
hoursStr = "0" + hoursStr;
if (min < 10)
......@@ -32,7 +30,7 @@ string getTimeLabel(int ms, string delim) {
}
}
string timeLabel = hoursStr + delim + minStr + delim + secStr + delim + milStr;
std::string timeLabel = hoursStr + delim + minStr + delim + secStr + delim + milStr;
return timeLabel;
}
......@@ -9,13 +9,11 @@
* WARNING:
* Currently, this program is only compatible with the Studer A810 and videos recorded in PAL standard.
*
* @author Nadir Dalla Pozza
* @authors Nadir Dalla Pozza <nadir.dallapozza@unipd.it>, Matteo Spanio <dev2@audioinnova.com>
* @copyright 2022, Audio Innova S.r.l.
* @credits Niccolò Pretto, Nadir Dalla Pozza, Sergio Canazza
* @license GPL v3.0
* @version 1.0.1
* @maintainer Nadir Dalla Pozza
* @email nadir.dallapozza@unipd.it
* @version 1.1.0
* @status Production
*/
#include <filesystem>
......@@ -47,9 +45,9 @@
#include "utility.h"
#include "forAudioAnalyser.h"
#include "lib/files.h"
#include "lib/colors.h"
#include "lib/time.h"
#include "lib/parser.h"
using namespace cv;
using namespace std;
......@@ -57,7 +55,6 @@ using json = nlohmann::json;
namespace fs = std::filesystem;
namespace po = boost::program_options;
// For capstan detection, there are two alternative approaches:
// Generalized Hough Transform and SURF.
bool useSURF = true;
......@@ -66,20 +63,18 @@ bool savingPinchRoller = false;
bool pinchRollerRect = false;
bool savingBrand = false;
bool endTapeSaved = false;
cv::Mat myFrame;
float mediaPrevFrame = 0;
bool firstBrand = true; // The first frame containing brands on tape must be saved
float firstInstant = 0;
string fileName, extension;
// Path variables
fs::path outputPath;
fs::path irregularityImagesPath;
fs::path videoPath;
fs::path outputPath {};
fs::path irregularityImagesPath {};
// JSON files
json configurationFile;
json irregularityFileOutput1;
json irregularityFileOutput2;
static json configurationFile {};
static json irregularityFileOutput1 {};
static json irregularityFileOutput2 {};
// RotatedRect identifying the processing area
RotatedRect rect, rectTape, rectCapstan;
......@@ -105,20 +100,14 @@ struct SceneObject {
Threshold threshold;
};
Config config;
SceneObject tape;
SceneObject capstan;
static Config config;
static SceneObject tape;
static SceneObject capstan;
void saveFile(fs::path fileName, auto content, bool append) {
ofstream outputFile;
if (append) {
outputFile.open(fileName, ios::app);
} else {
outputFile.open(fileName);
}
outputFile << content << endl;
outputFile.close();
}
// Constants Paths
static const string READING_HEAD_IMG = "images/reading_head.png";
static const string CAPSTAN_TEMPLATE_IMG = "input/capstanBERIO058prova.png";
static const string CONFIG_FILE = "config/config.json";
/**
* @brief Get operation arguments from command line or config.json file.
......@@ -128,17 +117,17 @@ void saveFile(fs::path fileName, auto content, bool append) {
* @return true if input configuration is valid;
* @return false otherwise.
*/
bool getArguments(int argc, char** argv) {
bool getArguments(int argc, const char** argv) {
// Read configuration file
ifstream iConfig("config/config.json");
ifstream iConfig(CONFIG_FILE);
iConfig >> configurationFile;
if (argc == 1) {
// Read from JSON file
string wp = configurationFile["WorkingPath"];
string working_path = configurationFile["WorkingPath"];
config = {
fs::path(wp),
fs::path(working_path),
configurationFile["FilesName"],
configurationFile["Brands"],
configurationFile["Speed"]
......@@ -211,25 +200,31 @@ bool getArguments(int argc, char** argv) {
* - The reading head;
* - The tape area under the tape head (computed on the basis of the detected reading head);
* - The capstan.
*
* @param myFrame The current frame of the video.
* @return true if some areas have been detected;
* @return false otherwise.
*/
bool findProcessingAreas() {
bool findProcessingAreas(Mat myFrame) {
/*********************************************************************************************/
/*********************************** READING HEAD DETECTION **********************************/
/*********************************************************************************************/
// Obtain grayscale version of myFrame
Mat myFrameGrayscale;
utility::Frame myFrameUtility(myFrame);
myFrameUtility
.convertColor(COLOR_BGR2GRAY)
.downsample(2);
utility::Frame templateImageUtility(cv::imread(READING_HEAD_IMG, IMREAD_GRAYSCALE));
templateImageUtility.downsample(2);
// Save a grayscale version of myFrame in myFrameGrayscale and downsample it in half pixels for performance reasons
Mat myFrameGrayscale, myFrameGrayscaleHalf;
cvtColor(myFrame, myFrameGrayscale, COLOR_BGR2GRAY);
// Get input shape in grayscale
Mat templateImage = imread("input/readingHead.png", IMREAD_GRAYSCALE);
// Downsample myFrameGrayscale in half pixels for performance reasons
Mat myFrameGrayscaleHalf;
pyrDown(myFrameGrayscale, myFrameGrayscaleHalf, Size(myFrame.cols/2, myFrame.rows/2));
// Downsample tapeShape in half pixels
// Get input shape in grayscale and downsample it in half pixels
Mat templateImage = cv::imread(READING_HEAD_IMG, IMREAD_GRAYSCALE);
Mat templateImageHalf;
pyrDown(templateImage, templateImageHalf, Size(templateImage.cols/2, templateImage.rows/2));
......@@ -240,11 +235,11 @@ bool findProcessingAreas() {
Mat templateShape = templateImageHalf;
// Algorithm and parameters
// for informations about the Generalized Hough Guild interface see the tutorial at https://docs.opencv.org/4.7.0/da/ddc/tutorial_generalized_hough_ballard_guil.html
Ptr<GeneralizedHoughGuil> alg = createGeneralizedHoughGuil();
vector<Vec4f> positionsPos, positionsNeg;
Mat votesPos, votesNeg;
TickMeter tm;
int oldPosThresh = tape.threshold.pos;
RotatedRect rectPos, rectNeg;
ofstream myFile;
......@@ -256,33 +251,29 @@ bool findProcessingAreas() {
double maxValPos = 0, maxValNeg = 0;
int indexPos = 0, indexNeg = 0;
alg -> setMinDist(tape.minDist);
alg -> setLevels(360);
alg -> setDp(2);
alg -> setMaxBufferSize(1000);
alg->setMinDist(tape.minDist);
alg->setLevels(360);
alg->setDp(2);
alg->setMaxBufferSize(1000);
alg -> setAngleStep(1);
alg -> setAngleThresh(tape.threshold.angle);
alg->setAngleStep(1);
alg->setAngleThresh(tape.threshold.angle);
alg -> setMinScale(0.9);
alg -> setMaxScale(1.1);
alg -> setScaleStep(0.01);
alg -> setScaleThresh(tape.threshold.scale);
alg->setMinScale(0.9);
alg->setMaxScale(1.1);
alg->setScaleStep(0.01);
alg->setScaleThresh(tape.threshold.scale);
alg -> setPosThresh(tape.threshold.pos);
alg->setPosThresh(tape.threshold.pos);
alg -> setCannyLowThresh(150); // Old: 100
alg -> setCannyHighThresh(240); // Old: 300
alg->setCannyLowThresh(150); // Old: 100
alg->setCannyHighThresh(240); // Old: 300
alg -> setTemplate(templateShape);
alg->setTemplate(templateShape);
cout << DARK_CYAN << "Reading head" << END << endl;
tm.start();
// Invoke utility.h function
detectShape(alg, templateShape, tape.threshold.pos, positionsPos, votesPos, positionsNeg, votesNeg, processingImage);
tm.stop();
cout << "Reading head detection time: " << tm.getTimeMilli() << " ms" << endl;
utility::detectShape(alg, templateShape, tape.threshold.pos, positionsPos, votesPos, positionsNeg, votesNeg, processingImage);
for (int i = 0; i < votesPos.size().width; i++) {
if (votesPos.at<int>(i) >= maxValPos) {
......@@ -300,9 +291,9 @@ bool findProcessingAreas() {
// The color is progressively darkened to emphasize that the algorithm found more than one shape
if (positionsPos.size() > 0)
rectPos = drawShapes(myFrame, positionsPos[indexPos], Scalar(0, 0, 255-indexPos*64), templateImageHalf.cols, templateImageHalf.rows, myFrameGrayscaleHalf.cols/4, myFrameGrayscaleHalf.rows/2, 2);
rectPos = utility::drawShapes(myFrame, positionsPos[indexPos], Scalar(0, 0, 255-indexPos*64), templateImageHalf.cols, templateImageHalf.rows, myFrameGrayscaleHalf.cols/4, myFrameGrayscaleHalf.rows/2, 2);
if (positionsNeg.size() > 0)
rectNeg = drawShapes(myFrame, positionsNeg[indexNeg], Scalar(128, 128, 255-indexNeg*64), templateImageHalf.cols, templateImageHalf.rows, myFrameGrayscaleHalf.cols/4, myFrameGrayscaleHalf.rows/2, 2);
rectNeg = utility::drawShapes(myFrame, positionsNeg[indexNeg], Scalar(128, 128, 255-indexNeg*64), templateImageHalf.cols, templateImageHalf.rows, myFrameGrayscaleHalf.cols/4, myFrameGrayscaleHalf.rows/2, 2);
myFile.open("log.txt", ios::app);
......@@ -336,7 +327,7 @@ bool findProcessingAreas() {
// 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, rect.angle );
rectTape = drawShapes(myFrame, positionTape, Scalar(0, 255-indexPos*64, 0), rect.size.width, 50 * (rect.size.width / 200), 0, 0, 1);
rectTape = utility::drawShapes(myFrame, positionTape, Scalar(0, 255-indexPos*64, 0), rect.size.width, 50 * (rect.size.width / 200), 0, 0, 1);
myFile << "Tape area:" << endl;
myFile << " Center (x, y): (" << rectTape.center.x << ", " << rectTape.center.y << ")" << endl;
......@@ -359,14 +350,14 @@ bool findProcessingAreas() {
}
};
saveFile(fs::path("./" + fileName + ".json"), autoJSON.dump(4), false);
files::saveFile(fs::path("./" + fileName + ".json"), autoJSON.dump(4), false);
/*********************************************************************************************/
/************************************* CAPSTAN DETECTION *************************************/
/*********************************************************************************************/
// Read template image - it is smaller than before, therefore there is no need to downsample
templateShape = imread("input/capstanBERIO058prova.png", IMREAD_GRAYSCALE); // WORKING
templateShape = imread(CAPSTAN_TEMPLATE_IMG, IMREAD_GRAYSCALE);
// templateShape = imread("../input/capstanBERIO058.png", IMREAD_GRAYSCALE);
cout << DARK_CYAN << "Capstan" << END << endl;
......@@ -379,12 +370,8 @@ bool findProcessingAreas() {
vector<KeyPoint> keypoints_object, keypoints_scene;
Mat descriptors_object, descriptors_scene;
tm.reset();
tm.start();
detector->detectAndCompute(templateShape, noArray(), keypoints_object, descriptors_object);
detector->detectAndCompute(myFrameGrayscale, noArray(), keypoints_scene, descriptors_scene);
tm.stop();
cout << "Capstan detection time: " << tm.getTimeMilli() << " ms" << endl;
// Step 2: Matching descriptor vectors with a FLANN based matcher
// Since SURF is a floating-point descriptor NORM_L2 is used
......@@ -428,7 +415,7 @@ bool findProcessingAreas() {
// +10 in X for centering and -20 in width
// +45 in Y for centering and -90 in height
Vec4f positionCapstan(capstanX + 10, capstanY + 45, 1, 0);
rectCapstan = drawShapes(myFrame, positionCapstan, Scalar(255-indexPos*64, 0, 0), templateShape.cols - 20, templateShape.rows - 90, 0, 0, 1);
rectCapstan = utility::drawShapes(myFrame, positionCapstan, Scalar(255-indexPos*64, 0, 0), templateShape.cols - 20, templateShape.rows - 90, 0, 0, 1);
} else {
......@@ -467,11 +454,7 @@ bool findProcessingAreas() {
vector<Vec4f> positionsC1Pos, positionsC1Neg;
Mat votesC1Pos, votesC1Neg;
tm.reset();
tm.start();
detectShape(alg, templateShape, capstan.threshold.pos, positionsC1Pos, votesC1Pos, positionsC1Neg, votesC1Neg, capstanProcessingAreaGrayscale);
tm.stop();
cout << "Capstan detection time: " << tm.getTimeMilli() << " ms" << endl;
utility::detectShape(alg, templateShape, capstan.threshold.pos, positionsC1Pos, votesC1Pos, positionsC1Neg, votesC1Neg, capstanProcessingAreaGrayscale);
// Find the best matches for positive and negative angles
// If there are more than one shapes, then choose the one with the highest score
......@@ -494,9 +477,9 @@ bool findProcessingAreas() {
RotatedRect rectCapstanPos, rectCapstanNeg;
if (positionsC1Pos.size() > 0)
rectCapstanPos = drawShapes(myFrame, positionsC1Pos[indexPos], Scalar(255-indexPos*64, 0, 0), templateShape.cols-22, templateShape.rows-92, capstanProcessingAreaRectX+11, capstanProcessingAreaRectY+46, 1);
rectCapstanPos = utility::drawShapes(myFrame, positionsC1Pos[indexPos], Scalar(255-indexPos*64, 0, 0), templateShape.cols-22, templateShape.rows-92, capstanProcessingAreaRectX+11, capstanProcessingAreaRectY+46, 1);
if (positionsC1Neg.size() > 0)
rectCapstanNeg = drawShapes(myFrame, positionsC1Neg[indexNeg], Scalar(255-indexNeg*64, 128, 0), templateShape.cols-22, templateShape.rows-92, capstanProcessingAreaRectX+11, capstanProcessingAreaRectY+46, 1);
rectCapstanNeg = utility::drawShapes(myFrame, positionsC1Neg[indexNeg], Scalar(255-indexNeg*64, 128, 0), templateShape.cols-22, templateShape.rows-92, capstanProcessingAreaRectX+11, capstanProcessingAreaRectY+46, 1);
if (maxValPos > 0)
if (maxValNeg > 0)
......@@ -582,7 +565,7 @@ bool frameDifference(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd) {
// END CODE FROM https://answers.opencv.org/question/497/extract-a-rotatedrect-area/
cv::Mat differenceFrame = difference(croppedPrevFrame, croppedCurrentFrame);
cv::Mat differenceFrame = utility::difference(croppedPrevFrame, croppedCurrentFrame);
int blackPixelsCapstan = 0;
......@@ -638,7 +621,7 @@ bool frameDifference(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd) {
// END CODE FROM https://answers.opencv.org/question/497/extract-a-rotatedrect-area/
cv::Mat differenceFrame = difference(croppedPrevFrame, croppedCurrentFrame);
cv::Mat differenceFrame = utility::difference(croppedPrevFrame, croppedCurrentFrame);
int decEnd = (msToEnd % 1000) / 100;
int secEnd = (msToEnd - (msToEnd % 1000)) / 1000;
......@@ -758,7 +741,7 @@ void processing(cv::VideoCapture videoCapture) {
// De-interlacing frame
cv::Mat oddFrame(frame.rows/2, frame.cols, CV_8UC3);
cv::Mat evenFrame(frame.rows/2, frame.cols, CV_8UC3);
separateFrame(frame, oddFrame, evenFrame);
utility::separateFrame(frame, oddFrame, evenFrame);
// Extract the image corresponding to the ROIs
Point2f pts[4];
......@@ -774,7 +757,7 @@ void processing(cv::VideoCapture videoCapture) {
if (subImage.rows % 2 != 0) // If the found rectangle is of odd height, we must increase evenSubImage height by 1, otherwise we have segmentation_fault!!!
evenSubImageRows += 1;
cv::Mat evenSubImage(evenSubImageRows, subImage.cols, CV_8UC3);
separateFrame(subImage, oddSubImage, evenSubImage);
utility::separateFrame(subImage, oddSubImage, evenSubImage);
string timeLabel = getTimeLabel(ms, ":");
string safeTimeLabel = getTimeLabel(ms, "-");
......@@ -850,6 +833,8 @@ int main(int argc, char** argv) {
json irregularityFileInput;
fs::path irregularityFileInputPath;
cv::Mat myFrame;
/*********************************************************************************************/
/*************************************** CONFIGURATION ***************************************/
/*********************************************************************************************/
......@@ -865,9 +850,10 @@ int main(int argc, char** argv) {
return -1;
}
videoPath = config.workingPath / "PreservationAudioVisualFile" / config.filesName;
if (findFileName(videoPath, fileName, extension) == -1) {
cerr << RED << BOLD << "config.json error!" << END << endl << RED << videoPath.string() << " cannot be found or opened." << END << endl;
fs::path VIDEO_PATH = config.workingPath / "PreservationAudioVisualFile" / config.filesName;
if (files::findFileName(VIDEO_PATH, fileName, extension) == -1) {
cerr << RED << BOLD << "config.json error!" << END << endl << RED << VIDEO_PATH.string() << " cannot be found or opened." << END << endl;
return -1;
}
......@@ -928,14 +914,14 @@ int main(int argc, char** argv) {
fs::path logPath = outputPath / "log.txt";
string logInfo = fileName + '\n' + "tsh: " + to_string(tape.threshold.percentual) + " tshp: " + to_string(capstan.threshold.percentual) + '\n' + ts;
saveFile(logPath, logInfo, true);
files::saveFile(logPath, logInfo, true);
/*********************************************************************************************/
/************************************** AREAS DETECTION **************************************/
/*********************************************************************************************/
cv::VideoCapture videoCapture(videoPath);
cv::VideoCapture videoCapture(VIDEO_PATH);
if (!videoCapture.isOpened()) {
cerr << RED << BOLD << "Video unreadable." << END << endl;
return -1;
......@@ -951,7 +937,7 @@ int main(int argc, char** argv) {
cout << "Video resolution: " << myFrame.cols << "x" << myFrame.rows << endl << endl;
// Find the processing area corresponding to the tape area over the reading head
bool found = findProcessingAreas();
bool found = findProcessingAreas(myFrame);
// Reset frame position
videoCapture.set(CAP_PROP_POS_FRAMES, 0);
......@@ -961,11 +947,11 @@ int main(int argc, char** argv) {
if (found) {
message = "Processing areas found!\n";
cout << message;
saveFile(logPath, message, true);
files::saveFile(logPath, message, true);
} else {
message = "Processing area not found. Try changing JSON parameters.\n";
cout << message;
saveFile(logPath, message, true);
files::saveFile(logPath, message, true);
return -1; // Program terminated early
}
......@@ -995,20 +981,20 @@ int main(int argc, char** argv) {
string result("Processing elapsed time: " + to_string((int)min) + ":" + to_string((int)sec));
cout << endl << result << endl;
saveFile("log.txt", result + '\n', true);
files::saveFile("log.txt", result + '\n', true);
/*********************************************************************************************/
/************************************* IRREGULARITY FILES ************************************/
/*********************************************************************************************/
fs::path outputFile1Name = outputPath / "VideoAnalyser_IrregularityFileOutput1.json";
saveFile(outputFile1Name, irregularityFileOutput1.dump(4), false);
files::saveFile(outputFile1Name, irregularityFileOutput1.dump(4), false);
// Irregularities to extract for the AudioAnalyser and to the TapeIrregularityClassifier
extractIrregularityImagesForAudio(outputPath, videoPath, irregularityFileInput, irregularityFileOutput2);
extractIrregularityImagesForAudio(outputPath, VIDEO_PATH, irregularityFileInput, irregularityFileOutput2);
fs::path outputFile2Name = outputPath / "VideoAnalyser_IrregularityFileOutput2.json";
saveFile(outputFile2Name, irregularityFileOutput2.dump(4), false);
files::saveFile(outputFile2Name, irregularityFileOutput2.dump(4), false);
return 0;
}
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