|
#include <iostream> |
|
#include <algorithm> |
|
#include <opencv2/opencv.hpp> |
|
|
|
void printImageInfo(const std::string &name, const cv::Mat &img) |
|
{ |
|
std::cout << name << " dimensions: " << img.cols << "x" << img.rows |
|
<< ", channels: " << img.channels() |
|
<< ", type: " << img.type() << std::endl; |
|
} |
|
|
|
void printRoiInfo(const std::string &name, const cv::Rect &roi) |
|
{ |
|
std::cout << name << " ROI: x=" << roi.x << ", y=" << roi.y |
|
<< ", width=" << roi.width << ", height=" << roi.height << std::endl; |
|
} |
|
|
|
cv::Mat BGRToI420(const cv::Mat &bgr) |
|
{ |
|
cv::Mat yuv; |
|
cv::cvtColor(bgr, yuv, cv::COLOR_BGR2YUV_I420); |
|
return yuv; |
|
} |
|
|
|
cv::Mat I420ToBGR(const cv::Mat &yuv) |
|
{ |
|
cv::Mat bgr; |
|
cv::cvtColor(yuv, bgr, cv::COLOR_YUV2BGR_I420); |
|
return bgr; |
|
} |
|
|
|
cv::Mat extractRoIFromI420(const cv::Mat &yuv, const cv::Rect &t_roi) |
|
{ |
|
int original_height = yuv.rows * 2 / 3; |
|
printImageInfo("YUV", yuv); |
|
std::cout << "Original dimensions: " << yuv.cols << "x" << original_height << std::endl; |
|
|
|
// Ensure ROI is within bounds of the Y plane |
|
cv::Rect roi = t_roi & cv::Rect(0, 0, yuv.cols, original_height); |
|
printRoiInfo("Adjusted Y", roi); |
|
|
|
// Ensure proper subsampling |
|
assert(roi.x % 2 == 0 && roi.y % 2 == 0 && roi.width % 2 == 0 && roi.height % 4 == 0); |
|
|
|
// Extract Y plane ROI |
|
cv::Mat y_plane(yuv, cv::Rect(0, 0, yuv.cols, original_height)); |
|
printImageInfo("Y plane", y_plane); |
|
cv::Mat roi_y = y_plane(roi).clone(); |
|
printImageInfo("ROI Y", roi_y); |
|
|
|
// Extract U/V plane ROI |
|
cv::Mat u_plane(yuv, cv::Rect(0, original_height, yuv.cols, original_height / 4)); |
|
cv::Mat v_plane(yuv, cv::Rect(0, original_height + original_height / 4, yuv.cols, original_height / 4)); |
|
|
|
printImageInfo("U plane", u_plane); |
|
printImageInfo("V plane", v_plane); |
|
|
|
cv::Mat u_plane_block = u_plane.reshape(0, original_height / 2); |
|
cv::Mat v_plane_block = v_plane.reshape(0, original_height / 2); |
|
|
|
printImageInfo("U block plane", u_plane_block); |
|
printImageInfo("V block plane", v_plane_block); |
|
|
|
cv::Mat roi_u_block = u_plane_block(cv::Rect(roi.x / 2, roi.y / 2, roi.width / 2, roi.height / 2)).clone(); |
|
cv::Mat roi_v_block = v_plane_block(cv::Rect(roi.x / 2, roi.y / 2, roi.width / 2, roi.height / 2)).clone(); |
|
|
|
cv::Mat roi_u = roi_u_block.reshape(0, roi_y.rows / 4); |
|
cv::Mat roi_v = roi_v_block.reshape(0, roi_y.rows / 4); |
|
|
|
printImageInfo("ROI U", roi_u); |
|
printImageInfo("ROI V", roi_v); |
|
|
|
// Combine Y, U, and V ROIs into a single I420 image |
|
cv::Mat roi_yuv(roi.height * 3 / 2, roi.width, CV_8UC1); |
|
roi_y.copyTo(roi_yuv(cv::Rect(0, 0, roi.width, roi.height))); |
|
|
|
roi_u.copyTo(roi_yuv(cv::Rect(0, roi.height, roi.width, roi.height / 4))); |
|
roi_v.copyTo(roi_yuv(cv::Rect(0, roi.height + roi.height / 4, roi.width, roi.height / 4))); |
|
|
|
printImageInfo("ROI YUV", roi_yuv); |
|
return roi_yuv; |
|
} |
|
|
|
cv::Mat cropCvtColorI420ToBGR(const cv::Mat &yuv, const cv::Rect &t_roi) |
|
{ |
|
// Ensures roi.x % 2 == 0 && roi.y % 2 == 0 && roi.width % 2 == 0 && roi.height % 4 == 0 |
|
cv::Rect roi = cv::Rect(std::clamp(t_roi.x + (t_roi.x % 2), 0, yuv.cols - 1), |
|
std::clamp(t_roi.y + (t_roi.y % 2), 0, yuv.rows - 1), |
|
std::clamp(t_roi.width + (t_roi.width % 2), 0, yuv.cols), |
|
std::clamp(t_roi.height + (4 - t_roi.height % 4) % 4, 0, yuv.rows)); |
|
|
|
printRoiInfo("Adjusted original", roi); |
|
cv::Mat roi_yuv = extractRoIFromI420(yuv, roi); |
|
cv::Mat roi_bgr = I420ToBGR(roi_yuv); |
|
return roi_bgr; |
|
} |
|
|
|
cv::Mat cvtColorCropI420ToBGR(const cv::Mat &yuv, const cv::Rect &t_roi) |
|
{ |
|
cv::Mat bgr_from_yuv = I420ToBGR(yuv); |
|
// Ensure ROI is within bounds of the Y plane |
|
cv::Rect roi = t_roi & cv::Rect(0, 0, bgr_from_yuv.cols, bgr_from_yuv.rows); |
|
printRoiInfo("Adjusted BGR", roi); |
|
return bgr_from_yuv(roi); |
|
} |
|
|
|
int main(int argc, char *argv[]) |
|
{ |
|
try |
|
{ |
|
// Load the image |
|
cv::Mat image = cv::imread(argv[1]); |
|
if (image.empty()) |
|
{ |
|
std::cout << "Error: Could not read the image." << std::endl; |
|
return -1; |
|
} |
|
|
|
printImageInfo("Original image", image); |
|
|
|
// Convert BGR to YUV I420 |
|
cv::Mat yuv = BGRToI420(image); |
|
printImageInfo("YUV I420", yuv); |
|
|
|
// Extract RoI from YUV I420 |
|
int x = std::stoi(argv[2]); |
|
int y = std::stoi(argv[3]); |
|
int width = std::stoi(argv[4]); |
|
int height = std::stoi(argv[5]); |
|
cv::Rect roi{x, y, width, height}; |
|
printRoiInfo("Original", roi); |
|
|
|
// Timing YUV -> RoI -> BGR |
|
cv::TickMeter tm1; |
|
tm1.start(); |
|
cv::Mat roi_bgr = cropCvtColorI420ToBGR(yuv, roi); |
|
tm1.stop(); |
|
printImageInfo("ROI from YUV", roi_bgr); |
|
std::cout << "Time for YUV -> RoI -> BGR: " << tm1.getTimeMilli() << " ms" << std::endl; |
|
|
|
// Timing YUV -> BGR -> RoI |
|
cv::TickMeter tm2; |
|
tm2.start(); |
|
cv::Mat roi_bgr_direct = cvtColorCropI420ToBGR(yuv, roi); |
|
tm2.stop(); |
|
printImageInfo("ROI from BGR", roi_bgr_direct); |
|
std::cout << "Time for YUV -> BGR -> RoI: " << tm2.getTimeMilli() << " ms" << std::endl; |
|
|
|
// Compare results |
|
cv::Mat diff; |
|
cv::absdiff(roi_bgr, roi_bgr_direct, diff); |
|
cv::Scalar mean_diff = cv::mean(diff); |
|
|
|
std::cout << "Mean absolute difference between RoIs: " |
|
<< mean_diff[0] << ", " << mean_diff[1] << ", " << mean_diff[2] << std::endl; |
|
|
|
// Display results |
|
cv::imshow("RoI from YUV", roi_bgr); |
|
cv::imshow("RoI from BGR", roi_bgr_direct); |
|
cv::waitKey(0); |
|
} |
|
catch (const cv::Exception &e) |
|
{ |
|
std::cerr << "OpenCV exception: " << e.what() << std::endl; |
|
} |
|
catch (const std::exception &e) |
|
{ |
|
std::cerr << "Standard exception: " << e.what() << std::endl; |
|
} |
|
catch (...) |
|
{ |
|
std::cerr << "Unknown exception occurred" << std::endl; |
|
} |
|
|
|
return 0; |
|
} |