/* Questo script esegue l'analisi di un video fornito per rilevare le discontinuità che vengono trovate. Tutte le informazioni necessarie all'agoritmo si possono individuare nei file XML all'interno della cartella config. @author Nadir Dalla Pozza @version 3.0 @date 29-06-2022 */ #include #include #include #include // uuid class #include // generators #include // streaming operators etc. #include #include #include #include #include #include #include "utility.h" #include "forAudioAnalyser.h" namespace fs = std::__fs::filesystem; using namespace cv; using namespace std; using json = nlohmann::json; /* ------------------------------------------------------------------------------ VARIABLES ------------------------------------------------------------------------------ */ bool savingPinchRoller = false, pinchRollerRect = false; bool savingBrand = false; cv::Mat myFrame; float mediaPrevFrame = 0; bool firstBrand = true; // The first frame containing brands on tape must be saved float firstBrandInstant = 0; // config.json parameters bool brands; std::string irregularityFileInputPath; std::string outputPath; std::string videoPath; float speed; float tapeThresholdPercentual; float capstanThresholdPercentual; // JSON files json configurationFile; json irregularityFileInput; json irregularityFileOutput1; json irregularityFileOutput2; // RotatedRect identifying the processing area RotatedRect rect, rectTape, rectCapstan; bool frameDifference(cv::Mat prevFrame, cv::Mat currentFrame, int msToEnd) { /********************************** Capstan analysis *****************************************/ // In the last minute of the video, check for pinchRoller position for endTape event if (msToEnd < 60000) { // Capstan area int capstanAreaPixels = rectCapstan.size.width * rectCapstan.size.height; float capstanDifferentPixelsThreshold = capstanAreaPixels * capstanThresholdPercentual / 100; // CODE FROM https://answers.opencv.org/question/497/extract-a-rotatedrect-area/ // matrices we'll use Mat M, rotatedPrevFrame, croppedPrevFrame, rotatedCurrentFrame, croppedCurrentFrame; // get angle and size from the bounding box float angle = rectCapstan.angle; Size rect_size = rectCapstan.size; // thanks to http://felix.abecassis.me/2011/10/opencv-rotation-deskewing/ if (rectCapstan.angle < -45.) { angle += 90.0; swap(rect_size.width, rect_size.height); } // get the rotation matrix M = getRotationMatrix2D(rectCapstan.center, angle, 1.0); // perform the affine transformation warpAffine(prevFrame, rotatedPrevFrame, M, prevFrame.size(), INTER_CUBIC); warpAffine(currentFrame, rotatedCurrentFrame, M, currentFrame.size(), INTER_CUBIC); // crop the resulting image getRectSubPix(rotatedPrevFrame, rect_size, rectCapstan.center, croppedPrevFrame); getRectSubPix(rotatedCurrentFrame, rect_size, rectCapstan.center, croppedCurrentFrame); // imshow("Current frame", currentFrame); // imshow("Cropped Current Frame", croppedCurrentFrame); // waitKey(); // END CODE FROM https://answers.opencv.org/question/497/extract-a-rotatedrect-area/ cv::Mat differenceFrame = difference(croppedPrevFrame, croppedCurrentFrame); int blackPixelsCapstan = 0; for (int i = 0; i < croppedCurrentFrame.rows; i++) { for (int j = 0; j < croppedCurrentFrame.cols; j++) { if (differenceFrame.at(i, j)[0] == 0) { // There is a black pixel, then there is a difference between previous and current frames blackPixelsCapstan++; } } } if (blackPixelsCapstan > capstanDifferentPixelsThreshold) { savingPinchRoller = true; return true; } else { savingPinchRoller = false; } } /************************************ Tape analysis ******************************************/ // Tape area int tapeAreaPixels = rectTape.size.width * rectTape.size.height; float tapeDifferentPixelsThreshold = tapeAreaPixels * tapeThresholdPercentual / 100; /***************** Extract matrices corresponding to the processing area *********************/ // Tape area // CODE FROM https://answers.opencv.org/question/497/extract-a-rotatedrect-area/ // matrices we'll use Mat M, rotatedPrevFrame, croppedPrevFrame, rotatedCurrentFrame, croppedCurrentFrame; // get angle and size from the bounding box float angle = rectTape.angle; Size rect_size = rectTape.size; // thanks to http://felix.abecassis.me/2011/10/opencv-rotation-deskewing/ if (rectTape.angle < -45.) { angle += 90.0; swap(rect_size.width, rect_size.height); } // get the rotation matrix M = getRotationMatrix2D(rectTape.center, angle, 1.0); // perform the affine transformation warpAffine(prevFrame, rotatedPrevFrame, M, prevFrame.size(), INTER_CUBIC); warpAffine(currentFrame, rotatedCurrentFrame, M, currentFrame.size(), INTER_CUBIC); // crop the resulting image getRectSubPix(rotatedPrevFrame, rect_size, rectTape.center, croppedPrevFrame); getRectSubPix(rotatedCurrentFrame, rect_size, rectTape.center, croppedCurrentFrame); // imshow("Current frame", currentFrame); // imshow("Cropped Current Frame", croppedCurrentFrame); // waitKey(); // END CODE FROM https://answers.opencv.org/question/497/extract-a-rotatedrect-area/ cv::Mat differenceFrame = difference(croppedPrevFrame, croppedCurrentFrame); int decEnd = (msToEnd % 1000) / 100; int secEnd = (msToEnd - (msToEnd % 1000)) / 1000; int minEnd = secEnd / 60; secEnd = secEnd % 60; /****************************** Segment analysis ****************************************/ int blackPixels = 0; float mediaCurrFrame; int totColoreCF = 0; for (int i = 0; i < croppedCurrentFrame.rows; i++) { for (int j = 0; j < croppedCurrentFrame.cols; j++) { totColoreCF += croppedCurrentFrame.at(i, j)[0] + croppedCurrentFrame.at(i, j)[1] + croppedCurrentFrame.at(i, j)[2]; if (differenceFrame.at(i, j)[0] == 0) { blackPixels++; } } } mediaCurrFrame = totColoreCF/tapeAreaPixels; if (blackPixels > tapeDifferentPixelsThreshold) { if (brands) { if (mediaPrevFrame > (mediaCurrFrame + 10) || mediaPrevFrame < (mediaCurrFrame - 10)) { // They are not similar for color average // Update mediaPrevFrame mediaPrevFrame = mediaCurrFrame; firstBrandInstant = msToEnd; return true; } // If the above condition is not verified, update anyway mediaPrevFrame mediaPrevFrame = mediaCurrFrame; // At the beginning of the video, wait at least 1 second before the next Irregularity to consider it as a brand. // It is not guaranteed that it will be the first brand, but it is generally a safe approach to have a correct image if (firstBrand && (firstBrandInstant - msToEnd > 1000)) { firstBrand = false; savingBrand = true; return true; } } else { return true; } } return false; } int processing(cv::VideoCapture videoCapture, std::string fileName) { // Video duration int frameNumbers_v = videoCapture.get(CAP_PROP_FRAME_COUNT); float fps_v = videoCapture.get(CAP_PROP_FPS); // FPS can be non-integers!!! float videoLength = (float) frameNumbers_v / fps_v; // [s] int videoLength_ms = videoLength * 1000; int savedFrames = 0, unsavedFrames = 0; float lastSaved = -160; int savingRate = 0; // [ms] // Whenever we find an Irregularity, we want to skip a lenght equal to the reading head (3 cm = 1.18 inches) if (speed == 7.5) savingRate = 157; // Time taken to cross 3 cm at 7.5 ips else if (speed == 15) savingRate = 79; // Time taken to cross 3 cm at 15 ips // The first frame of the video won't be processed cv::Mat prevFrame; videoCapture >> prevFrame; firstBrandInstant = videoLength_ms - videoCapture.get(CAP_PROP_POS_MSEC); while (videoCapture.isOpened()) { cv::Mat frame; videoCapture >> frame; 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; int secToEnd = msToEnd / 1000; int minToEnd = (secToEnd / 60) % 60; secToEnd = secToEnd % 60; std::string secStrToEnd = std::to_string(secToEnd), minStrToEnd = std::to_string(minToEnd); if (minToEnd < 10) minStrToEnd = "0" + minStrToEnd; if (secToEnd < 10) secStrToEnd = "0" + secStrToEnd; std::cout << "\rIrregularities: " << savedFrames << ". "; std::cout << "Remaining video time [mm:ss]: " << minStrToEnd << ":" << secStrToEnd << std::flush; if ((ms - lastSaved > savingRate) && frameDifference(prevFrame, frame, msToEnd)) { // An Irregularity is found! // 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); // Finding an image containing the whole tape 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(rectTape.size.height))); // De-interlacing the image with the whole tape 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); separateFrame(subImage, oddSubImage, evenSubImage); std::string timeLabel = getTimeLabel(ms); std::string safeTimeLabel = getSafeTimeLabel(ms); saveIrregularityImage(safeTimeLabel, fileName, oddFrame, oddSubImage, savingPinchRoller); // Append Irregularity information to JSON boost::uuids::uuid uuid = boost::uuids::random_generator()(); irregularityFileOutput1["Irregularities"] += {{ "IrregularityID", boost::lexical_cast(uuid) }, { "Source", "v" }, { "TimeLabel", timeLabel } }; irregularityFileOutput2["Irregularities"] += {{ "IrregularityID", boost::lexical_cast(uuid) }, { "Source", "v" }, { "TimeLabel", timeLabel }, { "ImageURI", pathTape224 + "/"+ fileName + "_" + safeTimeLabel + ".jpg" } }; lastSaved = ms; savedFrames++; } else { unsavedFrames++; } prevFrame = frame; } else { std::cout << "\nEmpty frame!" << std::endl; videoCapture.release(); break; } } ofstream myFile; myFile.open("log.txt", ios::app); myFile << "Saved frames are: " << savedFrames << std::endl; myFile.close(); return 0; } bool findProcessingAreas(json configurationFile) { /******************************************* JSON PARAMETERS *******************************************/ // Read parameters from JSON int minDist, angleThresh, scaleThresh, posThresh, minDistTape, angleThreshTape, scaleThreshTape, posThreshTape, minDistCapstan, angleThreshCapstan, scaleThreshCapstan, posThreshCapstan; try { minDist = configurationFile["MinDist"]; angleThresh = configurationFile["AngleThresh"]; scaleThresh = configurationFile["ScaleThresh"]; posThresh = configurationFile["PosThresh"]; minDistTape = configurationFile["MinDistTape"]; angleThreshTape = configurationFile["AngleThreshTape"]; scaleThreshTape = configurationFile["ScaleThreshTape"]; posThreshTape = configurationFile["PosThreshTape"]; minDistCapstan = configurationFile["MinDistCapstan"]; angleThreshCapstan = configurationFile["AngleThreshCapstan"]; scaleThreshCapstan = configurationFile["ScaleThreshCapstan"]; posThreshCapstan = configurationFile["PosThreshCapstan"]; } catch (nlohmann::detail::type_error e) { std::cerr << "\033[1;31mconfig.json error!\033[0;31m\n" << e.what() << std::endl; return -1; } /******************************************* READING HEAD DETECTION *******************************************/ // Obtain grayscale version of myFrame Mat myFrameGrayscale; cvtColor(myFrame, myFrameGrayscale, COLOR_BGR2GRAY); // Downsample myFrameGrayscale in half pixels for performance reasons Mat myFrameGrayscaleHalf; pyrDown(myFrameGrayscale, myFrameGrayscaleHalf, Size(myFrame.cols/2, myFrame.rows/2)); // Get input shape in grayscale Mat templateImage = imread("../input/readingHead.png", IMREAD_GRAYSCALE); // Downsample tapeShape in half pixels Mat templateImageHalf; pyrDown(templateImage, templateImageHalf, Size(templateImage.cols/2, templateImage.rows/2)); // Select the image to process Mat processingImage = myFrameGrayscaleHalf; // Select the template to be detected Mat templateShape = templateImageHalf; // Algorithm and parameters Ptr alg = createGeneralizedHoughGuil(); alg -> setMinDist(minDist); alg -> setLevels(360); alg -> setDp(2); alg -> setMaxBufferSize(1000); alg -> setAngleStep(1); alg -> setAngleThresh(angleThresh); alg -> setMinScale(0.9); alg -> setMaxScale(1.1); alg -> setScaleStep(0.1); alg -> setScaleThresh(scaleThresh); alg -> setPosThresh(posThresh); alg -> setCannyLowThresh(100); alg -> setCannyHighThresh(300); alg -> setTemplate(templateShape); vector positionsPos, positionsNeg; Mat votesPos, votesNeg; TickMeter tm; int oldPosThresh = posThresh; std::cout << "\033[1;36mReading head\033[0m" << endl; tm.start(); detectShape(alg, templateShape, posThresh, positionsPos, votesPos, positionsNeg, votesNeg, myFrameGrayscaleHalf); tm.stop(); std::cout << "Reading head detection time : " << tm.getTimeMilli() << " ms" << endl; RotatedRect rectPos = drawShapes(myFrame, positionsPos, Scalar(0, 0, 255), templateImageHalf.cols, templateImageHalf.rows, 0, 0, 2); RotatedRect rectNeg = drawShapes(myFrame, positionsNeg, Scalar(128, 128, 255), templateImageHalf.cols, templateImageHalf.rows, 0, 0, 2); Point2f pts[4]; if (rectPos.size.width > 0) if (rectNeg.size.width > 0) if (votesPos.at(0) > votesNeg.at(0)) { cout << "Positive is best" << endl; rect = rectPos; } else { cout << "Negative is best" << endl; rect = rectNeg; } else { cout << "Positive is the only choice." << endl; rect = rectPos; } else if (rectNeg.size.width > 0) { cout << "Negative is the only choice." << endl; rect = rectNeg; } else { return false; } cout << endl; rect.points(pts); /******************************************* TAPE AREA DETECTION *******************************************/ // Defining the processing area for identifying the tape under the reading head. // // Parameters for extracting a rectangle containing the found rectangle completely (also if it is slightly rotated) // and with twice its height (since the tape is immediatley below the found rectangle). int tapeProcessingAreaX = min(pts[0].x, pts[1].x); int tapeProcessingAreaY = min(pts[1].y, pts[2].y) + (max(pts[0].y, pts[3].y) - min(pts[1].y, pts[2].y)) * 2/3; // Shift down the area int tapeProcessingAreaWidth = max(pts[3].x-pts[1].x, pts[2].x-pts[0].x); int tapeProcessingAreaHeight = max(pts[3].y-pts[1].y, pts[0].y-pts[2].y); Rect tapeProcessingAreaRect(tapeProcessingAreaX, tapeProcessingAreaY, tapeProcessingAreaWidth, tapeProcessingAreaHeight); // Obtain grayscale version of tapeProcessingArea Mat tapeProcessingAreaGrayscale = myFrameGrayscale(tapeProcessingAreaRect); // Read template image - it is smaller than before, therefore there is no need to downsample templateShape = imread("../input/tapeArea.png", IMREAD_GRAYSCALE); // Reset algorithm and set parameters alg = createGeneralizedHoughGuil(); alg -> setMinDist(minDistTape); alg -> setLevels(360); alg -> setDp(2); alg -> setMaxBufferSize(1000); alg -> setAngleStep(1); alg -> setAngleThresh(angleThreshTape); alg -> setMinScale(0.9); alg -> setMaxScale(1.1); alg -> setScaleStep(0.05); alg -> setScaleThresh(scaleThreshTape); alg -> setPosThresh(posThreshTape); alg -> setCannyLowThresh(100); alg -> setCannyHighThresh(300); alg -> setTemplate(templateShape); oldPosThresh = posThreshTape; vector positionsTapePos, positionsTapeNeg; Mat votesTapePos, votesTapeNeg; std::cout << "\033[1;36mTape\033[0m" << endl; tm.reset(); tm.start(); detectShape(alg, templateShape, posThreshTape, positionsTapePos, votesTapePos, positionsTapeNeg, votesTapeNeg, tapeProcessingAreaGrayscale); tm.stop(); std::cout << "Tape detection time : " << tm.getTimeMilli() << " ms" << endl; RotatedRect rectTapePos = drawShapes(myFrame, positionsTapePos, Scalar(0, 255, 0), templateShape.cols, templateShape.rows, tapeProcessingAreaX, tapeProcessingAreaY, 1); RotatedRect rectTapeNeg = drawShapes(myFrame, positionsTapeNeg, Scalar(128, 255, 128), templateShape.cols, templateShape.rows, tapeProcessingAreaX, tapeProcessingAreaY, 1); if (rectTapePos.size.width > 0) if (rectTapeNeg.size.width > 0) if (votesTapePos.at(0) > votesTapeNeg.at(0)) { cout << "Positive is best" << endl; rectTape = rectTapePos; } else { cout << "Negative is best" << endl; rectTape = rectTapeNeg; } else { cout << "Positive is the only choice." << endl; rectTape = rectTapePos; } else if (rectTapeNeg.size.width > 0) { cout << "Negative is the only choice." << endl; rectTape = rectTapeNeg; } else { return false; } cout << endl; /******************************************* CAPSTAN DETECTION *******************************************/ // Process only right portion of the image, wherw the capstain always appears int capstanProcessingAreaRectX = myFrame.cols*3/4; int capstanProcessingAreaRectY = myFrame.rows/2; int capstanProcessingAreaRectWidth = myFrame.cols/4; int capstanProcessingAreaRectHeight = myFrame.rows/2; Rect capstanProcessingAreaRect(capstanProcessingAreaRectX, capstanProcessingAreaRectY, capstanProcessingAreaRectWidth, capstanProcessingAreaRectHeight); Mat capstanProcessingAreaGrayscale = myFrameGrayscale(capstanProcessingAreaRect); // Read template image - it is smaller than before, therefore there is no need to downsample templateShape = imread("../input/capstanBERIO058prova.png", IMREAD_GRAYSCALE); // Reset algorithm and set parameters alg = createGeneralizedHoughGuil(); alg -> setMinDist(minDistCapstan); alg -> setLevels(360); alg -> setDp(2); alg -> setMaxBufferSize(1000); alg -> setAngleStep(1); alg -> setAngleThresh(angleThreshCapstan); alg -> setMinScale(0.9); alg -> setMaxScale(1.1); alg -> setScaleStep(0.05); alg -> setScaleThresh(scaleThreshCapstan); alg -> setPosThresh(posThreshCapstan); alg -> setCannyLowThresh(100); alg -> setCannyHighThresh(250); alg -> setTemplate(templateShape); oldPosThresh = posThreshCapstan; vector positionsC1Pos, positionsC1Neg; Mat votesC1Pos, votesC1Neg; std::cout << "\033[1;36mCapstan\033[0m" << endl; tm.reset(); tm.start(); detectShape(alg, templateShape, posThreshCapstan, positionsC1Pos, votesC1Pos, positionsC1Neg, votesC1Neg, capstanProcessingAreaGrayscale); tm.stop(); std::cout << "Capstan detection time : " << tm.getTimeMilli() << " ms" << endl; RotatedRect rectCapstanPos = drawShapes(myFrame, positionsC1Pos, Scalar(255, 0, 0), templateShape.cols-22, templateShape.rows-92, capstanProcessingAreaRectX+11, capstanProcessingAreaRectY+46, 1); RotatedRect rectCapstanNeg = drawShapes(myFrame, positionsC1Neg, Scalar(255, 128, 0), templateShape.cols-22, templateShape.rows-92, capstanProcessingAreaRectX+11, capstanProcessingAreaRectY+46, 1); if (rectCapstanPos.size.width > 0) if (rectCapstanNeg.size.width > 0) if (votesC1Pos.at(0) > votesC1Neg.at(0)) { cout << "Positive is best" << endl; rectCapstan = rectCapstanPos; } else { cout << "Negative is best" << endl; rectCapstan = rectCapstanNeg; } else { cout << "Positive is the only choice." << endl; rectCapstan = rectCapstanPos; } else if (rectTapeNeg.size.width > 0) { cout << "Negative is the only choice." << endl; rectCapstan = rectCapstanNeg; } else { return false; } cout << endl; // Shows the detected areas // imshow("Tape area(s)", myFrame); // waitKey(); return true; } int main(int argc, char** argv) { /**************************************** CONFIGURATION FILE ****************************************/ // Read configuration file std::ifstream iConfig("../config/config.json"); iConfig >> configurationFile; // Initialise parameters try { brands = configurationFile["Brands"]; irregularityFileInputPath = configurationFile["IrregularityFileInput"]; outputPath = configurationFile["OutputPath"]; videoPath = configurationFile["PreservationAudioVisualFile"]; speed = configurationFile["Speed"]; tapeThresholdPercentual = configurationFile["ThresholdPercentualTape"]; capstanThresholdPercentual = configurationFile["ThresholdPercentualCapstan"]; } catch (nlohmann::detail::type_error e) { std::cerr << "\033[1;31mconfig.json error!\033[0;31m\n" << e.what() << std::endl; return -1; } // Input JSON check std::ifstream iJSON(irregularityFileInputPath); if (iJSON.fail()) { std::cerr << "\033[1;31mconfig.json error!\033[0;31m\nIrregularityFileInput.json cannot be found or opened." << std::endl; return -1; } std::string fileName, extension; if (findFileName(videoPath, fileName, extension) == -1) { std::cerr << "\033[1;31mconfig.json error!\033[0;31m\nThe PreservationAudioVisualFile cannot be found or opened." << std::endl; return -1; } if (speed != 7.5 && speed != 15) { std::cerr << "\033[1;31mconfig.json error!\033[0;31m\nSpeed parameter must be 7.5 or 15 ips." << std::endl; return -1; } if (tapeThresholdPercentual < 0 || tapeThresholdPercentual > 100) { std::cerr << "\033[1;31mconfig.json error!\033[0;31m\nThresholdPercentual parameter must be a percentage value." << std::endl; return -1; } if (capstanThresholdPercentual < 0 || capstanThresholdPercentual > 100) { std::cerr << "\033[1;31mconfig.json error!\033[0;31m\nThresholdPercentual parameter must be a percentage value." << std::endl; return -1; } // Speed reference = 7.5 // If the speed is 15, then increase threshold percentual by 4 to have similar detection performance if (speed == 15) tapeThresholdPercentual += 4; std::cout << "\nParameters from config.json file:" << std::endl; std::cout << " Brands: " << brands << std::endl; std::cout << " Speed: " << speed << std::endl; std::cout << " ThresholdPercentual: " << tapeThresholdPercentual << std::endl; std::cout << " ThresholdPercentualCapstan: " << capstanThresholdPercentual << std::endl; std::cout << std::endl; // Read input JSON iJSON >> irregularityFileInput; /******************************************* TAPE AREA DETECTION *******************************************/ cv::VideoCapture videoCapture(videoPath); if (!videoCapture.isOpened()) { std::cerr << "\033[31m" << "Video unreadable." << std::endl; return -1; } // Get total number of frames int totalFrames = videoCapture.get(CAP_PROP_FRAME_COUNT); // Set frame position to half video length videoCapture.set(CAP_PROP_POS_FRAMES, totalFrames/2); // Get frame and show it videoCapture >> myFrame; // Find the processing area corresponding to the tape area over the reading head bool found = findProcessingAreas(configurationFile); // Reset frame position videoCapture.set(CAP_PROP_POS_FRAMES, 0); /**************************** WRITE USEFUL INFORMATION TO LOG FILE ***************************/ // Get now time std::time_t t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); std::string ts = std::ctime(&t); ofstream myFile; myFile.open("log.txt", ios::app); myFile << endl << fileName << endl; myFile << "tsh: " << tapeThresholdPercentual << " tshp: " << capstanThresholdPercentual << std::endl; myFile << ts; // No endline character for avoiding middle blank line. if (found) { std::cout << "Processing areas found!" << endl; myFile << "Processing areas found!" << endl; myFile.close(); } else { std::cout << "Processing area not found. Try changing JSON parameters." << endl; myFile << "Processing area not found." << endl; myFile.close(); return -1; // Program terminated early } /********************************* MAKE REQUIRED DIRECTORIES *********************************/ makeDirectories(fileName, outputPath, brands); /**************************************** PROCESSING *****************************************/ std::cout << "\n\033[32mStarting processing...\033[0m\n" << std::endl; // Processing timer time_t startTimer, endTimer; startTimer = time(NULL); processing(videoCapture, fileName); endTimer = time(NULL); float min = (endTimer - startTimer) / 60; float sec = (endTimer - startTimer) % 60; std::string result("Processing elapsed time: " + std::to_string((int)min) + ":" + std::to_string((int)sec)); std::cout << endl << result << endl; myFile.open("log.txt", ios::app); myFile << result << std::endl << std::endl; myFile.close(); /**************************************** IRREGULARITY FILES ****************************************/ std::ofstream outputFile1; std::string outputFile1Name = outputPath + "IrregularityFileOutput1.json"; outputFile1.open(outputFile1Name); outputFile1 << irregularityFileOutput1 << std::endl; // Irregularities to extract for the AudioAnalyser and to the TapeIrregularityClassifier extractIrregularityImagesForAudio(outputPath, videoPath, irregularityFileInput, irregularityFileOutput2); std::ofstream outputFile2; std::string outputFile2Name = outputPath + "IrregularityFileOutput2.json"; outputFile2.open(outputFile2Name); outputFile2 << irregularityFileOutput2 << std::endl; return 0; }