Commit e4ffbd13 authored by Matteo's avatar Matteo
Browse files

update

parent e2ba3b87
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <iostream> #include <iostream>
#include <stdlib.h> #include <stdlib.h>
#include <sys/timeb.h> #include <sys/timeb.h>
#include <ranges>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include <boost/uuid/uuid.hpp> // uuid class #include <boost/uuid/uuid.hpp> // uuid class
...@@ -108,6 +109,10 @@ static const string READING_HEAD_IMG = "input/readingHead.png"; ...@@ -108,6 +109,10 @@ static const string READING_HEAD_IMG = "input/readingHead.png";
static const string CAPSTAN_TEMPLATE_IMG = "input/capstanBERIO058prova.png"; static const string CAPSTAN_TEMPLATE_IMG = "input/capstanBERIO058prova.png";
static const string CONFIG_FILE = "config/config.json"; static const string CONFIG_FILE = "config/config.json";
double rotatedRectArea(RotatedRect rect) {
return rect.size.width * rect.size.height;
}
/** /**
* @brief Get operation arguments from command line or config.json file. * @brief Get operation arguments from command line or config.json file.
* *
...@@ -290,7 +295,6 @@ bool findProcessingAreas(Mat myFrame) { ...@@ -290,7 +295,6 @@ bool findProcessingAreas(Mat myFrame) {
); );
Mat processingImage = halved_gray_current_frame(readingHeadProcessingAreaRect); Mat processingImage = halved_gray_current_frame(readingHeadProcessingAreaRect);
ofstream myFile;
RotatedRect rectPos, rectNeg; RotatedRect rectPos, rectNeg;
auto [indexPos, indexNeg, maxValPos, maxValNeg, positionsPos, positionsNeg] = findObject(reading_head_template, tape, processingImage); auto [indexPos, indexNeg, maxValPos, maxValNeg, positionsPos, positionsNeg] = findObject(reading_head_template, tape, processingImage);
...@@ -300,26 +304,19 @@ bool findProcessingAreas(Mat myFrame) { ...@@ -300,26 +304,19 @@ bool findProcessingAreas(Mat myFrame) {
if (positionsNeg.size() > 0) if (positionsNeg.size() > 0)
rectNeg = utility::drawShapes(myFrame, positionsNeg[indexNeg], Scalar(128, 128, 255-indexNeg*64), reading_head_template.cols, reading_head_template.rows, halved_gray_current_frame.cols/4, halved_gray_current_frame.rows/2, 2); rectNeg = utility::drawShapes(myFrame, positionsNeg[indexNeg], Scalar(128, 128, 255-indexNeg*64), reading_head_template.cols, reading_head_template.rows, halved_gray_current_frame.cols/4, halved_gray_current_frame.rows/2, 2);
myFile.open("log.txt", ios::app);
if (maxValPos > 0) if (maxValPos > 0)
if (maxValNeg > 0) if (maxValNeg > 0)
if (maxValPos > maxValNeg) { if (maxValPos > maxValNeg) {
myFile << "READING HEAD: Positive angle is best, match number: " << indexPos << endl;
rect = rectPos; rect = rectPos;
} else { } else {
myFile << "READING HEAD: Negative angle is best, match number: " << indexNeg << endl;
rect = rectNeg; rect = rectNeg;
} }
else { else {
myFile << "READING HEAD: Positive angle is the only choice, match number: " << indexPos << endl;
rect = rectPos; rect = rectPos;
} }
else if (maxValNeg > 0) { else if (maxValNeg > 0) {
myFile << "READING HEAD: Negative angle is the only choice, match number: " << indexNeg << endl;
rect = rectNeg; rect = rectNeg;
} else { } else {
myFile.close();
return false; return false;
} }
...@@ -331,11 +328,6 @@ bool findProcessingAreas(Mat myFrame) { ...@@ -331,11 +328,6 @@ bool findProcessingAreas(Mat myFrame) {
Vec4f positionTape( rect.center.x, rect.center.y + rect.size.height / 2 + 20 * (rect.size.width / 200), 1, rect.angle ); Vec4f positionTape( rect.center.x, rect.center.y + rect.size.height / 2 + 20 * (rect.size.width / 200), 1, rect.angle );
rectTape = utility::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;
myFile << " Size (w, h): (" << rectTape.size.width << ", " << rectTape.size.height << ")" << endl;
myFile << " Angle (deg): (" << rectTape.angle << ")" << endl;
json autoJSON; json autoJSON;
autoJSON["PreservationAudioVisualFile"] = fileName; autoJSON["PreservationAudioVisualFile"] = fileName;
autoJSON["RotatedRect"] = { autoJSON["RotatedRect"] = {
...@@ -439,31 +431,20 @@ bool findProcessingAreas(Mat myFrame) { ...@@ -439,31 +431,20 @@ bool findProcessingAreas(Mat myFrame) {
if (maxValPos > 0) if (maxValPos > 0)
if (maxValNeg > 0) if (maxValNeg > 0)
if (maxValPos > maxValNeg) { if (maxValPos > maxValNeg) {
myFile << "CAPSTAN: Positive is best, match number: " << indexPos << endl;
rectCapstan = rectCapstanPos; rectCapstan = rectCapstanPos;
} else { } else {
myFile << "CAPSTAN: Negative is best, match number: " << indexNeg << endl;
rectCapstan = rectCapstanNeg; rectCapstan = rectCapstanNeg;
} }
else { else {
myFile << "CAPSTAN: Positive is the only choice, match number: " << indexPos << endl;
rectCapstan = rectCapstanPos; rectCapstan = rectCapstanPos;
} }
else if (maxValNeg > 0) { else if (maxValNeg > 0) {
myFile << "CAPSTAN: Negative is the only choice, match number: " << indexNeg << endl;
rectCapstan = rectCapstanNeg; rectCapstan = rectCapstanNeg;
} else { } else {
myFile.close();
return false; return false;
} }
} }
myFile << "Capstan ROI:" << endl;
myFile << " Center (x, y): (" << rectCapstan.center.x << ", " << rectCapstan.center.y << ")" << endl;
myFile << " Size (w, h): (" << rectCapstan.size.width << ", " << rectCapstan.size.height << ")" << endl;
myFile << " Angle (deg): (" << rectCapstan.angle << ")" << endl;
myFile.close();
cout << endl; cout << endl;
// Save the image containing the detected areas // Save the image containing the detected areas
...@@ -549,7 +530,7 @@ bool frameDifference(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd) { ...@@ -549,7 +530,7 @@ bool frameDifference(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd) {
/*********************************************************************************************/ /*********************************************************************************************/
// Tape area // Tape area
int tapeAreaPixels = rectTape.size.width * rectTape.size.height; int tapeAreaPixels = rotatedRectArea(rectTape);
float tapeDifferentPixelsThreshold = tapeAreaPixels * tape.threshold.percentual / 100; float tapeDifferentPixelsThreshold = tapeAreaPixels * tape.threshold.percentual / 100;
// Extract matrices corresponding to the processing area // Extract matrices corresponding to the processing area
...@@ -643,13 +624,11 @@ bool frameDifference(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd) { ...@@ -643,13 +624,11 @@ bool frameDifference(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd) {
*/ */
void processing(cv::VideoCapture videoCapture) { void processing(cv::VideoCapture videoCapture) {
// Video duration const int video_length_ms = ((float) videoCapture.get(CAP_PROP_FRAME_COUNT) / videoCapture.get(CAP_PROP_FPS)) * 1000;
int frameNumbers_v = videoCapture.get(CAP_PROP_FRAME_COUNT); int video_current_ms = videoCapture.get(CAP_PROP_POS_MSEC);
float fps_v = videoCapture.get(CAP_PROP_FPS); // FPS can be non-integers!!! // counters
float videoLength = (float) frameNumbers_v / fps_v; // [s] int savedFrames = 0;
int videoLength_ms = videoLength * 1000; int unsavedFrames = 0;
int savedFrames = 0, unsavedFrames = 0;
float lastSaved = -160; 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). // 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. 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.
...@@ -660,110 +639,100 @@ void processing(cv::VideoCapture videoCapture) { ...@@ -660,110 +639,100 @@ void processing(cv::VideoCapture videoCapture) {
// The first frame of the video won't be processed // The first frame of the video won't be processed
cv::Mat prevFrame; cv::Mat prevFrame;
videoCapture >> prevFrame; videoCapture >> prevFrame;
firstInstant = videoLength_ms - videoCapture.get(CAP_PROP_POS_MSEC); firstInstant = video_length_ms - video_current_ms;
while (videoCapture.isOpened()) { while (videoCapture.isOpened()) {
cv::Mat frame; cv::Mat frame;
videoCapture >> frame; videoCapture >> frame;
video_current_ms = videoCapture.get(CAP_PROP_POS_MSEC);
if (!frame.empty()) { if (frame.empty()) {
int ms = videoCapture.get(CAP_PROP_POS_MSEC);
int msToEnd = videoLength_ms - ms;
if (ms == 0) // With OpenCV library, this happens at the last few frames of the video before realising that "frame" is empty.
break;
// Variables to display program status
int secToEnd = msToEnd / 1000;
int minToEnd = (secToEnd / 60) % 60;
secToEnd = secToEnd % 60;
string secStrToEnd = to_string(secToEnd), minStrToEnd = to_string(minToEnd);
if (minToEnd < 10)
minStrToEnd = "0" + minStrToEnd;
if (secToEnd < 10)
secStrToEnd = "0" + secStrToEnd;
// Display program status
cout << "\rIrregularities: " << savedFrames << ". ";
cout << "Remaining video time [mm:ss]: " << minStrToEnd << ":" << secStrToEnd << flush;
if ((ms - lastSaved > savingRate) && frameDifference(prevFrame, frame, msToEnd)) {
// An Irregularity has been found!
// De-interlacing frame
cv::Mat oddFrame(frame.rows/2, frame.cols, CV_8UC3);
cv::Mat evenFrame(frame.rows/2, frame.cols, CV_8UC3);
utility::separateFrame(frame, oddFrame, evenFrame);
// Extract the image corresponding to the ROIs
Point2f pts[4];
if (savingPinchRoller)
rectCapstan.points(pts);
else
rectTape.points(pts);
cv::Mat subImage(frame, cv::Rect(100, min(pts[1].y, pts[2].y), frame.cols - 100, static_cast<int>(rectTape.size.height)));
// De-interlacing
cv::Mat oddSubImage(subImage.rows/2, subImage.cols, CV_8UC3);
int evenSubImageRows = subImage.rows/2;
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);
utility::separateFrame(subImage, oddSubImage, evenSubImage);
string timeLabel = getTimeLabel(ms, ":");
string safeTimeLabel = getTimeLabel(ms, "-");
string irregularityImageFilename = to_string(savedFrames) + "_" + safeTimeLabel + ".jpg";
cv::imwrite(irregularityImagesPath / irregularityImageFilename, oddFrame);
// Append Irregularity information to JSON
boost::uuids::uuid uuid = boost::uuids::random_generator()();
irregularityFileOutput1["Irregularities"] += {
{
"IrregularityID", boost::lexical_cast<string>(uuid)
}, {
"Source", "v"
}, {
"TimeLabel", timeLabel
}
};
irregularityFileOutput2["Irregularities"] += {
{
"IrregularityID", boost::lexical_cast<string>(uuid)
}, {
"Source", "v"
}, {
"TimeLabel", timeLabel
}, {
"ImageURI", irregularityImagesPath.string() + "/" + irregularityImageFilename
}
};
lastSaved = ms;
savedFrames++;
} else {
unsavedFrames++;
}
prevFrame = frame;
} else {
cout << endl << "Empty frame!" << endl; cout << endl << "Empty frame!" << endl;
videoCapture.release(); videoCapture.release();
break; break;
} }
}
int msToEnd = video_length_ms - video_current_ms;
if (video_current_ms == 0) // With OpenCV library, this happens at the last few frames of the video before realising that "frame" is empty.
break;
// Variables to display program status
int secToEnd = msToEnd / 1000;
int minToEnd = (secToEnd / 60) % 60;
secToEnd = secToEnd % 60;
string secStrToEnd = to_string(secToEnd), minStrToEnd = to_string(minToEnd);
if (minToEnd < 10)
minStrToEnd = "0" + minStrToEnd;
if (secToEnd < 10)
secStrToEnd = "0" + secStrToEnd;
// Display program status
cout << "\rIrregularities: " << savedFrames << ". ";
cout << "Remaining video time [mm:ss]: " << minStrToEnd << ":" << secStrToEnd << flush;
if ((video_current_ms - lastSaved > savingRate) && frameDifference(prevFrame, frame, msToEnd)) {
// An Irregularity has been found!
// De-interlacing frame
cv::Mat oddFrame(frame.rows/2, frame.cols, CV_8UC3);
cv::Mat evenFrame(frame.rows/2, frame.cols, CV_8UC3);
utility::separateFrame(frame, oddFrame, evenFrame);
// Extract the image corresponding to the ROIs
Point2f pts[4];
if (savingPinchRoller)
rectCapstan.points(pts);
else
rectTape.points(pts);
cv::Mat subImage(frame, cv::Rect(100, min(pts[1].y, pts[2].y), frame.cols - 100, static_cast<int>(rectTape.size.height)));
// De-interlacing
cv::Mat oddSubImage(subImage.rows/2, subImage.cols, CV_8UC3);
int evenSubImageRows = subImage.rows/2;
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);
utility::separateFrame(subImage, oddSubImage, evenSubImage);
string timeLabel = getTimeLabel(video_current_ms, ":");
string safeTimeLabel = getTimeLabel(video_current_ms, "-");
string irregularityImageFilename = to_string(savedFrames) + "_" + safeTimeLabel + ".jpg";
cv::imwrite(irregularityImagesPath / irregularityImageFilename, oddFrame);
// Append Irregularity information to JSON
boost::uuids::uuid uuid = boost::uuids::random_generator()();
irregularityFileOutput1["Irregularities"] += {
{
"IrregularityID", boost::lexical_cast<string>(uuid)
}, {
"Source", "v"
}, {
"TimeLabel", timeLabel
}
};
irregularityFileOutput2["Irregularities"] += {
{
"IrregularityID", boost::lexical_cast<string>(uuid)
}, {
"Source", "v"
}, {
"TimeLabel", timeLabel
}, {
"ImageURI", irregularityImagesPath.string() + "/" + irregularityImageFilename
}
};
ofstream myFile; lastSaved = video_current_ms;
myFile.open("log.txt", ios::app); savedFrames++;
myFile << "Saved frames are: " << savedFrames << endl;
myFile.close();
} else {
unsavedFrames++;
}
prevFrame = frame;
}
} }
...@@ -805,7 +774,7 @@ int main(int argc, char** argv) { ...@@ -805,7 +774,7 @@ int main(int argc, char** argv) {
return -1; return -1;
} }
fs::path VIDEO_PATH = config.workingPath / "PreservationAudioVisualFile" / config.filesName; const fs::path VIDEO_PATH = config.workingPath / "PreservationAudioVisualFile" / config.filesName;
if (files::findFileName(VIDEO_PATH, fileName, extension) == -1) { 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; cerr << RED << BOLD << "config.json error!" << END << endl << RED << VIDEO_PATH.string() << " cannot be found or opened." << END << endl;
...@@ -862,15 +831,9 @@ int main(int argc, char** argv) { ...@@ -862,15 +831,9 @@ int main(int argc, char** argv) {
// Make directory with fileName name // Make directory with fileName name
outputPath = config.workingPath / "temp" / fileName; outputPath = config.workingPath / "temp" / fileName;
int outputFileNameDirectory = create_directory(outputPath); int outputFileNameDirectory = create_directory(outputPath);
// Get now time
time_t t = chrono::system_clock::to_time_t(chrono::system_clock::now());
string ts = ctime(&t);
// Write useful info to log file
fs::path logPath = outputPath / "log.txt";
string logInfo = fileName + '\n' + "tsh: " + to_string(tape.threshold.percentual) + " tshp: " + to_string(capstan.threshold.percentual) + '\n' + ts;
files::saveFile(logPath, logInfo, true);
irregularityImagesPath = outputPath / "IrregularityImages";
int fullFrameDirectory = fs::create_directory(irregularityImagesPath);
/*********************************************************************************************/ /*********************************************************************************************/
/************************************** AREAS DETECTION **************************************/ /************************************** AREAS DETECTION **************************************/
...@@ -882,41 +845,24 @@ int main(int argc, char** argv) { ...@@ -882,41 +845,24 @@ int main(int argc, char** argv) {
return -1; return -1;
} }
// Get total number of frames int frames_number = videoCapture.get(CAP_PROP_FRAME_COUNT);
int totalFrames = videoCapture.get(CAP_PROP_FRAME_COUNT);
// Set frame position to half video length // Set frame position to half video length
videoCapture.set(CAP_PROP_POS_FRAMES, totalFrames/2); videoCapture.set(CAP_PROP_POS_FRAMES, frames_number/2);
// Get frame // Get frame
videoCapture >> myFrame; videoCapture >> myFrame;
cout << "Video resolution: " << myFrame.cols << "x" << myFrame.rows << endl << endl; cout << "Video resolution: " << myFrame.cols << "x" << myFrame.rows << endl;
// Find the processing area corresponding to the tape area over the reading head
bool found = findProcessingAreas(myFrame); bool found = findProcessingAreas(myFrame);
// Reset frame position // Reset frame position
videoCapture.set(CAP_PROP_POS_FRAMES, 0); videoCapture.set(CAP_PROP_POS_FRAMES, 0);
// Write useful information to log file if (!found) {
string message; cout << RED << "Processing area not found. Try changing JSON parameters." << END << endl;
if (found) {
message = "Processing areas found!\n";
cout << message;
files::saveFile(logPath, message, true);
} else {
message = "Processing area not found. Try changing JSON parameters.\n";
cout << message;
files::saveFile(logPath, message, true);
return -1; // Program terminated early return -1; // Program terminated early
} }
/*********************************************************************************************/
/***************************** MAKE ADDITIONAL OUTPUT DIRECTORIES ****************************/
/*********************************************************************************************/
irregularityImagesPath = outputPath / "IrregularityImages";
int fullFrameDirectory = fs::create_directory(irregularityImagesPath);
/*********************************************************************************************/ /*********************************************************************************************/
/**************************************** PROCESSING *****************************************/ /**************************************** PROCESSING *****************************************/
/*********************************************************************************************/ /*********************************************************************************************/
...@@ -936,8 +882,6 @@ int main(int argc, char** argv) { ...@@ -936,8 +882,6 @@ int main(int argc, char** argv) {
string result("Processing elapsed time: " + to_string((int)min) + ":" + to_string((int)sec)); string result("Processing elapsed time: " + to_string((int)min) + ":" + to_string((int)sec));
cout << endl << result << endl; cout << endl << result << endl;
files::saveFile("log.txt", result + '\n', true);
/*********************************************************************************************/ /*********************************************************************************************/
/************************************* IRREGULARITY FILES ************************************/ /************************************* IRREGULARITY FILES ************************************/
/*********************************************************************************************/ /*********************************************************************************************/
......
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