-
Notifications
You must be signed in to change notification settings - Fork 309
Add QImage and CV::Mat conversion functions into Frame for better handling alpha channel handling
#998
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Add QImage and CV::Mat conversion functions into Frame for better handling alpha channel handling
#998
Changes from all commits
a11c9b2
4551154
b020108
ed33b6d
11f8f78
2520c68
be5e2e9
f2f76e0
b3c171f
b8f6bd4
a7cd61c
985a9df
02637fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||
| * @file | ||||||||||||||||||||||||||||||||||||||||||||||||||
| * @brief Source file for Frame class | ||||||||||||||||||||||||||||||||||||||||||||||||||
| * @author Jonathan Thomas <jonathan@openshot.org> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| * @author HaiVQ <me@haivq.com> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||
| * @ref License | ||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -113,6 +114,7 @@ Frame::~Frame() { | |||||||||||||||||||||||||||||||||||||||||||||||||
| audio.reset(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| #ifdef USE_OPENCV | ||||||||||||||||||||||||||||||||||||||||||||||||||
| imagecv.release(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| brga_image_cv.release(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -893,6 +895,13 @@ cv::Mat Frame::GetImageCV() | |||||||||||||||||||||||||||||||||||||||||||||||||
| return imagecv; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // Set pointer to OpenCV image object | ||||||||||||||||||||||||||||||||||||||||||||||||||
| void Frame::SetImageCV(cv::Mat _image) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| imagecv = _image; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| image = Mat2Qimage(_image); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| std::shared_ptr<QImage> Frame::Mat2Qimage(cv::Mat img){ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cv::cvtColor(img, img, cv::COLOR_BGR2RGB); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| QImage qimg((uchar*) img.data, img.cols, img.rows, img.step, QImage::Format_RGB888); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -906,11 +915,39 @@ std::shared_ptr<QImage> Frame::Mat2Qimage(cv::Mat img){ | |||||||||||||||||||||||||||||||||||||||||||||||||
| return imgIn; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // Set pointer to OpenCV image object | ||||||||||||||||||||||||||||||||||||||||||||||||||
| void Frame::SetImageCV(cv::Mat _image) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| imagecv = _image; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| image = Mat2Qimage(_image); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // Convert QImage to cv::Mat and vice versa | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // Frame class has GetImageCV, but it does not include alpha channel | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // so we need a separate methods which preserve alpha channel | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cv::Mat Frame::QImage2BGRACvMat(std::shared_ptr<QImage>& qimage) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cv::Mat cv_img( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| qimage->height(), qimage->width(), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| CV_8UC4, (uchar*)qimage->constBits(), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| qimage->bytesPerLine() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return cv_img; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+922
to
+927
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| cv::Mat cv_img( | |
| qimage->height(), qimage->width(), | |
| CV_8UC4, (uchar*)qimage->constBits(), | |
| qimage->bytesPerLine() | |
| ); | |
| return cv_img; | |
| // QImage::Format_RGBA8888_Premultiplied stores data in RGBA order. | |
| // First, wrap the existing bits as an RGBA cv::Mat (no copy). | |
| cv::Mat rgba_img( | |
| qimage->height(), qimage->width(), | |
| CV_8UC4, const_cast<uchar*>(qimage->constBits()), | |
| qimage->bytesPerLine() | |
| ); | |
| // Then convert channel order from RGBA to BGRA as expected by OpenCV. | |
| cv::Mat bgra_img; | |
| cv::cvtColor(rgba_img, bgra_img, cv::COLOR_RGBA2BGRA); | |
| return bgra_img; |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The QImage2BGRACvMat function creates a cv::Mat that shares memory with the QImage (shallow copy) without calling .clone(). This is inconsistent with the Qimage2mat function (line 875) which explicitly clones the data. While this works for the current Outline effect usage, it could lead to memory safety issues if the QImage is modified or destroyed while the returned cv::Mat is still in use. Consider adding .clone() for consistency and safety, similar to how Qimage2mat handles it.
| return cv_img; | |
| // Return a deep copy to avoid depending on QImage lifetime / mutability | |
| return cv_img.clone(); |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After converting the cv::Mat to RGBA format using cv::COLOR_BGRA2RGBA, the code creates a QImage with QImage::Format_ARGB32. However, Format_ARGB32 expects ARGB in native endianness (which is BGRA in memory on little-endian systems), but the data is in RGBA format. This creates a format mismatch that requires an additional costly conversion on line 935. Consider creating the QImage directly with QImage::Format_RGBA8888 to match the actual data layout and avoid the unnecessary conversion.
| QImage qimage(final_img.data, final_img.cols, final_img.rows, final_img.step, QImage::Format_ARGB32); | |
| QImage qimage(final_img.data, final_img.cols, final_img.rows, final_img.step, QImage::Format_RGBA8888); |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Storing the result in brga_image_cv member variable is problematic because the cv::Mat shares memory with the QImage (no deep copy). If the QImage changes, brga_image_cv becomes invalid. This is different from imagecv which is a deep copy. Consider either: (1) not storing it as a member variable and returning a new cv::Mat each time, or (2) making it a deep copy with .clone() if caching is desired. The current approach creates a dangling reference risk.
| brga_image_cv = QImage2BGRACvMat(image); | |
| // Create a cv::Mat that references the QImage, then store a deep copy | |
| brga_image_cv = QImage2BGRACvMat(image).clone(); |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name has a typo: brga_image_cv should be bgra_image_cv to correctly represent the BGRA color format.
| brga_image_cv = QImage2BGRACvMat(image); | |
| return brga_image_cv; | |
| } | |
| void Frame::SetBGRACvMat(cv::Mat _image) { | |
| brga_image_cv = _image; | |
| bgra_image_cv = QImage2BGRACvMat(image); | |
| return bgra_image_cv; | |
| } | |
| void Frame::SetBGRACvMat(cv::Mat _image) { | |
| bgra_image_cv = _image; |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name has a typo: brga_image_cv should be bgra_image_cv to correctly represent the BGRA color format.
| brga_image_cv = QImage2BGRACvMat(image); | |
| return brga_image_cv; | |
| } | |
| void Frame::SetBGRACvMat(cv::Mat _image) { | |
| brga_image_cv = _image; | |
| bgra_image_cv = QImage2BGRACvMat(image); | |
| return bgra_image_cv; | |
| } | |
| void Frame::SetBGRACvMat(cv::Mat _image) { | |
| bgra_image_cv = _image; |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |||||||||
| * @file | ||||||||||
| * @brief Header file for Frame class | ||||||||||
| * @author Jonathan Thomas <jonathan@openshot.org> | ||||||||||
| * @author HaiVQ <me@haivq.com> | ||||||||||
| * | ||||||||||
| * @ref License | ||||||||||
| */ | ||||||||||
|
|
@@ -106,6 +107,7 @@ namespace openshot | |||||||||
|
|
||||||||||
| #ifdef USE_OPENCV | ||||||||||
| cv::Mat imagecv; ///< OpenCV image. It will always be in BGR format | ||||||||||
| cv::Mat brga_image_cv; ///< OpenCV image. It will always be in BGR format | ||||||||||
|
||||||||||
| cv::Mat brga_image_cv; ///< OpenCV image. It will always be in BGR format | |
| cv::Mat bgra_image_cv; ///< OpenCV image. It will always be in BGR format |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment incorrectly states "BGR format" but this variable stores images in BGRA format (4 channels including alpha). Update the comment to accurately reflect this.
| cv::Mat brga_image_cv; ///< OpenCV image. It will always be in BGR format | |
| cv::Mat brga_image_cv; ///< OpenCV image. It will always be in BGRA format (with alpha channel) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
| * @brief Unit tests for openshot::Frame | ||
| * @author Jonathan Thomas <jonathan@openshot.org> | ||
| * @author FeRD (Frank Dana) <ferdnyc@gmail.com> | ||
| * @author HaiVQ <me@haivq.com> | ||
| * | ||
| * @ref License | ||
| */ | ||
|
|
@@ -160,4 +161,26 @@ TEST_CASE( "Convert_Image", "[libopenshot][opencv][frame]" ) | |
| CHECK(f1->GetHeight() == cvimage.rows); | ||
| CHECK(cvimage.channels() == 3); | ||
| } | ||
|
|
||
| TEST_CASE( "Convert_Image_Alpha", "[libopenshot][opencv][frame]" ) | ||
| { | ||
| // Create a video clip | ||
| std::stringstream path; | ||
| path << TEST_MEDIA_PATH << "sintel_trailer-720p.mp4"; | ||
| Clip c1(path.str()); | ||
| c1.Open(); | ||
|
|
||
| // Get first frame | ||
| auto f1 = c1.GetFrame(1); | ||
|
|
||
| // Get first Mat image | ||
| cv::Mat cvimage = f1->GetBGRACvMat(); | ||
|
|
||
| CHECK_FALSE(cvimage.empty()); | ||
|
|
||
| CHECK(f1->number == 1); | ||
| CHECK(f1->GetWidth() == cvimage.cols); | ||
| CHECK(f1->GetHeight() == cvimage.rows); | ||
| CHECK(cvimage.channels() == 4); | ||
| } | ||
|
Comment on lines
+165
to
+185
|
||
| #endif | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name has a typo:
brga_image_cvshould bebgra_image_cvto correctly represent the BGRA color format.