From a11c9b2e30f61e8c38710518471f9a9c6659d9ec Mon Sep 17 00:00:00 2001 From: HaiVQ Date: Fri, 21 Mar 2025 03:14:14 +0000 Subject: [PATCH 1/8] Add support for alpha channel in image conversion methods and refactor related code --- src/Frame.cpp | 42 +++++++++++++++++++++++++++++++++++++---- src/Frame.h | 12 ++++++++++++ src/effects/Outline.cpp | 36 +++++++++++++++++------------------ src/effects/Outline.h | 9 +++------ 4 files changed, 70 insertions(+), 29 deletions(-) diff --git a/src/Frame.cpp b/src/Frame.cpp index 85d7fd20f..b60ff5595 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -891,6 +891,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 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); @@ -904,11 +911,38 @@ std::shared_ptr Frame::Mat2Qimage(cv::Mat img){ return imgIn; } -// Set pointer to OpenCV image object -void Frame::SetImageCV(cv::Mat _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 +// Idea from: https://stackoverflow.com/a/78480103 +cv::Mat QImage2BGRACvMat(std::shared_ptr& qimage) { + cv::Mat cv_img( + qimage->height(), qimage->width(), + CV_8UC4, (uchar*)qimage->constBits(), + qimage->bytesPerLine() + ); + return cv_img; +} + +std::shared_ptr 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); + std::shared_ptr imgIn = std::make_shared(qimage.convertToFormat(QImage::Format_RGBA8888_Premultiplied)); + return imgIn; +} + +cv::Mat Frame::GetBGRACvMat() { + if (!image) + // Fill with black + AddColor(width, height, color); + imagecv = QImage2BGRACvMat(image); + return imagecv; +} + +void Frame::SetBGRACvMat(cv::Mat _image) { imagecv = _image; - image = Mat2Qimage(_image); + image = BGRACvMat2QImage(_image); } #endif diff --git a/src/Frame.h b/src/Frame.h index 094d885af..291a4ada3 100644 --- a/src/Frame.h +++ b/src/Frame.h @@ -279,6 +279,18 @@ namespace openshot /// Set pointer to OpenCV image object void SetImageCV(cv::Mat _image); + + /// Convert QImage to OpenCV Mat (alpha channel included) + cv::Mat QImage2BGRACvMat(std::shared_ptr& qimage); + + /// Convert OpenCV Mat to QImage (alpha channel included) + std::shared_ptr BGRACvMat2QImage(cv::Mat img); + + /// Get pointer to OpenCV Mat image object (with alpha channel) + cv::Mat GetBGRACvMat(); + + /// Set pointer to OpenCV image object (with alpha channel) + void SetBGRACvMat(cv::Mat _image); #endif }; diff --git a/src/effects/Outline.cpp b/src/effects/Outline.cpp index bcd9e18ba..dee300703 100644 --- a/src/effects/Outline.cpp +++ b/src/effects/Outline.cpp @@ -60,12 +60,11 @@ std::shared_ptr Outline::GetFrame(std::shared_ptr frame_image = frame->GetImage(); - + cv::Mat cv_image = frame->GetBGRACvMat(); + float sigmaValue = widthValue / 3.0; if (sigmaValue <= 0.0) sigmaValue = 0.01; - cv::Mat cv_image = QImageToBGRACvMat(frame_image); // Extract alpha channel for the mask std::vector channels(4); @@ -95,25 +94,24 @@ std::shared_ptr Outline::GetFrame(std::shared_ptr new_frame_image = BGRACvMatToQImage(final_image); - - // FIXME: The shared_ptr::swap does not work somehow - *frame_image = *new_frame_image; + frame->SetBGRACvMat(final_image); + return frame; } -cv::Mat Outline::QImageToBGRACvMat(std::shared_ptr& qimage) { - cv::Mat cv_img(qimage->height(), qimage->width(), CV_8UC4, (uchar*)qimage->constBits(), qimage->bytesPerLine()); - return cv_img; -} - -std::shared_ptr Outline::BGRACvMatToQImage(cv::Mat img) { - cv::Mat final_img; - cv::cvtColor(img, final_img, cv::COLOR_RGBA2BGRA); - QImage qimage(final_img.data, final_img.cols, final_img.rows, final_img.step, QImage::Format_ARGB32); - std::shared_ptr imgIn = std::make_shared(qimage.convertToFormat(QImage::Format_RGBA8888_Premultiplied)); - return imgIn; -} +// Moved to Frame.cpp +// cv::Mat Outline::QImageToBGRACvMat(std::shared_ptr& qimage) { +// cv::Mat cv_img(qimage->height(), qimage->width(), CV_8UC4, (uchar*)qimage->constBits(), qimage->bytesPerLine()); +// return cv_img; +// } + +// std::shared_ptr Outline::BGRACvMatToQImage(cv::Mat img) { +// cv::Mat final_img; +// cv::cvtColor(img, final_img, cv::COLOR_RGBA2BGRA); +// QImage qimage(final_img.data, final_img.cols, final_img.rows, final_img.step, QImage::Format_ARGB32); +// std::shared_ptr imgIn = std::make_shared(qimage.convertToFormat(QImage::Format_RGBA8888_Premultiplied)); +// return imgIn; +// } // Generate JSON string of this object std::string Outline::Json() const { diff --git a/src/effects/Outline.h b/src/effects/Outline.h index 99cb91eeb..8b1b74cd2 100644 --- a/src/effects/Outline.h +++ b/src/effects/Outline.h @@ -40,12 +40,9 @@ namespace openshot /// Init effect settings void init_effect_details(); - // Convert QImage to cv::Mat and vice versa - // Although Frame class has GetImageCV, but it does not include alpha channel - // so we need a separate methods which preserve alpha channel - // Idea from: https://stackoverflow.com/a/78480103 - cv::Mat QImageToBGRACvMat(std::shared_ptr& qimage); - std::shared_ptr BGRACvMatToQImage(cv::Mat img); + // Moved to Frame.h + // cv::Mat QImageToBGRACvMat(std::shared_ptr& qimage); + // std::shared_ptr BGRACvMatToQImage(cv::Mat img); public: Keyframe width; ///< Width of the outline From 4551154cef802ce2d4220bfedb2856b6ad9bf5be Mon Sep 17 00:00:00 2001 From: HaiVQ Date: Fri, 21 Mar 2025 03:43:39 +0000 Subject: [PATCH 2/8] Refactor image conversion methods to be member functions of Frame class for better encapsulation --- src/Frame.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Frame.cpp b/src/Frame.cpp index b60ff5595..c6b5a1545 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -915,7 +915,7 @@ std::shared_ptr Frame::Mat2Qimage(cv::Mat img){ // Frame class has GetImageCV, but it does not include alpha channel // so we need a separate methods which preserve alpha channel // Idea from: https://stackoverflow.com/a/78480103 -cv::Mat QImage2BGRACvMat(std::shared_ptr& qimage) { +cv::Mat Frame::QImage2BGRACvMat(std::shared_ptr& qimage) { cv::Mat cv_img( qimage->height(), qimage->width(), CV_8UC4, (uchar*)qimage->constBits(), @@ -924,7 +924,7 @@ cv::Mat QImage2BGRACvMat(std::shared_ptr& qimage) { return cv_img; } -std::shared_ptr BGRACvMat2QImage(cv::Mat img) { +std::shared_ptr 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); From b0201087d5d4c6224ddb70c5381d61950740df72 Mon Sep 17 00:00:00 2001 From: HaiVQ Date: Fri, 21 Mar 2025 03:52:50 +0000 Subject: [PATCH 3/8] Add unit test for image conversion with alpha channel support --- tests/Frame.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/Frame.cpp b/tests/Frame.cpp index ffe4d84dd..a1e3743b4 100644 --- a/tests/Frame.cpp +++ b/tests/Frame.cpp @@ -160,4 +160,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() == 3); +} #endif From ed33b6d608bbbeb46f2f9e2a77e954694aa4db2b Mon Sep 17 00:00:00 2001 From: HaiVQ Date: Fri, 21 Mar 2025 07:24:22 +0000 Subject: [PATCH 4/8] Rename imagecv to brga_image_cv for clarity and consistency in Frame class --- src/Frame.cpp | 6 +++--- src/Frame.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Frame.cpp b/src/Frame.cpp index c6b5a1545..7ccde030c 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -936,12 +936,12 @@ cv::Mat Frame::GetBGRACvMat() { if (!image) // Fill with black AddColor(width, height, color); - imagecv = QImage2BGRACvMat(image); - return imagecv; + brga_image_cv = QImage2BGRACvMat(image); + return brga_image_cv; } void Frame::SetBGRACvMat(cv::Mat _image) { - imagecv = _image; + brga_image_cv = _image; image = BGRACvMat2QImage(_image); } #endif diff --git a/src/Frame.h b/src/Frame.h index 291a4ada3..2649a15f9 100644 --- a/src/Frame.h +++ b/src/Frame.h @@ -108,6 +108,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 #endif /// Constrain a color value from 0 to 255 From 11f8f788f9bdf3cdeb2adcfcc8fdf0378acd93bc Mon Sep 17 00:00:00 2001 From: HaiVQ Date: Sat, 22 Mar 2025 09:31:59 +0000 Subject: [PATCH 5/8] Fix author attribution and remove unrelated note --- src/Frame.cpp | 4 +++- src/Frame.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Frame.cpp b/src/Frame.cpp index 7ccde030c..be8d486e3 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -2,6 +2,7 @@ * @file * @brief Source file for Frame class * @author Jonathan Thomas + * @author HaiVQ * * @ref License */ @@ -914,7 +915,6 @@ std::shared_ptr Frame::Mat2Qimage(cv::Mat img){ // 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 -// Idea from: https://stackoverflow.com/a/78480103 cv::Mat Frame::QImage2BGRACvMat(std::shared_ptr& qimage) { cv::Mat cv_img( qimage->height(), qimage->width(), @@ -924,6 +924,7 @@ cv::Mat Frame::QImage2BGRACvMat(std::shared_ptr& qimage) { return cv_img; } +// Convert cv::Mat back to QImage std::shared_ptr Frame::BGRACvMat2QImage(cv::Mat img) { cv::Mat final_img; cv::cvtColor(img, final_img, cv::COLOR_BGRA2RGBA); @@ -932,6 +933,7 @@ std::shared_ptr Frame::BGRACvMat2QImage(cv::Mat img) { return imgIn; } +// Get BGRA cv::Mat Frame::GetBGRACvMat() { if (!image) // Fill with black diff --git a/src/Frame.h b/src/Frame.h index 2649a15f9..62f5d3906 100644 --- a/src/Frame.h +++ b/src/Frame.h @@ -2,6 +2,7 @@ * @file * @brief Header file for Frame class * @author Jonathan Thomas + * @author HaiVQ * * @ref License */ From 2520c68f5a15ac13ab85c698637a681797bf15e3 Mon Sep 17 00:00:00 2001 From: HaiVQ Date: Sat, 22 Mar 2025 09:32:12 +0000 Subject: [PATCH 6/8] Update author attribution and modify image channel check in unit test for alpha support --- tests/Frame.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Frame.cpp b/tests/Frame.cpp index a1e3743b4..ba280fff8 100644 --- a/tests/Frame.cpp +++ b/tests/Frame.cpp @@ -3,6 +3,7 @@ * @brief Unit tests for openshot::Frame * @author Jonathan Thomas * @author FeRD (Frank Dana) + * @author HaiVQ * * @ref License */ @@ -180,6 +181,6 @@ TEST_CASE( "Convert_Image_Alpha", "[libopenshot][opencv][frame]" ) CHECK(f1->number == 1); CHECK(f1->GetWidth() == cvimage.cols); CHECK(f1->GetHeight() == cvimage.rows); - CHECK(cvimage.channels() == 3); + CHECK(cvimage.channels() == 4); } #endif From be5e2e9f1ba60d22d04ddb4e7cea9aab7bdcab14 Mon Sep 17 00:00:00 2001 From: HaiVQ Date: Sat, 22 Mar 2025 09:32:20 +0000 Subject: [PATCH 7/8] Update author attribution in Outline effect files for clarity --- src/effects/Outline.cpp | 5 +++-- src/effects/Outline.h | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/effects/Outline.cpp b/src/effects/Outline.cpp index dee300703..2948d8dd4 100644 --- a/src/effects/Outline.cpp +++ b/src/effects/Outline.cpp @@ -1,8 +1,9 @@ /** * @file * @brief Source file for Outline effect class - * @author Jonathan Thomas , HaiVQ - * + * @author Jonathan Thomas + * @author HaiVQ + * * @ref License */ diff --git a/src/effects/Outline.h b/src/effects/Outline.h index 8b1b74cd2..3e45cad31 100644 --- a/src/effects/Outline.h +++ b/src/effects/Outline.h @@ -1,7 +1,8 @@ /** * @file * @brief Header file for Outline effect class - * @author Jonathan Thomas , HaiVQ + * @author Jonathan Thomas + * @author HaiVQ * * @ref License */ @@ -33,6 +34,7 @@ namespace openshot * with openshot::Keyframe curves over time. * * Outlines can be added around any image or text, and animated over time. + * Idea from: https://stackoverflow.com/a/78480103 */ class Outline : public EffectBase { From f2f76e0deaf4fa51093133533c84ba0161c12ad9 Mon Sep 17 00:00:00 2001 From: HaiVQ Date: Sat, 22 Mar 2025 09:35:47 +0000 Subject: [PATCH 8/8] Release resources for brga_image_cv in Frame destructor --- src/Frame.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Frame.cpp b/src/Frame.cpp index be8d486e3..5db990334 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -114,6 +114,7 @@ Frame::~Frame() { audio.reset(); #ifdef USE_OPENCV imagecv.release(); + brga_image_cv.release(); #endif }