Skip to content

Instantly share code, notes, and snippets.

@denguir
Last active August 18, 2024 10:50
Show Gist options
  • Save denguir/5821f3f756e56fdcba1d940d0274da44 to your computer and use it in GitHub Desktop.
Save denguir/5821f3f756e56fdcba1d940d0274da44 to your computer and use it in GitHub Desktop.
Roi crop from YUV image

Roi crop from YUV image

About YUV

See: https://gist.github.com/Jim-Bar/3cbba684a71d1a9d468a6711a6eddbeb

Here we focus on implementing YUV cropping for I420 images. In term of speed, it appears that YUV cropping is legit when the image resolution is very high, otherwise just cvtColor then crop.

Compile with opencv

g++ -std=c++17 yuv_crop.cpp -o yuv_crop `pkg-config --cflags --libs opencv4`

Run example

Provide path to image, roi.x, roi.y, roi.width, roi.height

./yuv_crop zidane.jpg 200 200 500 300

#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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment