-
Notifications
You must be signed in to change notification settings - Fork 308
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
Conversation
…ss for better encapsulation
… for alpha support
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #998 +/- ##
===========================================
+ Coverage 59.60% 59.68% +0.08%
===========================================
Files 206 206
Lines 20374 20397 +23
===========================================
+ Hits 12143 12174 +31
+ Misses 8231 8223 -8 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
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.
Pull request overview
This pull request adds alpha channel support to Frame's OpenCV conversion functions by introducing four new methods: QImage2BGRACvMat, BGRACvMat2QImage, GetBGRACvMat, and SetBGRACvMat. These functions are migrated from the Outline effect to make them reusable across multiple effects that require alpha channel handling.
Key changes:
- Added new member variable
brga_image_cvand four conversion methods to Frame class for BGRA format with alpha channel - Refactored Outline effect to use the new Frame methods instead of local conversion functions
- Added unit test to verify 4-channel cv::Mat conversion
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Frame.h | Declares new brga_image_cv member variable and four new methods for QImage ↔ cv::Mat conversion with alpha channel support |
| src/Frame.cpp | Implements the conversion methods and adds cleanup for the new member variable in destructor |
| src/effects/Outline.h | Removes local conversion method declarations, now using Frame class methods |
| src/effects/Outline.cpp | Refactored to use Frame::GetBGRACvMat() and Frame::SetBGRACvMat() instead of local conversion functions |
| tests/Frame.cpp | Adds test case verifying GetBGRACvMat returns a 4-channel cv::Mat |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!image) | ||
| // Fill with black | ||
| AddColor(width, height, color); | ||
| brga_image_cv = QImage2BGRACvMat(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.
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(); |
| std::shared_ptr<QImage> Frame::BGRACvMat2QImage(cv::Mat img) { | ||
| cv::Mat final_img; | ||
| cv::cvtColor(img, final_img, cv::COLOR_BGRA2RGBA); | ||
| QImage qimage(final_img.data, final_img.cols, final_img.rows, final_img.step, QImage::Format_ARGB32); |
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); |
| cv::Mat cv_img( | ||
| qimage->height(), qimage->width(), | ||
| CV_8UC4, (uchar*)qimage->constBits(), | ||
| qimage->bytesPerLine() | ||
| ); | ||
| return cv_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 function name suggests it returns a cv::Mat in BGRA format, but it actually wraps the QImage data without any color conversion. Since QImages in this codebase use Format_RGBA8888_Premultiplied (RGBA in memory), the returned cv::Mat contains RGBA data, not BGRA. This is inconsistent with the existing Qimage2mat function (line 873) which converts RGB to BGR. OpenCV operations typically expect BGR/BGRA format. The function should either convert RGBA to BGRA, or be renamed to accurately reflect that it returns RGBA data.
| 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; |
|
|
||
| #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 |
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. BGRA is the correct order: Blue, Green, Red, Alpha.
| 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 |
| 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); | ||
| } |
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 test only verifies that GetBGRACvMat returns a 4-channel cv::Mat but doesn't validate that the color channels are in the correct order or that the color values are accurate. Consider adding assertions that check specific pixel values or perform a round-trip conversion test (QImage → cv::Mat → QImage) to ensure data integrity and correct channel ordering.
|
|
||
| #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 |
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) |
| audio.reset(); | ||
| #ifdef USE_OPENCV | ||
| imagecv.release(); | ||
| brga_image_cv.release(); |
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.release(); | |
| bgra_image_cv.release(); |
| brga_image_cv = QImage2BGRACvMat(image); | ||
| return brga_image_cv; | ||
| } | ||
|
|
||
| void Frame::SetBGRACvMat(cv::Mat _image) { | ||
| brga_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; |
| brga_image_cv = QImage2BGRACvMat(image); | ||
| return brga_image_cv; | ||
| } | ||
|
|
||
| void Frame::SetBGRACvMat(cv::Mat _image) { | ||
| brga_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; |
| CV_8UC4, (uchar*)qimage->constBits(), | ||
| qimage->bytesPerLine() | ||
| ); | ||
| return cv_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(); |
This pull request introduces enhanced image effect capabilities for the
Frameclass by leveraging the OpenCV library. Direct manipulation ofQImageraw data for complex effects proves challenging, and the performance ofQGraphicsEffectcan be suboptimal. To address these limitations, this PR proposes the integration of OpenCV for more efficient and versatile effect creation.While existing conversion functions between
QImageandcv::MatinFrame.cpplack alpha channel support, several effects, including the currently implementedOutlineand the proposedShadow, require it for proper rendering. This pull request introduces four new methods to facilitate seamless conversion betweenQImageandcv::Matwhile preserving the alpha channel:QImage2BGRACvMat: Converts aQImageto acv::MatwithBGRApixel format.BGRACvMat2QImage: Converts acv::MatwithBGRApixel format back to aQImage.GetBGRACvMat: Provides access to the underlyingcv::Mat(inBGRAformat) of aFrameobject.SetBGRACvMat: Allows setting thecv::Mat(inBGRAformat) to aFrameobject.These conversion functions were previously located within the
Outlineeffect code and have been migrated to theFrameclass for broader reusability. Your review and feedback on these additions would be greatly appreciated.Note: Theimagecvvariable, which appears to be a potentially unused cachedcv::Mat, is currently shared across the code. While investigating its purpose, no clear or active usage was identified. The line intended for caching was also commented out. This aspect might warrant further review or potential removal in a subsequent cleanup.Now switched to
brga_image_cvvariable.