You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
219 lines
8.1 KiB
219 lines
8.1 KiB
#define NOMINMAX |
|
#include "hailo/hailort.hpp" |
|
#include "hailo/vdevice.hpp" |
|
#include "hailo/expected.hpp" |
|
#include "hailo/hailort_common.hpp" |
|
#include "hailo/runtime_statistics.hpp" |
|
#include "VideoToAI.hpp" |
|
|
|
#include <iostream> |
|
#include <chrono> |
|
#include <thread> |
|
#include <fstream> |
|
|
|
#include <cstring> |
|
#if defined(__unix__) |
|
#include <sys/mman.h> |
|
#endif |
|
//#include <include/common.h> |
|
#include <opencv2/opencv.hpp> |
|
#include <vector> |
|
#include <mutex> |
|
#include <condition_variable> |
|
#include <queue> |
|
#define BATCH_COUNT (1000) |
|
#define BATCH_SIZE (4) |
|
#define FPS (30) // Set target FPS to 30 |
|
|
|
using namespace hailort; |
|
using namespace VideoToAI; |
|
|
|
std::mutex mtx; |
|
std::condition_variable cv_var; |
|
std::queue<cv::Mat> frameQueue; |
|
bool doneReading = false; |
|
|
|
cv::Mat readYUV420(const std::string& yuvFile, int width, int height, int frameIndex) { |
|
int frameSize = width * height + (width * height / 2); |
|
std::ifstream yuvStream(yuvFile, std::ios::binary); |
|
if (!yuvStream.is_open()) { |
|
throw std::runtime_error("Cannot open YUV file"); |
|
} |
|
|
|
// Calculate file size for bounds checking |
|
yuvStream.seekg(0, std::ios::end); |
|
std::streamsize fileSize = yuvStream.tellg(); |
|
if (frameIndex * frameSize >= fileSize) { |
|
throw std::runtime_error("Frame index out of range. Check the input file size."); |
|
} |
|
|
|
yuvStream.seekg(frameSize * frameIndex, std::ios::beg); |
|
std::vector<uint8_t> buffer(frameSize); |
|
yuvStream.read(reinterpret_cast<char*>(buffer.data()), frameSize); |
|
if (yuvStream.gcount() != frameSize) { |
|
throw std::runtime_error("Failed to read full YUV frame. Possibly corrupted file."); |
|
} |
|
|
|
return cv::Mat(height + height / 2, width, CV_8UC1, buffer.data()).clone(); |
|
} |
|
|
|
|
|
void readYUVThread(const std::string& yuvFile, int width, int height, int numFrames, |
|
ConfiguredInferModel& configured_infer_model, |
|
std::shared_ptr<InferModel>& infer_model, VDevice* vdevice) { |
|
std::ifstream yuvStream(yuvFile, std::ios::binary); |
|
if (!yuvStream.is_open()) { |
|
throw std::runtime_error("Cannot open YUV file"); |
|
} |
|
std::cout << "[INFO] Opened YUV file: " << yuvFile << std::endl; |
|
std::vector<VideoToAI::DetectionResult> detection_results; |
|
cv::Ptr<cv::BackgroundSubtractorMOG2> fgbg_mog2 = cv::createBackgroundSubtractorMOG2(); |
|
|
|
for (int i = 0; i < numFrames; ++i) { |
|
try { |
|
std::cout << "[INFO] Reading frame " << i + 1 << " of " << numFrames << std::endl; |
|
|
|
// Read the YUV frame |
|
cv::Mat yuvFrame = readYUV420(yuvFile, width, height, i); |
|
if (yuvFrame.empty()) { |
|
throw std::runtime_error("YUV frame is empty."); |
|
} |
|
|
|
// Split into Y and UV planes |
|
cv::Mat yPlane = yuvFrame(cv::Rect(0, 0, width, height)); |
|
cv::Mat uvPlane = yuvFrame(cv::Rect(0, height, width, height / 2)); |
|
|
|
// Apply letterbox on Y and UV planes |
|
cv::Mat resizedYPlane, resizedUVPlane; |
|
letterbox(yPlane, resizedYPlane, cv::Size(640, 640)); |
|
letterbox(uvPlane, resizedUVPlane, cv::Size(640, 640 / 2)); |
|
if (resizedYPlane.cols != 640 || resizedYPlane.rows != 640 || |
|
resizedUVPlane.cols != 640 || resizedUVPlane.rows != 320) { |
|
throw std::runtime_error("Unexpected size after letterbox. Ensure input dimensions and padding are correct."); |
|
} |
|
if (resizedYPlane.empty() || resizedUVPlane.empty()) { |
|
throw std::runtime_error("Letterbox failed to resize planes correctly."); |
|
} |
|
|
|
// Only Hailo infer |
|
hailoInference(resizedYPlane, resizedUVPlane, configured_infer_model, infer_model, vdevice, detection_results); |
|
|
|
// Catch detection results |
|
/*for (const auto& result : detection_results) { |
|
std::cout << "Class ID: " << result.class_id |
|
<< ", Confidence: " << result.confidence |
|
<< ", BBox: [" |
|
<< result.bbox.x_min << ", " |
|
<< result.bbox.y_min << ", " |
|
<< result.bbox.x_max << ", " |
|
<< result.bbox.y_max << "]" << std::endl; |
|
}*/ |
|
|
|
std::cout << "[INFO] Processed YUV frame." << std::endl; |
|
|
|
// Combine Y and UV planes into a single frame |
|
std::vector<cv::Mat> channels = { resizedYPlane, resizedUVPlane }; |
|
cv::Mat combinedFrame; |
|
cv::vconcat(channels, combinedFrame); |
|
|
|
// Push the processed frame to the queue |
|
std::unique_lock<std::mutex> lock(mtx); |
|
frameQueue.push(combinedFrame.clone()); |
|
// lock.unlock(); // aviod didn't auto unlock |
|
cv_var.notify_one(); |
|
// std::cout << "[INFO] Frame pushed to the queue." << std::endl; |
|
|
|
} |
|
catch (const std::exception& e) { |
|
std::cerr << "[ERROR] Read Error: " << e.what() << std::endl; |
|
break; |
|
} |
|
} |
|
|
|
std::unique_lock<std::mutex> lock(mtx); |
|
doneReading = true; |
|
cv_var.notify_one(); |
|
// std::cout << "[INFO] Done reading YUV file." << std::endl; |
|
} |
|
|
|
|
|
void writeYUVThread(const std::string& outputFile) { |
|
std::ofstream outputYuvStream(outputFile, std::ios::binary); |
|
if (!outputYuvStream.is_open()) { |
|
std::cerr << "Cannot open output YUV file" << std::endl; |
|
return; |
|
} |
|
|
|
while (true) { |
|
std::unique_lock<std::mutex> lock(mtx); |
|
cv_var.wait(lock, [] { return !frameQueue.empty() || doneReading; }); |
|
// lock.unlock(); // aviod didn't auto unlock |
|
while (!frameQueue.empty()) { |
|
cv::Mat frame = frameQueue.front(); |
|
frameQueue.pop(); |
|
outputYuvStream.write(reinterpret_cast<const char*>(frame.data), frame.total()); |
|
} |
|
if (doneReading && frameQueue.empty()) { |
|
break; |
|
} |
|
} |
|
outputYuvStream.close(); |
|
std::cout << "YUV video saved to " << outputFile << std::endl; |
|
} |
|
|
|
int main(int argc, char** argv) { |
|
if (argc != 3) { |
|
std::cerr << "Usage: " << argv[0] << " <input_file> <output_file>" << std::endl; |
|
return -1; |
|
} |
|
// |
|
std::string inputFilePath = argv[1]; |
|
std::string outputFilePath = argv[2]; |
|
|
|
try { |
|
auto vdevice = VDevice::create().expect("Failed to create vdevice"); |
|
auto infer_model = vdevice->create_infer_model("yolov7_yuv21.hef").expect("Failed to create infer model"); |
|
infer_model->set_batch_size(BATCH_SIZE); |
|
std::cout << "Set batch_size to " << BATCH_SIZE << std::endl; |
|
auto configured_infer_model = infer_model->configure().expect("Failed to configure infer model"); |
|
//infer_model->set_batch_size(BATCH_SIZE); |
|
if (inputFilePath.size() >= 4 && inputFilePath.substr(inputFilePath.size() - 4) == ".yuv") { |
|
int width = 640; |
|
int height = 360; |
|
|
|
std::ifstream yuvStream(inputFilePath, std::ios::binary | std::ios::ate); |
|
if (!yuvStream.is_open()) { |
|
std::cerr << "Cannot open input YUV file" << std::endl; |
|
return -1; |
|
} |
|
|
|
std::streamsize fileSize = yuvStream.tellg(); |
|
int frameSize = width * height + (width * height / 2); |
|
if (fileSize % frameSize != 0) { |
|
std::cerr << "Warning: File size is not an exact multiple of frame size. There might be incomplete frames." << std::endl; |
|
} |
|
int numFrames = static_cast<int>(fileSize / frameSize); |
|
std::thread reader( |
|
[&]() { |
|
readYUVThread(inputFilePath, width, height, numFrames, configured_infer_model, infer_model, vdevice.get()); |
|
}); |
|
|
|
std::thread writer(writeYUVThread, outputFilePath); |
|
|
|
reader.join(); |
|
writer.join(); |
|
} |
|
else { |
|
std::cerr << "Unsupported file format. Please provide a .yuv file." << std::endl; |
|
return -1; |
|
} |
|
|
|
std::cout << "Video processing completed and saved to " << outputFilePath << std::endl; |
|
} |
|
catch (const hailort_error& e) { |
|
std::cerr << "Hailo initialization or inference error: " << e.what() << std::endl; |
|
return -1; |
|
} |
|
|
|
return 0; |
|
} |