I'm attempting to overlay an image stored in a cv::Mat object onto another cv::Mat image at a certain position using OpenCV's C++ library.
Most of the existing solutions are documented using OpenCV's Python library, and I'm encountering some difficulties translating those solutions to C++.
As a minimal test, suppose I have a simple test image test_1.png of size 100x100:
which I want to overlay in the middle of a black background of size 300x300 as such:
Most of the answers, including this one, make use of the addWeighted function. Attempting to implement the function as such:
#include <opencv2/opencv.hpp>
#include <opencv2/core/mat.hpp>
#include <opencv2/core/core.hpp>
int main()
{
cv::Mat base = cv::Mat::zeros(300, 300, CV_8UC3);
cv::Mat overlay = cv::imread("test_1.png", 1);
cv::Mat result;
cv::addWeighted(base, 1, overlay, 0.1, 0, result);
cv::imshow("test", result);
cv::waitKey(1);
std::system("pause");
return 0;
}
Results in a C++ memory error at the addWeighted call, I'm assuming because base and overlay aren't the same size.
Another post answer that asked about the same issue in C++ specificaly provides the following answer:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/mat.hpp>
#include <opencv2/core/core.hpp>
void OverlayImage(cv::Mat& base, cv::Mat& overlay, std::pair<int, int> pos)
{
cv::Size fsize{overlay.size()};
cv::Mat gray;
cv::Mat mask;
cv::Mat maskInv;
cv::cvtColor(overlay, gray, cv::COLOR_BGR2GRAY);
cv::threshold(gray, mask, 0, 255, cv::THRESH_BINARY);
cv::bitwise_not(mask, maskInv);
cv::Mat roi{base(cv::Range(pos.first, pos.second + fsize.width), cv::Range(pos.second, fsize.height))};
cv::Mat backBg;
cv::Mat frontBg;
cv::bitwise_and(roi, roi, backBg, maskInv);
cv::bitwise_and(overlay, overlay, frontBg, mask);
cv::Mat result;
cv::add(backBg, frontBg, result);
cv::addWeighted(roi, 0.1, result, 0.9, 0.0, result);
result.copyTo(base(cv::Rect(pos.first, pos.second, fsize.width, fsize.height)));
}
int main()
{
cv::Mat base = cv::Mat::zeros(300, 300, CV_8UC3);
cv::Mat overlay = cv::imread("test_1.png", 1);
OverlayImage(base, overlay, std::pair<int, int>(0, 0));
cv::imshow("test", base);
cv::waitKey(1);
std::system("pause");
return 0;
}
While this solution appears to work with a position of (0, 0):
It doesn't appear to work for any other position other than (0, 0).
If either of the position components are positive, a C++ memory error occurs at the line:
cv::bitwise_and(roi, roi, backBg, maskInv);
If either of the position components are negative, an error occurs within the cv::Mat constructor, from the line:
cv::Mat roi{base(cv::Range(pos.first, pos.second + fsize.width), cv::Range(pos.second, fsize.height))};
What is the proper way to overlay a cv::Mat image onto another cv::Mat image at a certain position using OpenCV's C++ library?
(sidenote: the background will not always be black as in my example, and I wish for the image to be partially visible if I place it close to the base images' edges)
Thanks for reading my post, any guidance is appreciated.




This line is a mess. Maybe, should be
If you want to support the situation that
overlayis not completely included inbase, like this:For example, when
main()is below, the result becomes: